Extracting Compressed files with any compression algorithm like gzip can cause to denial of service attacks.
+
Attackers can compress a huge file which created by repeated similiar byte and convert it to a small compressed file.
+
+
+
+
+
When you want to decompress a user-provided compressed file you must be careful about the decompression ratio or read these files within a loop byte by byte to be able to manage the decompressed size in each cycle of the loop.
+
+
+
+
python ZipFile library is vulnerable by default
+
+
+
By checking the decompressed size from input zipped file you can check the decompression ratio. attackers can forge this decompressed size header too.
+So can't rely on file_size attribute of ZipInfo class. this is recommended to use "ZipFile.open" method to be able to manage decompressed size.
+
Reading decompressed file byte by byte and verifying the total current size in each loop cycle in recommended to use in any decompression library.
+
+
+
diff --git a/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql b/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql
new file mode 100644
index 00000000000..7f8f493decf
--- /dev/null
+++ b/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql
@@ -0,0 +1,602 @@
+/**
+ * @name User-controlled file decompression
+ * @description User-controlled data that flows into decompression library APIs without checking the compression rate is dangerous
+ * @kind path-problem
+ * @problem.severity error
+ * @security-severity 7.8
+ * @precision medium
+ * @id py/user-controlled-file-decompression
+ * @tags security
+ * experimental
+ * external/cwe/cwe-409
+ */
+
+import python
+import semmle.python.dataflow.new.DataFlow
+import semmle.python.dataflow.new.TaintTracking
+import semmle.python.ApiGraphs
+import semmle.python.dataflow.new.RemoteFlowSources
+import semmle.python.dataflow.new.internal.DataFlowPublic
+
+module pyZipFile {
+ /**
+ * ```python
+ * zipfile.PyZipFile()
+ */
+ private API::Node pyZipFileClass() {
+ result = API::moduleImport("zipfile").getMember("PyZipFile")
+ }
+
+ /**
+ * same as zipfileSinks
+ */
+ DataFlow::Node isSink() { result = sink(pyZipFileClass()).getACall() }
+
+ private API::Node sink(API::Node pyZipFileClass) {
+ result = pyZipFileClass.getReturn().getMember(["extractall", "read", "extract", "testzip"])
+ or
+ result = pyZipFileClass.getReturn().getMember("open") and
+ // only read mode is sink
+ // mode can be set in open() argument or in PyZipFile instantiation argument
+ (
+ not exists(
+ result
+ .getACall()
+ .getParameter(1, "mode")
+ .getAValueReachingSink()
+ .asExpr()
+ .(StrConst)
+ .getText()
+ ) or
+ result
+ .getACall()
+ .getParameter(1, "mode")
+ .getAValueReachingSink()
+ .asExpr()
+ .(StrConst)
+ .getText() = "r"
+ ) and
+ (
+ not exists(
+ pyZipFileClass
+ .getACall()
+ .getParameter(1, "mode")
+ .getAValueReachingSink()
+ .asExpr()
+ .(StrConst)
+ .getText()
+ ) or
+ pyZipFileClass
+ .getACall()
+ .getParameter(1, "mode")
+ .getAValueReachingSink()
+ .asExpr()
+ .(StrConst)
+ .getText() = "r"
+ )
+ }
+
+ /**
+ * Same as ZipFile
+ * I made PyZipFile seperated from ZipFile as in future this will be compatible
+ * if anyone want to add new methods an sink to each object.
+ */
+ predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
+ exists(API::Node pyZipFileClass | pyZipFileClass = pyZipFileClass() |
+ nodeFrom = pyZipFileClass.getACall().getParameter(0, "file").asSink() and
+ nodeTo =
+ [
+ sink(pyZipFileClass).getACall(),
+ pyZipFileClass
+ .getACall()
+ .getReturn()
+ .getMember(["extractall", "read", "extract", "testzip"])
+ .getACall()
+ ]
+ )
+ }
+}
+
+module Lzma {
+ private API::Node lzmaClass() {
+ result = API::moduleImport("lzma").getMember(["LZMAFile", "open"])
+ }
+
+ /**
+ * `lzma.open(sink)`
+ * `lzma.LZMAFile(sink)`
+ * only read mode is sink
+ */
+ DataFlow::Node isSink() {
+ exists(API::Node lzmaClass | lzmaClass = lzmaClass() |
+ result = lzmaClass.getACall().getParameter(0, "filename").asSink() and
+ (
+ not exists(
+ lzmaClass
+ .getACall()
+ .getParameter(1, "mode")
+ .getAValueReachingSink()
+ .asExpr()
+ .(StrConst)
+ .getText()
+ ) or
+ lzmaClass
+ .getACall()
+ .getParameter(1, "mode")
+ .getAValueReachingSink()
+ .asExpr()
+ .(StrConst)
+ .getText()
+ .matches("%r%")
+ )
+ )
+ }
+}
+
+module Bz2 {
+ private API::Node bz2Class() { result = API::moduleImport("bz2").getMember(["BZ2File", "open"]) }
+
+ /**
+ * `bz2.open(sink)`
+ * `bz2.BZ2File(sink)`
+ * only read mode is sink
+ */
+ DataFlow::Node isSink() {
+ exists(API::Node bz2Class | bz2Class = bz2Class() |
+ result = bz2Class.getACall().getParameter(0, "filename").asSink() and
+ (
+ not exists(
+ bz2Class
+ .getACall()
+ .getParameter(1, "mode")
+ .getAValueReachingSink()
+ .asExpr()
+ .(StrConst)
+ .getText()
+ ) or
+ bz2Class
+ .getACall()
+ .getParameter(1, "mode")
+ .getAValueReachingSink()
+ .asExpr()
+ .(StrConst)
+ .getText()
+ .matches("%r%")
+ )
+ )
+ }
+}
+
+module Gzip {
+ private API::Node gzipClass() {
+ result = API::moduleImport("gzip").getMember(["GzipFile", "open"])
+ }
+
+ /**
+ * `gzip.open(sink)`
+ * `gzip.GzipFile(sink)`
+ * only read mode is sink
+ */
+ DataFlow::Node isSink() {
+ exists(API::Node gzipClass | gzipClass = gzipClass() |
+ result = gzipClass.getACall().getParameter(0, "filename").asSink() and
+ (
+ not exists(
+ gzipClass
+ .getACall()
+ .getParameter(1, "mode")
+ .getAValueReachingSink()
+ .asExpr()
+ .(StrConst)
+ .getText()
+ ) or
+ gzipClass
+ .getACall()
+ .getParameter(1, "mode")
+ .getAValueReachingSink()
+ .asExpr()
+ .(StrConst)
+ .getText()
+ .matches("%r%")
+ )
+ )
+ }
+}
+
+module ZipFile {
+ // more sinks file:///home/am/CodeQL-home/codeql-repo/python/ql/src/experimental/semmle/python/security/ZipSlip.qll
+ /**
+ * ```python
+ * zipfile.ZipFile()
+ * ```
+ */
+ private API::Node zipFileClass() { result = API::moduleImport("zipfile").getMember("ZipFile") }
+
+ /**
+ * ```python
+ * zipfile.ZipFile("zipfileName.zip")
+ * # read() or one of ["read", "readline", "readlines", "seek", "tell", "__iter__", "__next__"]
+ * myzip.open('eggs.txt',"r").read()
+ * # I decided to choice open method with "r" mode as sink
+ * # because opening zipfile with "r" mode mostly is for reading content of that file
+ * # so we have a very few of FP here
+ * next(myzip.open('eggs.txt'))
+ * myzip.extractall()
+ * myzip.read()
+ * myzip.extract()
+ * # testzip not a RAM consumer but it uses as much CPU as possible
+ * myzip.testzip()
+ *
+ * ```
+ */
+ private API::Node sink(API::Node zipFileInstance) {
+ // we can go forward one more step and check whether we call the required methods for read
+ // or just opening zipfile for reading is enough ( mode = "r")
+ // result =
+ // zipfileReturnIOFile()
+ // .getReturn()
+ // .getMember(["read", "readline", "readlines", "seek", "tell", "__iter__", "__next__"])
+ // or
+ (
+ result = zipFileInstance.getReturn().getMember(["extractall", "read", "extract", "testzip"])
+ or
+ result = zipFileInstance.getReturn().getMember("open") and
+ (
+ not exists(
+ result
+ .getACall()
+ .getParameter(1, "mode")
+ .getAValueReachingSink()
+ .asExpr()
+ .(StrConst)
+ .getText()
+ ) or
+ result
+ .getACall()
+ .getParameter(1, "mode")
+ .getAValueReachingSink()
+ .asExpr()
+ .(StrConst)
+ .getText() = "r"
+ ) and
+ (
+ not exists(
+ zipFileInstance
+ .getACall()
+ .getParameter(1, "mode")
+ .getAValueReachingSink()
+ .asExpr()
+ .(StrConst)
+ .getText()
+ ) or
+ zipFileInstance
+ .getACall()
+ .getParameter(1, "mode")
+ .getAValueReachingSink()
+ .asExpr()
+ .(StrConst)
+ .getText() = "r"
+ ) and
+ zipFileSanitizer(result)
+ ) and
+ exists(result.getACall().getLocation().getFile().getRelativePath())
+ }
+
+ /**
+ * a sanitizers which check if there is a managed read
+ * ```python
+ * with zipfile.ZipFile(zipFileName) as myzip:
+ * with myzip.open(fileinfo.filename, mode="r") as myfile:
+ * while chunk:
+ * chunk = myfile.read(buffer_size)
+ * total_size += buffer_size
+ * if total_size > SIZE_THRESHOLD:
+ * ...
+ * ```
+ */
+ predicate zipFileSanitizer(API::Node n) {
+ not TaintTracking::localExprTaint(n.getReturn()
+ .getMember("read")
+ .getParameter(0)
+ .asSink()
+ .asExpr(), any(Compare i).getASubExpression*())
+ }
+
+ DataFlow::Node isSink() { result = sink(zipFileClass()).getACall() }
+
+ /**
+ * ```python
+ * nodeFrom = "zipFileName.zip"
+ * myZip = zipfile.ZipFile(nodeFrom)
+ * nodeTo2 = myZip.open('eggs.txt',"r")
+ *
+ * nodeTo = myZip.extractall()
+ * nodeTo = myZip.read()
+ * nodeTo = myZip.extract()
+ * # testzip not a RAM consumer but it uses as much CPU as possible
+ * nodeTo = myZip.testzip()
+ *
+ * ```
+ */
+ predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
+ exists(API::Node zipFileInstance | zipFileInstance = zipFileClass() |
+ nodeFrom = zipFileInstance.getACall().getParameter(0, "file").asSink() and
+ nodeTo =
+ [
+ sink(zipFileInstance).getACall(),
+ zipFileInstance
+ .getACall()
+ .getReturn()
+ .getMember(["extractall", "read", "extract", "testzip"])
+ .getACall()
+ ]
+ ) and
+ exists(nodeTo.getLocation().getFile().getRelativePath())
+ }
+}
+
+module TarFile {
+ /**
+ * tarfile.open
+ *
+ * tarfile.Tarfile.open/xzopen/gzopen/bz2open
+ * and not mode="r:" which means no compression accepted
+ */
+ API::Node tarfileInstance() {
+ result =
+ [
+ API::moduleImport("tarfile").getMember("open"),
+ API::moduleImport("tarfile")
+ .getMember("TarFile")
+ .getMember(["xzopen", "gzopen", "bz2open", "open"])
+ ] and
+ (
+ not exists(
+ result
+ .getACall()
+ .getParameter(1, "mode")
+ .getAValueReachingSink()
+ .asExpr()
+ .(StrConst)
+ .getText()
+ ) or
+ not result
+ .getACall()
+ .getParameter(1, "mode")
+ .getAValueReachingSink()
+ .asExpr()
+ .(StrConst)
+ .getText()
+ .matches("r:%")
+ )
+ }
+
+ /**
+ * a Call of
+ * `tarfile.open(filepath).extractall()/extract()/extractfile()`
+ * or
+ * `tarfile.Tarfile.xzopen()/gzopen()/bz2open()`
+ */
+ DataFlow::Node isSink() {
+ result =
+ tarfileInstance().getReturn().getMember(["extractall", "extract", "extractfile"]).getACall()
+ }
+
+ predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
+ exists(API::Node tarfileInstance | tarfileInstance = tarfileInstance() |
+ nodeFrom = tarfileInstance.getACall().getParameter(0, "name").asSink() and
+ nodeTo =
+ tarfileInstance.getReturn().getMember(["extractall", "extract", "extractfile"]).getACall()
+ )
+ }
+}
+
+module Shutil {
+ DataFlow::Node isSink() {
+ result =
+ [
+ API::moduleImport("shutil")
+ .getMember("unpack_archive")
+ .getACall()
+ .getParameter(0, "filename")
+ .asSink()
+ ]
+ }
+}
+
+module Pandas {
+ DataFlow::Node isSink() {
+ exists(API::CallNode calltoPandasMethods |
+ (
+ calltoPandasMethods =
+ API::moduleImport("pandas")
+ .getMember([
+ "read_csv", "read_json", "read_sas", "read_stata", "read_table", "read_xml"
+ ])
+ .getACall() and
+ result = calltoPandasMethods.getArg(0)
+ or
+ calltoPandasMethods =
+ API::moduleImport("pandas")
+ .getMember(["read_csv", "read_sas", "read_stata", "read_table"])
+ .getACall() and
+ result = calltoPandasMethods.getArgByName("filepath_or_buffer")
+ or
+ calltoPandasMethods = API::moduleImport("pandas").getMember("read_json").getACall() and
+ result = calltoPandasMethods.getArgByName("path_or_buf")
+ or
+ calltoPandasMethods = API::moduleImport("pandas").getMember("read_xml").getACall() and
+ result = calltoPandasMethods.getArgByName("path_or_buffer")
+ ) and
+ (
+ not exists(calltoPandasMethods.getArgByName("compression"))
+ or
+ not calltoPandasMethods
+ .getKeywordParameter("compression")
+ .getAValueReachingSink()
+ .asExpr()
+ .(StrConst)
+ .getText() = "tar"
+ )
+ )
+ }
+}
+
+module FileAndFormRemoteFlowSource {
+ class FastAPI extends DataFlow::Node {
+ FastAPI() {
+ exists(API::Node fastAPIParam |
+ fastAPIParam =
+ API::moduleImport("fastapi")
+ .getMember("FastAPI")
+ .getReturn()
+ .getMember("post")
+ .getReturn()
+ .getParameter(0)
+ .getKeywordParameter(_) and
+ API::moduleImport("fastapi")
+ .getMember("UploadFile")
+ .getASubclass*()
+ .getAValueReachableFromSource()
+ .asExpr() =
+ fastAPIParam.asSource().asExpr().(Parameter).getAnnotation().getASubExpression*()
+ |
+ // in the case of List of files
+ exists(For f, Attribute attr, DataFlow::Node a, DataFlow::Node b |
+ fastAPIParam.getAValueReachableFromSource().asExpr() = f.getIter().getASubExpression*()
+ |
+ // file.file in following
+ // def upload(files: List[UploadFile] = File(...)):
+ // for file in files:
+ // **file.file**
+ // thanks Arthur Baars for helping me in following
+ TaintTracking::localTaint(a, b) and
+ a.asExpr() = f.getIter() and
+ b.asExpr() = attr.getObject() and
+ attr.getName() = ["filename", "content_type", "headers", "file", "read"] and
+ this.asExpr() = attr
+ )
+ or
+ // exclude cases like type-annotated with `Response`
+ // and not not any(Response::RequestHandlerParam src).asExpr() = result
+ this =
+ [
+ fastAPIParam.asSource(),
+ fastAPIParam.getMember(["filename", "content_type", "headers", "file"]).asSource(),
+ fastAPIParam.getMember(["read"]).getReturn().asSource(),
+ // file-like object, I'm trying to not do additional work here by using already existing file-like objs if it is possible
+ // fastAPIParam.getMember("file").getAMember().asSource(),
+ ]
+ )
+ or
+ exists(API::Node fastAPIParam |
+ fastAPIParam =
+ API::moduleImport("fastapi")
+ .getMember("FastAPI")
+ .getReturn()
+ .getMember("post")
+ .getReturn()
+ .getParameter(0)
+ .getKeywordParameter(_) and
+ API::moduleImport("fastapi")
+ .getMember("File")
+ .getASubclass*()
+ .getAValueReachableFromSource()
+ .asExpr() =
+ fastAPIParam.asSource().asExpr().(Parameter).getAnnotation().getASubExpression*()
+ |
+ // in the case of List of files
+ exists(For f, Attribute attr, DataFlow::Node a, DataFlow::Node b |
+ fastAPIParam.getAValueReachableFromSource().asExpr() = f.getIter().getASubExpression*()
+ |
+ // file.file in following
+ // def upload(files: List[UploadFile] = File(...)):
+ // for file in files:
+ // **file.file**
+ // thanks Arthur Baars for helping me in following
+ TaintTracking::localTaint(a, b) and
+ a.asExpr() = f.getIter() and
+ b.asExpr() = attr.getObject() and
+ attr.getName() = "file" and
+ this.asExpr() = attr
+ )
+ or
+ // exclude cases like type-annotated with `Response`
+ // and not not any(Response::RequestHandlerParam src).asExpr() = result
+ this = fastAPIParam.asSource()
+ ) and
+ exists(this.getLocation().getFile().getRelativePath())
+ }
+ }
+}
+
+/**
+ * `io.TextIOWrapper(ip, encoding='utf-8')` like following:
+ * ```python
+ * with gzip.open(bomb_input, 'rb') as ip:
+ * with io.TextIOWrapper(ip, encoding='utf-8') as decoder:
+ * content = decoder.read()
+ * print(content)
+ * ```
+ * I saw this builtin method many places so I added it as a AdditionalTaintStep.
+ * it would be nice if it is added as a global AdditionalTaintStep
+ */
+predicate isAdditionalTaintStepTextIOWrapper(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
+ exists(API::CallNode textIOWrapper |
+ textIOWrapper = API::moduleImport("io").getMember("TextIOWrapper").getACall()
+ |
+ nodeFrom = textIOWrapper.getParameter(0, "input").asSink() and
+ nodeTo = textIOWrapper
+ ) and
+ exists(nodeTo.getLocation().getFile().getRelativePath())
+}
+
+module BombsConfig implements DataFlow::ConfigSig {
+ // borrowed from UnsafeUnpackQuery.qll
+ predicate isSource(DataFlow::Node source) {
+ source instanceof RemoteFlowSource
+ or
+ exists(MethodCallNode args |
+ args = source.(AttrRead).getObject().getALocalSource() and
+ args =
+ [
+ API::moduleImport("argparse")
+ .getMember("ArgumentParser")
+ .getReturn()
+ .getMember("parse_args")
+ .getACall(), API::moduleImport("os").getMember("getenv").getACall(),
+ API::moduleImport("os").getMember("environ").getMember("get").getACall()
+ ]
+ )
+ or
+ source instanceof FileAndFormRemoteFlowSource::FastAPI
+ }
+
+ predicate isSink(DataFlow::Node sink) {
+ sink =
+ [
+ pyZipFile::isSink(), ZipFile::isSink(), Gzip::isSink(), Lzma::isSink(), Bz2::isSink(),
+ TarFile::isSink(), Lzma::isSink(), Shutil::isSink(), Pandas::isSink()
+ ] and
+ exists(sink.getLocation().getFile().getRelativePath())
+ }
+
+ predicate isAdditionalFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
+ (
+ isAdditionalTaintStepTextIOWrapper(nodeFrom, nodeTo) or
+ ZipFile::isAdditionalTaintStep(nodeFrom, nodeTo) or
+ pyZipFile::isAdditionalTaintStep(nodeFrom, nodeTo) or
+ TarFile::isAdditionalTaintStep(nodeFrom, nodeTo)
+ ) and
+ exists(nodeTo.getLocation().getFile().getRelativePath())
+ }
+}
+
+module Bombs = TaintTracking::Global;
+
+import Bombs::PathGraph
+
+from Bombs::PathNode source, Bombs::PathNode sink
+where Bombs::flowPath(source, sink)
+select sink.getNode(), source, sink, "This file extraction depends on a $@.", source.getNode(),
+ "potentially untrusted source"
diff --git a/python/ql/src/experimental/Security/CWE-409/example_bad.py b/python/ql/src/experimental/Security/CWE-409/example_bad.py
new file mode 100644
index 00000000000..32442366458
--- /dev/null
+++ b/python/ql/src/experimental/Security/CWE-409/example_bad.py
@@ -0,0 +1,5 @@
+import zipfile
+
+
+def Bad(zip_path):
+ zipfile.ZipFile(zip_path, "r").extract("filename", "./tmp/")
diff --git a/python/ql/src/experimental/Security/CWE-409/example_good.py b/python/ql/src/experimental/Security/CWE-409/example_good.py
new file mode 100644
index 00000000000..f11c7bba097
--- /dev/null
+++ b/python/ql/src/experimental/Security/CWE-409/example_good.py
@@ -0,0 +1,21 @@
+import zipfile
+
+
+def safeUnzip(zipFileName):
+ buffer_size = 2 ** 10 * 8
+ total_size = 0
+ SIZE_THRESHOLD = 2 ** 10 * 100
+ with zipfile.ZipFile(zipFileName) as myzip:
+ for fileinfo in myzip.infolist():
+ content = b''
+ with myzip.open(fileinfo.filename, mode="r") as myfile:
+ chunk = myfile.read(buffer_size)
+ while chunk:
+ chunk = myfile.read(buffer_size)
+ total_size += buffer_size
+ if total_size > SIZE_THRESHOLD:
+ print("Bomb detected")
+ return
+ content += chunk
+ print(bytes.decode(content, 'utf-8'))
+ return "No Bombs!"
diff --git a/python/ql/test/experimental/query-tests/Security/CWE-409/DecompressionBombs.expected b/python/ql/test/experimental/query-tests/Security/CWE-409/DecompressionBombs.expected
new file mode 100644
index 00000000000..94839132e56
--- /dev/null
+++ b/python/ql/test/experimental/query-tests/Security/CWE-409/DecompressionBombs.expected
@@ -0,0 +1,258 @@
+edges
+| Other_compresstion_modules.py:7:10:7:19 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:9:27:9:36 | ControlFlowNode for bomb_input |
+| Other_compresstion_modules.py:7:10:7:19 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:11:15:11:24 | ControlFlowNode for bomb_input |
+| Other_compresstion_modules.py:7:10:7:19 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:12:19:12:28 | ControlFlowNode for bomb_input |
+| Other_compresstion_modules.py:7:10:7:19 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:14:15:14:24 | ControlFlowNode for bomb_input |
+| Other_compresstion_modules.py:7:10:7:19 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:15:19:15:28 | ControlFlowNode for bomb_input |
+| Other_compresstion_modules.py:7:10:7:19 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:17:20:17:29 | ControlFlowNode for bomb_input |
+| Other_compresstion_modules.py:7:10:7:19 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:23:14:23:23 | ControlFlowNode for bomb_input |
+| Other_compresstion_modules.py:7:10:7:19 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:24:17:24:26 | ControlFlowNode for bomb_input |
+| Other_compresstion_modules.py:7:10:7:19 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:27:40:27:49 | ControlFlowNode for bomb_input |
+| Other_compresstion_modules.py:7:10:7:19 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:29:23:29:32 | ControlFlowNode for bomb_input |
+| Other_compresstion_modules.py:7:10:7:19 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:30:21:30:30 | ControlFlowNode for bomb_input |
+| Other_compresstion_modules.py:7:10:7:19 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:32:40:32:49 | ControlFlowNode for bomb_input |
+| Other_compresstion_modules.py:7:10:7:19 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:33:22:33:31 | ControlFlowNode for bomb_input |
+| Other_compresstion_modules.py:7:10:7:19 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:34:21:34:30 | ControlFlowNode for bomb_input |
+| Other_compresstion_modules.py:7:10:7:19 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:35:42:35:51 | ControlFlowNode for bomb_input |
+| Other_compresstion_modules.py:7:10:7:19 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:36:23:36:32 | ControlFlowNode for bomb_input |
+| Other_compresstion_modules.py:7:10:7:19 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:37:36:37:45 | ControlFlowNode for bomb_input |
+| Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:53:10:53:19 | ControlFlowNode for bomb_input |
+| Other_compresstion_modules.py:53:10:53:19 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:7:10:7:19 | ControlFlowNode for bomb_input |
+| file:///usr/lib/python3.10/zipfile.py:344:24:344:31 | ControlFlowNode for filename | file:///usr/lib/python3.10/zipfile.py:351:24:351:44 | ControlFlowNode for Subscript |
+| file:///usr/lib/python3.10/zipfile.py:344:24:344:31 | ControlFlowNode for filename | file:///usr/lib/python3.10/zipfile.py:358:25:358:32 | ControlFlowNode for filename |
+| file:///usr/lib/python3.10/zipfile.py:351:24:351:44 | ControlFlowNode for Subscript | file:///usr/lib/python3.10/zipfile.py:358:25:358:32 | ControlFlowNode for filename |
+| file:///usr/lib/python3.10/zipfile.py:358:25:358:32 | ControlFlowNode for filename | file:///usr/lib/python3.10/zipfile.py:358:9:358:12 | [post] ControlFlowNode for self [Attribute filename] |
+| file:///usr/lib/python3.10/zipfile.py:1216:24:1216:27 | ControlFlowNode for file | file:///usr/lib/python3.10/zipfile.py:1258:23:1258:26 | ControlFlowNode for file |
+| file:///usr/lib/python3.10/zipfile.py:1258:13:1258:16 | [post] ControlFlowNode for self [Attribute fp] | file:///usr/lib/python3.10/zipfile.py:1259:13:1259:16 | ControlFlowNode for self [Attribute fp] |
+| file:///usr/lib/python3.10/zipfile.py:1258:23:1258:26 | ControlFlowNode for file | file:///usr/lib/python3.10/zipfile.py:1258:13:1258:16 | [post] ControlFlowNode for self [Attribute fp] |
+| file:///usr/lib/python3.10/zipfile.py:1259:13:1259:16 | ControlFlowNode for self [Attribute fp] | file:///usr/lib/python3.10/zipfile.py:1260:9:1260:12 | ControlFlowNode for self [Attribute fp] |
+| file:///usr/lib/python3.10/zipfile.py:1260:9:1260:12 | ControlFlowNode for self [Attribute fp] | file:///usr/lib/python3.10/zipfile.py:1261:9:1261:12 | ControlFlowNode for self [Attribute fp] |
+| file:///usr/lib/python3.10/zipfile.py:1261:9:1261:12 | ControlFlowNode for self [Attribute fp] | file:///usr/lib/python3.10/zipfile.py:1262:9:1262:12 | ControlFlowNode for self [Attribute fp] |
+| file:///usr/lib/python3.10/zipfile.py:1262:9:1262:12 | ControlFlowNode for self [Attribute fp] | file:///usr/lib/python3.10/zipfile.py:1263:9:1263:12 | ControlFlowNode for self [Attribute fp] |
+| file:///usr/lib/python3.10/zipfile.py:1263:9:1263:12 | ControlFlowNode for self [Attribute fp] | file:///usr/lib/python3.10/zipfile.py:1267:17:1267:20 | ControlFlowNode for self [Attribute fp] |
+| file:///usr/lib/python3.10/zipfile.py:1263:9:1263:12 | ControlFlowNode for self [Attribute fp] | file:///usr/lib/python3.10/zipfile.py:1287:21:1287:24 | ControlFlowNode for self [Attribute fp] |
+| file:///usr/lib/python3.10/zipfile.py:1267:17:1267:20 | ControlFlowNode for self [Attribute fp] | file:///usr/lib/python3.10/zipfile.py:1267:17:1267:20 | [post] ControlFlowNode for self [Attribute filelist, List element, Attribute filename] |
+| file:///usr/lib/python3.10/zipfile.py:1267:17:1267:20 | ControlFlowNode for self [Attribute fp] | file:///usr/lib/python3.10/zipfile.py:1326:26:1326:29 | ControlFlowNode for self [Attribute fp] |
+| file:///usr/lib/python3.10/zipfile.py:1287:21:1287:24 | ControlFlowNode for self [Attribute fp] | file:///usr/lib/python3.10/zipfile.py:1287:21:1287:24 | [post] ControlFlowNode for self [Attribute filelist, List element, Attribute filename] |
+| file:///usr/lib/python3.10/zipfile.py:1287:21:1287:24 | ControlFlowNode for self [Attribute fp] | file:///usr/lib/python3.10/zipfile.py:1326:26:1326:29 | ControlFlowNode for self [Attribute fp] |
+| file:///usr/lib/python3.10/zipfile.py:1326:26:1326:29 | ControlFlowNode for self [Attribute fp] | file:///usr/lib/python3.10/zipfile.py:1328:14:1328:17 | ControlFlowNode for self [Attribute fp] |
+| file:///usr/lib/python3.10/zipfile.py:1328:14:1328:17 | ControlFlowNode for self [Attribute fp] | file:///usr/lib/python3.10/zipfile.py:1328:14:1328:20 | ControlFlowNode for Attribute |
+| file:///usr/lib/python3.10/zipfile.py:1328:14:1328:20 | ControlFlowNode for Attribute | file:///usr/lib/python3.10/zipfile.py:1376:25:1376:32 | ControlFlowNode for filename |
+| file:///usr/lib/python3.10/zipfile.py:1376:17:1376:33 | ControlFlowNode for ZipInfo() [Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1377:13:1377:13 | ControlFlowNode for x [Attribute filename] |
+| file:///usr/lib/python3.10/zipfile.py:1376:25:1376:32 | ControlFlowNode for filename | file:///usr/lib/python3.10/zipfile.py:344:24:344:31 | ControlFlowNode for filename |
+| file:///usr/lib/python3.10/zipfile.py:1376:25:1376:32 | ControlFlowNode for filename | file:///usr/lib/python3.10/zipfile.py:1376:17:1376:33 | ControlFlowNode for ZipInfo() [Attribute filename] |
+| file:///usr/lib/python3.10/zipfile.py:1377:13:1377:13 | ControlFlowNode for x [Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1378:13:1378:13 | ControlFlowNode for x [Attribute filename] |
+| file:///usr/lib/python3.10/zipfile.py:1378:13:1378:13 | ControlFlowNode for x [Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1379:13:1379:13 | ControlFlowNode for x [Attribute filename] |
+| file:///usr/lib/python3.10/zipfile.py:1379:13:1379:13 | ControlFlowNode for x [Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1388:13:1388:13 | ControlFlowNode for x [Attribute filename] |
+| file:///usr/lib/python3.10/zipfile.py:1388:13:1388:13 | ControlFlowNode for x [Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1389:13:1389:13 | ControlFlowNode for x [Attribute filename] |
+| file:///usr/lib/python3.10/zipfile.py:1389:13:1389:13 | ControlFlowNode for x [Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1393:13:1393:13 | ControlFlowNode for x [Attribute filename] |
+| file:///usr/lib/python3.10/zipfile.py:1393:13:1393:13 | ControlFlowNode for x [Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1394:34:1394:34 | ControlFlowNode for x [Attribute filename] |
+| file:///usr/lib/python3.10/zipfile.py:1394:13:1394:25 | [post] ControlFlowNode for Attribute [List element, Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1394:13:1394:16 | [post] ControlFlowNode for self [Attribute filelist, List element, Attribute filename] |
+| file:///usr/lib/python3.10/zipfile.py:1394:34:1394:34 | ControlFlowNode for x [Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1394:13:1394:25 | [post] ControlFlowNode for Attribute [List element, Attribute filename] |
+| file:///usr/lib/python3.10/zipfile.py:1410:18:1410:21 | ControlFlowNode for self [Attribute filelist, List element, Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1413:16:1413:19 | ControlFlowNode for self [Attribute filelist, List element, Attribute filename] |
+| file:///usr/lib/python3.10/zipfile.py:1413:16:1413:19 | ControlFlowNode for self [Attribute filelist, List element, Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1413:16:1413:28 | ControlFlowNode for Attribute [List element, Attribute filename] |
+| file:///usr/lib/python3.10/zipfile.py:1617:23:1617:28 | ControlFlowNode for member [Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1628:37:1628:42 | ControlFlowNode for member [Attribute filename] |
+| file:///usr/lib/python3.10/zipfile.py:1628:37:1628:42 | ControlFlowNode for member [Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1628:16:1628:54 | ControlFlowNode for Attribute() |
+| file:///usr/lib/python3.10/zipfile.py:1628:37:1628:42 | ControlFlowNode for member [Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1662:31:1662:36 | ControlFlowNode for member [Attribute filename] |
+| file:///usr/lib/python3.10/zipfile.py:1662:31:1662:36 | ControlFlowNode for member [Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1671:19:1671:24 | ControlFlowNode for member [Attribute filename] |
+| file:///usr/lib/python3.10/zipfile.py:1671:19:1671:24 | ControlFlowNode for member [Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1671:19:1671:33 | ControlFlowNode for Attribute |
+| file:///usr/lib/python3.10/zipfile.py:1671:19:1671:33 | ControlFlowNode for Attribute | file:///usr/lib/python3.10/zipfile.py:1677:19:1677:48 | ControlFlowNode for Subscript |
+| file:///usr/lib/python3.10/zipfile.py:1677:19:1677:48 | ControlFlowNode for Subscript | file:///usr/lib/python3.10/zipfile.py:1679:42:1679:42 | SSA variable x |
+| file:///usr/lib/python3.10/zipfile.py:1679:36:1680:65 | ControlFlowNode for GeneratorExp | file:///usr/lib/python3.10/zipfile.py:1696:20:1696:29 | ControlFlowNode for targetpath |
+| file:///usr/lib/python3.10/zipfile.py:1679:36:1680:65 | ControlFlowNode for GeneratorExp | file:///usr/lib/python3.10/zipfile.py:1702:16:1702:25 | ControlFlowNode for targetpath |
+| file:///usr/lib/python3.10/zipfile.py:1679:42:1679:42 | SSA variable x | file:///usr/lib/python3.10/zipfile.py:1679:36:1680:65 | ControlFlowNode for GeneratorExp |
+| tarfile_module.py:5:17:5:24 | ControlFlowNode for tar_path | tarfile_module.py:8:5:8:54 | ControlFlowNode for Attribute() |
+| tarfile_module.py:5:17:5:24 | ControlFlowNode for tar_path | tarfile_module.py:9:5:9:54 | ControlFlowNode for Attribute() |
+| tarfile_module.py:5:17:5:24 | ControlFlowNode for tar_path | tarfile_module.py:10:5:10:56 | ControlFlowNode for Attribute() |
+| tarfile_module.py:5:17:5:24 | ControlFlowNode for tar_path | tarfile_module.py:11:5:11:49 | ControlFlowNode for Attribute() |
+| tarfile_module.py:5:17:5:24 | ControlFlowNode for tar_path | tarfile_module.py:12:5:12:59 | ControlFlowNode for Attribute() |
+| tarfile_module.py:5:17:5:24 | ControlFlowNode for tar_path | tarfile_module.py:13:5:13:51 | ControlFlowNode for Attribute() |
+| tarfile_module.py:29:19:29:24 | ControlFlowNode for bar_id | tarfile_module.py:30:17:30:22 | ControlFlowNode for bar_id |
+| tarfile_module.py:30:17:30:22 | ControlFlowNode for bar_id | tarfile_module.py:5:17:5:24 | ControlFlowNode for tar_path |
+| zipfile_module.py:9:17:9:24 | ControlFlowNode for zip_path | zipfile_module.py:11:5:11:57 | ControlFlowNode for Attribute() |
+| zipfile_module.py:9:17:9:24 | ControlFlowNode for zip_path | zipfile_module.py:12:34:12:41 | ControlFlowNode for zip_path |
+| zipfile_module.py:9:17:9:24 | ControlFlowNode for zip_path | zipfile_module.py:15:5:15:38 | ControlFlowNode for Attribute() |
+| zipfile_module.py:9:17:9:24 | ControlFlowNode for zip_path | zipfile_module.py:17:5:17:58 | ControlFlowNode for Attribute() |
+| zipfile_module.py:12:18:12:47 | ControlFlowNode for Attribute() [Attribute filelist, List element, Attribute filename] | zipfile_module.py:17:24:17:33 | ControlFlowNode for zipFileObj [Attribute filelist, List element, Attribute filename] |
+| zipfile_module.py:12:34:12:41 | ControlFlowNode for zip_path | file:///usr/lib/python3.10/zipfile.py:1216:24:1216:27 | ControlFlowNode for file |
+| zipfile_module.py:12:34:12:41 | ControlFlowNode for zip_path | zipfile_module.py:12:18:12:47 | ControlFlowNode for Attribute() [Attribute filelist, List element, Attribute filename] |
+| zipfile_module.py:17:24:17:33 | ControlFlowNode for zipFileObj [Attribute filelist, List element, Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1410:18:1410:21 | ControlFlowNode for self [Attribute filelist, List element, Attribute filename] |
+| zipfile_module.py:17:24:17:33 | ControlFlowNode for zipFileObj [Attribute filelist, List element, Attribute filename] | zipfile_module.py:17:24:17:44 | ControlFlowNode for Attribute() [List element, Attribute filename] |
+| zipfile_module.py:17:24:17:44 | ControlFlowNode for Attribute() [List element, Attribute filename] | zipfile_module.py:17:24:17:47 | ControlFlowNode for Subscript [Attribute filename] |
+| zipfile_module.py:17:24:17:47 | ControlFlowNode for Subscript [Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1617:23:1617:28 | ControlFlowNode for member [Attribute filename] |
+| zipfile_module.py:17:24:17:47 | ControlFlowNode for Subscript [Attribute filename] | zipfile_module.py:17:5:17:58 | ControlFlowNode for Attribute() |
+| zipfile_module.py:20:20:20:27 | ControlFlowNode for zip_path | zipfile_module.py:30:5:30:35 | ControlFlowNode for Attribute() |
+| zipfile_module.py:34:18:34:25 | ControlFlowNode for zip_path | zipfile_module.py:37:14:37:29 | ControlFlowNode for Attribute() |
+| zipfile_module.py:34:18:34:25 | ControlFlowNode for zip_path | zipfile_module.py:42:29:42:36 | ControlFlowNode for Attribute |
+| zipfile_module.py:34:18:34:25 | ControlFlowNode for zip_path | zipfile_module.py:45:14:45:39 | ControlFlowNode for Attribute() |
+| zipfile_module.py:34:18:34:25 | ControlFlowNode for zip_path | zipfile_module.py:53:5:53:35 | ControlFlowNode for Attribute() |
+| zipfile_module.py:34:18:34:25 | ControlFlowNode for zip_path | zipfile_module.py:62:44:62:61 | ControlFlowNode for Attribute() |
+| zipfile_module.py:42:21:42:37 | ControlFlowNode for BytesIO() | file:///usr/lib/python3.10/zipfile.py:1216:24:1216:27 | ControlFlowNode for file |
+| zipfile_module.py:42:21:42:37 | ControlFlowNode for BytesIO() | zipfile_module.py:42:5:42:54 | ControlFlowNode for Attribute() |
+| zipfile_module.py:42:29:42:36 | ControlFlowNode for Attribute | zipfile_module.py:42:5:42:54 | ControlFlowNode for Attribute() |
+| zipfile_module.py:42:29:42:36 | ControlFlowNode for Attribute | zipfile_module.py:42:21:42:37 | ControlFlowNode for BytesIO() |
+| zipfile_module.py:89:19:89:24 | ControlFlowNode for bar_id | zipfile_module.py:90:20:90:25 | ControlFlowNode for bar_id |
+| zipfile_module.py:89:19:89:24 | ControlFlowNode for bar_id | zipfile_module.py:91:17:91:22 | ControlFlowNode for bar_id |
+| zipfile_module.py:89:19:89:24 | ControlFlowNode for bar_id | zipfile_module.py:92:18:92:23 | ControlFlowNode for bar_id |
+| zipfile_module.py:90:20:90:25 | ControlFlowNode for bar_id | zipfile_module.py:20:20:20:27 | ControlFlowNode for zip_path |
+| zipfile_module.py:91:17:91:22 | ControlFlowNode for bar_id | zipfile_module.py:9:17:9:24 | ControlFlowNode for zip_path |
+| zipfile_module.py:92:18:92:23 | ControlFlowNode for bar_id | zipfile_module.py:34:18:34:25 | ControlFlowNode for zip_path |
+| zipfile_module.py:99:26:99:34 | ControlFlowNode for Attribute | file:///usr/lib/python3.10/zipfile.py:1216:24:1216:27 | ControlFlowNode for file |
+| zipfile_module.py:99:26:99:34 | ControlFlowNode for Attribute | zipfile_module.py:100:14:100:29 | ControlFlowNode for Attribute() |
+| zipfile_module.py:106:23:106:26 | ControlFlowNode for file | zipfile_module.py:108:14:108:29 | ControlFlowNode for Attribute() |
+| zipfile_module.py:116:30:116:38 | ControlFlowNode for Attribute | file:///usr/lib/python3.10/zipfile.py:1216:24:1216:27 | ControlFlowNode for file |
+| zipfile_module.py:116:30:116:38 | ControlFlowNode for Attribute | zipfile_module.py:117:18:117:33 | ControlFlowNode for Attribute() |
+| zipfile_module.py:125:30:125:38 | ControlFlowNode for Attribute | file:///usr/lib/python3.10/zipfile.py:1216:24:1216:27 | ControlFlowNode for file |
+| zipfile_module.py:125:30:125:38 | ControlFlowNode for Attribute | zipfile_module.py:126:18:126:33 | ControlFlowNode for Attribute() |
+nodes
+| Other_compresstion_modules.py:7:10:7:19 | ControlFlowNode for bomb_input | semmle.label | ControlFlowNode for bomb_input |
+| Other_compresstion_modules.py:9:27:9:36 | ControlFlowNode for bomb_input | semmle.label | ControlFlowNode for bomb_input |
+| Other_compresstion_modules.py:11:15:11:24 | ControlFlowNode for bomb_input | semmle.label | ControlFlowNode for bomb_input |
+| Other_compresstion_modules.py:12:19:12:28 | ControlFlowNode for bomb_input | semmle.label | ControlFlowNode for bomb_input |
+| Other_compresstion_modules.py:14:15:14:24 | ControlFlowNode for bomb_input | semmle.label | ControlFlowNode for bomb_input |
+| Other_compresstion_modules.py:15:19:15:28 | ControlFlowNode for bomb_input | semmle.label | ControlFlowNode for bomb_input |
+| Other_compresstion_modules.py:17:20:17:29 | ControlFlowNode for bomb_input | semmle.label | ControlFlowNode for bomb_input |
+| Other_compresstion_modules.py:23:14:23:23 | ControlFlowNode for bomb_input | semmle.label | ControlFlowNode for bomb_input |
+| Other_compresstion_modules.py:24:17:24:26 | ControlFlowNode for bomb_input | semmle.label | ControlFlowNode for bomb_input |
+| Other_compresstion_modules.py:27:40:27:49 | ControlFlowNode for bomb_input | semmle.label | ControlFlowNode for bomb_input |
+| Other_compresstion_modules.py:29:23:29:32 | ControlFlowNode for bomb_input | semmle.label | ControlFlowNode for bomb_input |
+| Other_compresstion_modules.py:30:21:30:30 | ControlFlowNode for bomb_input | semmle.label | ControlFlowNode for bomb_input |
+| Other_compresstion_modules.py:32:40:32:49 | ControlFlowNode for bomb_input | semmle.label | ControlFlowNode for bomb_input |
+| Other_compresstion_modules.py:33:22:33:31 | ControlFlowNode for bomb_input | semmle.label | ControlFlowNode for bomb_input |
+| Other_compresstion_modules.py:34:21:34:30 | ControlFlowNode for bomb_input | semmle.label | ControlFlowNode for bomb_input |
+| Other_compresstion_modules.py:35:42:35:51 | ControlFlowNode for bomb_input | semmle.label | ControlFlowNode for bomb_input |
+| Other_compresstion_modules.py:36:23:36:32 | ControlFlowNode for bomb_input | semmle.label | ControlFlowNode for bomb_input |
+| Other_compresstion_modules.py:37:36:37:45 | ControlFlowNode for bomb_input | semmle.label | ControlFlowNode for bomb_input |
+| Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | semmle.label | ControlFlowNode for bomb_input |
+| Other_compresstion_modules.py:53:10:53:19 | ControlFlowNode for bomb_input | semmle.label | ControlFlowNode for bomb_input |
+| file:///usr/lib/python3.10/zipfile.py:344:24:344:31 | ControlFlowNode for filename | semmle.label | ControlFlowNode for filename |
+| file:///usr/lib/python3.10/zipfile.py:351:24:351:44 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
+| file:///usr/lib/python3.10/zipfile.py:358:9:358:12 | [post] ControlFlowNode for self [Attribute filename] | semmle.label | [post] ControlFlowNode for self [Attribute filename] |
+| file:///usr/lib/python3.10/zipfile.py:358:25:358:32 | ControlFlowNode for filename | semmle.label | ControlFlowNode for filename |
+| file:///usr/lib/python3.10/zipfile.py:1216:24:1216:27 | ControlFlowNode for file | semmle.label | ControlFlowNode for file |
+| file:///usr/lib/python3.10/zipfile.py:1258:13:1258:16 | [post] ControlFlowNode for self [Attribute fp] | semmle.label | [post] ControlFlowNode for self [Attribute fp] |
+| file:///usr/lib/python3.10/zipfile.py:1258:23:1258:26 | ControlFlowNode for file | semmle.label | ControlFlowNode for file |
+| file:///usr/lib/python3.10/zipfile.py:1259:13:1259:16 | ControlFlowNode for self [Attribute fp] | semmle.label | ControlFlowNode for self [Attribute fp] |
+| file:///usr/lib/python3.10/zipfile.py:1260:9:1260:12 | ControlFlowNode for self [Attribute fp] | semmle.label | ControlFlowNode for self [Attribute fp] |
+| file:///usr/lib/python3.10/zipfile.py:1261:9:1261:12 | ControlFlowNode for self [Attribute fp] | semmle.label | ControlFlowNode for self [Attribute fp] |
+| file:///usr/lib/python3.10/zipfile.py:1262:9:1262:12 | ControlFlowNode for self [Attribute fp] | semmle.label | ControlFlowNode for self [Attribute fp] |
+| file:///usr/lib/python3.10/zipfile.py:1263:9:1263:12 | ControlFlowNode for self [Attribute fp] | semmle.label | ControlFlowNode for self [Attribute fp] |
+| file:///usr/lib/python3.10/zipfile.py:1267:17:1267:20 | ControlFlowNode for self [Attribute fp] | semmle.label | ControlFlowNode for self [Attribute fp] |
+| file:///usr/lib/python3.10/zipfile.py:1267:17:1267:20 | [post] ControlFlowNode for self [Attribute filelist, List element, Attribute filename] | semmle.label | [post] ControlFlowNode for self [Attribute filelist, List element, Attribute filename] |
+| file:///usr/lib/python3.10/zipfile.py:1287:21:1287:24 | ControlFlowNode for self [Attribute fp] | semmle.label | ControlFlowNode for self [Attribute fp] |
+| file:///usr/lib/python3.10/zipfile.py:1287:21:1287:24 | [post] ControlFlowNode for self [Attribute filelist, List element, Attribute filename] | semmle.label | [post] ControlFlowNode for self [Attribute filelist, List element, Attribute filename] |
+| file:///usr/lib/python3.10/zipfile.py:1326:26:1326:29 | ControlFlowNode for self [Attribute fp] | semmle.label | ControlFlowNode for self [Attribute fp] |
+| file:///usr/lib/python3.10/zipfile.py:1328:14:1328:17 | ControlFlowNode for self [Attribute fp] | semmle.label | ControlFlowNode for self [Attribute fp] |
+| file:///usr/lib/python3.10/zipfile.py:1328:14:1328:20 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| file:///usr/lib/python3.10/zipfile.py:1376:17:1376:33 | ControlFlowNode for ZipInfo() [Attribute filename] | semmle.label | ControlFlowNode for ZipInfo() [Attribute filename] |
+| file:///usr/lib/python3.10/zipfile.py:1376:25:1376:32 | ControlFlowNode for filename | semmle.label | ControlFlowNode for filename |
+| file:///usr/lib/python3.10/zipfile.py:1377:13:1377:13 | ControlFlowNode for x [Attribute filename] | semmle.label | ControlFlowNode for x [Attribute filename] |
+| file:///usr/lib/python3.10/zipfile.py:1378:13:1378:13 | ControlFlowNode for x [Attribute filename] | semmle.label | ControlFlowNode for x [Attribute filename] |
+| file:///usr/lib/python3.10/zipfile.py:1379:13:1379:13 | ControlFlowNode for x [Attribute filename] | semmle.label | ControlFlowNode for x [Attribute filename] |
+| file:///usr/lib/python3.10/zipfile.py:1388:13:1388:13 | ControlFlowNode for x [Attribute filename] | semmle.label | ControlFlowNode for x [Attribute filename] |
+| file:///usr/lib/python3.10/zipfile.py:1389:13:1389:13 | ControlFlowNode for x [Attribute filename] | semmle.label | ControlFlowNode for x [Attribute filename] |
+| file:///usr/lib/python3.10/zipfile.py:1393:13:1393:13 | ControlFlowNode for x [Attribute filename] | semmle.label | ControlFlowNode for x [Attribute filename] |
+| file:///usr/lib/python3.10/zipfile.py:1394:13:1394:16 | [post] ControlFlowNode for self [Attribute filelist, List element, Attribute filename] | semmle.label | [post] ControlFlowNode for self [Attribute filelist, List element, Attribute filename] |
+| file:///usr/lib/python3.10/zipfile.py:1394:13:1394:25 | [post] ControlFlowNode for Attribute [List element, Attribute filename] | semmle.label | [post] ControlFlowNode for Attribute [List element, Attribute filename] |
+| file:///usr/lib/python3.10/zipfile.py:1394:34:1394:34 | ControlFlowNode for x [Attribute filename] | semmle.label | ControlFlowNode for x [Attribute filename] |
+| file:///usr/lib/python3.10/zipfile.py:1410:18:1410:21 | ControlFlowNode for self [Attribute filelist, List element, Attribute filename] | semmle.label | ControlFlowNode for self [Attribute filelist, List element, Attribute filename] |
+| file:///usr/lib/python3.10/zipfile.py:1413:16:1413:19 | ControlFlowNode for self [Attribute filelist, List element, Attribute filename] | semmle.label | ControlFlowNode for self [Attribute filelist, List element, Attribute filename] |
+| file:///usr/lib/python3.10/zipfile.py:1413:16:1413:28 | ControlFlowNode for Attribute [List element, Attribute filename] | semmle.label | ControlFlowNode for Attribute [List element, Attribute filename] |
+| file:///usr/lib/python3.10/zipfile.py:1617:23:1617:28 | ControlFlowNode for member [Attribute filename] | semmle.label | ControlFlowNode for member [Attribute filename] |
+| file:///usr/lib/python3.10/zipfile.py:1628:16:1628:54 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| file:///usr/lib/python3.10/zipfile.py:1628:37:1628:42 | ControlFlowNode for member [Attribute filename] | semmle.label | ControlFlowNode for member [Attribute filename] |
+| file:///usr/lib/python3.10/zipfile.py:1662:31:1662:36 | ControlFlowNode for member [Attribute filename] | semmle.label | ControlFlowNode for member [Attribute filename] |
+| file:///usr/lib/python3.10/zipfile.py:1671:19:1671:24 | ControlFlowNode for member [Attribute filename] | semmle.label | ControlFlowNode for member [Attribute filename] |
+| file:///usr/lib/python3.10/zipfile.py:1671:19:1671:33 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| file:///usr/lib/python3.10/zipfile.py:1677:19:1677:48 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
+| file:///usr/lib/python3.10/zipfile.py:1679:36:1680:65 | ControlFlowNode for GeneratorExp | semmle.label | ControlFlowNode for GeneratorExp |
+| file:///usr/lib/python3.10/zipfile.py:1679:42:1679:42 | SSA variable x | semmle.label | SSA variable x |
+| file:///usr/lib/python3.10/zipfile.py:1696:20:1696:29 | ControlFlowNode for targetpath | semmle.label | ControlFlowNode for targetpath |
+| file:///usr/lib/python3.10/zipfile.py:1702:16:1702:25 | ControlFlowNode for targetpath | semmle.label | ControlFlowNode for targetpath |
+| tarfile_module.py:5:17:5:24 | ControlFlowNode for tar_path | semmle.label | ControlFlowNode for tar_path |
+| tarfile_module.py:8:5:8:54 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| tarfile_module.py:9:5:9:54 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| tarfile_module.py:10:5:10:56 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| tarfile_module.py:11:5:11:49 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| tarfile_module.py:12:5:12:59 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| tarfile_module.py:13:5:13:51 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| tarfile_module.py:29:19:29:24 | ControlFlowNode for bar_id | semmle.label | ControlFlowNode for bar_id |
+| tarfile_module.py:30:17:30:22 | ControlFlowNode for bar_id | semmle.label | ControlFlowNode for bar_id |
+| zipfile_module.py:9:17:9:24 | ControlFlowNode for zip_path | semmle.label | ControlFlowNode for zip_path |
+| zipfile_module.py:11:5:11:57 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| zipfile_module.py:12:18:12:47 | ControlFlowNode for Attribute() [Attribute filelist, List element, Attribute filename] | semmle.label | ControlFlowNode for Attribute() [Attribute filelist, List element, Attribute filename] |
+| zipfile_module.py:12:34:12:41 | ControlFlowNode for zip_path | semmle.label | ControlFlowNode for zip_path |
+| zipfile_module.py:15:5:15:38 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| zipfile_module.py:17:5:17:58 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| zipfile_module.py:17:24:17:33 | ControlFlowNode for zipFileObj [Attribute filelist, List element, Attribute filename] | semmle.label | ControlFlowNode for zipFileObj [Attribute filelist, List element, Attribute filename] |
+| zipfile_module.py:17:24:17:44 | ControlFlowNode for Attribute() [List element, Attribute filename] | semmle.label | ControlFlowNode for Attribute() [List element, Attribute filename] |
+| zipfile_module.py:17:24:17:47 | ControlFlowNode for Subscript [Attribute filename] | semmle.label | ControlFlowNode for Subscript [Attribute filename] |
+| zipfile_module.py:20:20:20:27 | ControlFlowNode for zip_path | semmle.label | ControlFlowNode for zip_path |
+| zipfile_module.py:30:5:30:35 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| zipfile_module.py:34:18:34:25 | ControlFlowNode for zip_path | semmle.label | ControlFlowNode for zip_path |
+| zipfile_module.py:37:14:37:29 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| zipfile_module.py:42:5:42:54 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| zipfile_module.py:42:21:42:37 | ControlFlowNode for BytesIO() | semmle.label | ControlFlowNode for BytesIO() |
+| zipfile_module.py:42:29:42:36 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| zipfile_module.py:45:14:45:39 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| zipfile_module.py:53:5:53:35 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| zipfile_module.py:62:44:62:61 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| zipfile_module.py:89:19:89:24 | ControlFlowNode for bar_id | semmle.label | ControlFlowNode for bar_id |
+| zipfile_module.py:90:20:90:25 | ControlFlowNode for bar_id | semmle.label | ControlFlowNode for bar_id |
+| zipfile_module.py:91:17:91:22 | ControlFlowNode for bar_id | semmle.label | ControlFlowNode for bar_id |
+| zipfile_module.py:92:18:92:23 | ControlFlowNode for bar_id | semmle.label | ControlFlowNode for bar_id |
+| zipfile_module.py:99:26:99:34 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| zipfile_module.py:100:14:100:29 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| zipfile_module.py:106:23:106:26 | ControlFlowNode for file | semmle.label | ControlFlowNode for file |
+| zipfile_module.py:108:14:108:29 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| zipfile_module.py:116:30:116:38 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| zipfile_module.py:117:18:117:33 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| zipfile_module.py:125:30:125:38 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
+| zipfile_module.py:126:18:126:33 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+subpaths
+| file:///usr/lib/python3.10/zipfile.py:1267:17:1267:20 | ControlFlowNode for self [Attribute fp] | file:///usr/lib/python3.10/zipfile.py:1326:26:1326:29 | ControlFlowNode for self [Attribute fp] | file:///usr/lib/python3.10/zipfile.py:1394:13:1394:16 | [post] ControlFlowNode for self [Attribute filelist, List element, Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1267:17:1267:20 | [post] ControlFlowNode for self [Attribute filelist, List element, Attribute filename] |
+| file:///usr/lib/python3.10/zipfile.py:1287:21:1287:24 | ControlFlowNode for self [Attribute fp] | file:///usr/lib/python3.10/zipfile.py:1326:26:1326:29 | ControlFlowNode for self [Attribute fp] | file:///usr/lib/python3.10/zipfile.py:1394:13:1394:16 | [post] ControlFlowNode for self [Attribute filelist, List element, Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1287:21:1287:24 | [post] ControlFlowNode for self [Attribute filelist, List element, Attribute filename] |
+| file:///usr/lib/python3.10/zipfile.py:1376:25:1376:32 | ControlFlowNode for filename | file:///usr/lib/python3.10/zipfile.py:344:24:344:31 | ControlFlowNode for filename | file:///usr/lib/python3.10/zipfile.py:358:9:358:12 | [post] ControlFlowNode for self [Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1376:17:1376:33 | ControlFlowNode for ZipInfo() [Attribute filename] |
+| file:///usr/lib/python3.10/zipfile.py:1628:37:1628:42 | ControlFlowNode for member [Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1662:31:1662:36 | ControlFlowNode for member [Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1696:20:1696:29 | ControlFlowNode for targetpath | file:///usr/lib/python3.10/zipfile.py:1628:16:1628:54 | ControlFlowNode for Attribute() |
+| file:///usr/lib/python3.10/zipfile.py:1628:37:1628:42 | ControlFlowNode for member [Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1662:31:1662:36 | ControlFlowNode for member [Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1702:16:1702:25 | ControlFlowNode for targetpath | file:///usr/lib/python3.10/zipfile.py:1628:16:1628:54 | ControlFlowNode for Attribute() |
+| zipfile_module.py:12:34:12:41 | ControlFlowNode for zip_path | file:///usr/lib/python3.10/zipfile.py:1216:24:1216:27 | ControlFlowNode for file | file:///usr/lib/python3.10/zipfile.py:1267:17:1267:20 | [post] ControlFlowNode for self [Attribute filelist, List element, Attribute filename] | zipfile_module.py:12:18:12:47 | ControlFlowNode for Attribute() [Attribute filelist, List element, Attribute filename] |
+| zipfile_module.py:12:34:12:41 | ControlFlowNode for zip_path | file:///usr/lib/python3.10/zipfile.py:1216:24:1216:27 | ControlFlowNode for file | file:///usr/lib/python3.10/zipfile.py:1287:21:1287:24 | [post] ControlFlowNode for self [Attribute filelist, List element, Attribute filename] | zipfile_module.py:12:18:12:47 | ControlFlowNode for Attribute() [Attribute filelist, List element, Attribute filename] |
+| zipfile_module.py:17:24:17:33 | ControlFlowNode for zipFileObj [Attribute filelist, List element, Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1410:18:1410:21 | ControlFlowNode for self [Attribute filelist, List element, Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1413:16:1413:28 | ControlFlowNode for Attribute [List element, Attribute filename] | zipfile_module.py:17:24:17:44 | ControlFlowNode for Attribute() [List element, Attribute filename] |
+| zipfile_module.py:17:24:17:47 | ControlFlowNode for Subscript [Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1617:23:1617:28 | ControlFlowNode for member [Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1628:16:1628:54 | ControlFlowNode for Attribute() | zipfile_module.py:17:5:17:58 | ControlFlowNode for Attribute() |
+#select
+| Other_compresstion_modules.py:9:27:9:36 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:9:27:9:36 | ControlFlowNode for bomb_input | This file extraction depends on a $@. | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | potentially untrusted source |
+| Other_compresstion_modules.py:11:15:11:24 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:11:15:11:24 | ControlFlowNode for bomb_input | This file extraction depends on a $@. | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | potentially untrusted source |
+| Other_compresstion_modules.py:12:19:12:28 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:12:19:12:28 | ControlFlowNode for bomb_input | This file extraction depends on a $@. | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | potentially untrusted source |
+| Other_compresstion_modules.py:14:15:14:24 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:14:15:14:24 | ControlFlowNode for bomb_input | This file extraction depends on a $@. | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | potentially untrusted source |
+| Other_compresstion_modules.py:15:19:15:28 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:15:19:15:28 | ControlFlowNode for bomb_input | This file extraction depends on a $@. | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | potentially untrusted source |
+| Other_compresstion_modules.py:17:20:17:29 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:17:20:17:29 | ControlFlowNode for bomb_input | This file extraction depends on a $@. | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | potentially untrusted source |
+| Other_compresstion_modules.py:23:14:23:23 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:23:14:23:23 | ControlFlowNode for bomb_input | This file extraction depends on a $@. | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | potentially untrusted source |
+| Other_compresstion_modules.py:24:17:24:26 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:24:17:24:26 | ControlFlowNode for bomb_input | This file extraction depends on a $@. | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | potentially untrusted source |
+| Other_compresstion_modules.py:27:40:27:49 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:27:40:27:49 | ControlFlowNode for bomb_input | This file extraction depends on a $@. | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | potentially untrusted source |
+| Other_compresstion_modules.py:29:23:29:32 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:29:23:29:32 | ControlFlowNode for bomb_input | This file extraction depends on a $@. | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | potentially untrusted source |
+| Other_compresstion_modules.py:30:21:30:30 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:30:21:30:30 | ControlFlowNode for bomb_input | This file extraction depends on a $@. | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | potentially untrusted source |
+| Other_compresstion_modules.py:32:40:32:49 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:32:40:32:49 | ControlFlowNode for bomb_input | This file extraction depends on a $@. | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | potentially untrusted source |
+| Other_compresstion_modules.py:33:22:33:31 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:33:22:33:31 | ControlFlowNode for bomb_input | This file extraction depends on a $@. | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | potentially untrusted source |
+| Other_compresstion_modules.py:34:21:34:30 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:34:21:34:30 | ControlFlowNode for bomb_input | This file extraction depends on a $@. | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | potentially untrusted source |
+| Other_compresstion_modules.py:35:42:35:51 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:35:42:35:51 | ControlFlowNode for bomb_input | This file extraction depends on a $@. | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | potentially untrusted source |
+| Other_compresstion_modules.py:36:23:36:32 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:36:23:36:32 | ControlFlowNode for bomb_input | This file extraction depends on a $@. | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | potentially untrusted source |
+| Other_compresstion_modules.py:37:36:37:45 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:37:36:37:45 | ControlFlowNode for bomb_input | This file extraction depends on a $@. | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | potentially untrusted source |
+| tarfile_module.py:8:5:8:54 | ControlFlowNode for Attribute() | tarfile_module.py:29:19:29:24 | ControlFlowNode for bar_id | tarfile_module.py:8:5:8:54 | ControlFlowNode for Attribute() | This file extraction depends on a $@. | tarfile_module.py:29:19:29:24 | ControlFlowNode for bar_id | potentially untrusted source |
+| tarfile_module.py:9:5:9:54 | ControlFlowNode for Attribute() | tarfile_module.py:29:19:29:24 | ControlFlowNode for bar_id | tarfile_module.py:9:5:9:54 | ControlFlowNode for Attribute() | This file extraction depends on a $@. | tarfile_module.py:29:19:29:24 | ControlFlowNode for bar_id | potentially untrusted source |
+| tarfile_module.py:10:5:10:56 | ControlFlowNode for Attribute() | tarfile_module.py:29:19:29:24 | ControlFlowNode for bar_id | tarfile_module.py:10:5:10:56 | ControlFlowNode for Attribute() | This file extraction depends on a $@. | tarfile_module.py:29:19:29:24 | ControlFlowNode for bar_id | potentially untrusted source |
+| tarfile_module.py:11:5:11:49 | ControlFlowNode for Attribute() | tarfile_module.py:29:19:29:24 | ControlFlowNode for bar_id | tarfile_module.py:11:5:11:49 | ControlFlowNode for Attribute() | This file extraction depends on a $@. | tarfile_module.py:29:19:29:24 | ControlFlowNode for bar_id | potentially untrusted source |
+| tarfile_module.py:12:5:12:59 | ControlFlowNode for Attribute() | tarfile_module.py:29:19:29:24 | ControlFlowNode for bar_id | tarfile_module.py:12:5:12:59 | ControlFlowNode for Attribute() | This file extraction depends on a $@. | tarfile_module.py:29:19:29:24 | ControlFlowNode for bar_id | potentially untrusted source |
+| tarfile_module.py:13:5:13:51 | ControlFlowNode for Attribute() | tarfile_module.py:29:19:29:24 | ControlFlowNode for bar_id | tarfile_module.py:13:5:13:51 | ControlFlowNode for Attribute() | This file extraction depends on a $@. | tarfile_module.py:29:19:29:24 | ControlFlowNode for bar_id | potentially untrusted source |
+| zipfile_module.py:11:5:11:57 | ControlFlowNode for Attribute() | zipfile_module.py:89:19:89:24 | ControlFlowNode for bar_id | zipfile_module.py:11:5:11:57 | ControlFlowNode for Attribute() | This file extraction depends on a $@. | zipfile_module.py:89:19:89:24 | ControlFlowNode for bar_id | potentially untrusted source |
+| zipfile_module.py:15:5:15:38 | ControlFlowNode for Attribute() | zipfile_module.py:89:19:89:24 | ControlFlowNode for bar_id | zipfile_module.py:15:5:15:38 | ControlFlowNode for Attribute() | This file extraction depends on a $@. | zipfile_module.py:89:19:89:24 | ControlFlowNode for bar_id | potentially untrusted source |
+| zipfile_module.py:17:5:17:58 | ControlFlowNode for Attribute() | zipfile_module.py:89:19:89:24 | ControlFlowNode for bar_id | zipfile_module.py:17:5:17:58 | ControlFlowNode for Attribute() | This file extraction depends on a $@. | zipfile_module.py:89:19:89:24 | ControlFlowNode for bar_id | potentially untrusted source |
+| zipfile_module.py:30:5:30:35 | ControlFlowNode for Attribute() | zipfile_module.py:89:19:89:24 | ControlFlowNode for bar_id | zipfile_module.py:30:5:30:35 | ControlFlowNode for Attribute() | This file extraction depends on a $@. | zipfile_module.py:89:19:89:24 | ControlFlowNode for bar_id | potentially untrusted source |
+| zipfile_module.py:37:14:37:29 | ControlFlowNode for Attribute() | zipfile_module.py:89:19:89:24 | ControlFlowNode for bar_id | zipfile_module.py:37:14:37:29 | ControlFlowNode for Attribute() | This file extraction depends on a $@. | zipfile_module.py:89:19:89:24 | ControlFlowNode for bar_id | potentially untrusted source |
+| zipfile_module.py:42:5:42:54 | ControlFlowNode for Attribute() | zipfile_module.py:89:19:89:24 | ControlFlowNode for bar_id | zipfile_module.py:42:5:42:54 | ControlFlowNode for Attribute() | This file extraction depends on a $@. | zipfile_module.py:89:19:89:24 | ControlFlowNode for bar_id | potentially untrusted source |
+| zipfile_module.py:45:14:45:39 | ControlFlowNode for Attribute() | zipfile_module.py:89:19:89:24 | ControlFlowNode for bar_id | zipfile_module.py:45:14:45:39 | ControlFlowNode for Attribute() | This file extraction depends on a $@. | zipfile_module.py:89:19:89:24 | ControlFlowNode for bar_id | potentially untrusted source |
+| zipfile_module.py:53:5:53:35 | ControlFlowNode for Attribute() | zipfile_module.py:89:19:89:24 | ControlFlowNode for bar_id | zipfile_module.py:53:5:53:35 | ControlFlowNode for Attribute() | This file extraction depends on a $@. | zipfile_module.py:89:19:89:24 | ControlFlowNode for bar_id | potentially untrusted source |
+| zipfile_module.py:62:44:62:61 | ControlFlowNode for Attribute() | zipfile_module.py:89:19:89:24 | ControlFlowNode for bar_id | zipfile_module.py:62:44:62:61 | ControlFlowNode for Attribute() | This file extraction depends on a $@. | zipfile_module.py:89:19:89:24 | ControlFlowNode for bar_id | potentially untrusted source |
+| zipfile_module.py:100:14:100:29 | ControlFlowNode for Attribute() | zipfile_module.py:99:26:99:34 | ControlFlowNode for Attribute | zipfile_module.py:100:14:100:29 | ControlFlowNode for Attribute() | This file extraction depends on a $@. | zipfile_module.py:99:26:99:34 | ControlFlowNode for Attribute | potentially untrusted source |
+| zipfile_module.py:108:14:108:29 | ControlFlowNode for Attribute() | zipfile_module.py:106:23:106:26 | ControlFlowNode for file | zipfile_module.py:108:14:108:29 | ControlFlowNode for Attribute() | This file extraction depends on a $@. | zipfile_module.py:106:23:106:26 | ControlFlowNode for file | potentially untrusted source |
+| zipfile_module.py:117:18:117:33 | ControlFlowNode for Attribute() | zipfile_module.py:116:30:116:38 | ControlFlowNode for Attribute | zipfile_module.py:117:18:117:33 | ControlFlowNode for Attribute() | This file extraction depends on a $@. | zipfile_module.py:116:30:116:38 | ControlFlowNode for Attribute | potentially untrusted source |
+| zipfile_module.py:126:18:126:33 | ControlFlowNode for Attribute() | zipfile_module.py:125:30:125:38 | ControlFlowNode for Attribute | zipfile_module.py:126:18:126:33 | ControlFlowNode for Attribute() | This file extraction depends on a $@. | zipfile_module.py:125:30:125:38 | ControlFlowNode for Attribute | potentially untrusted source |
diff --git a/python/ql/test/experimental/query-tests/Security/CWE-409/DecompressionBombs.qlref b/python/ql/test/experimental/query-tests/Security/CWE-409/DecompressionBombs.qlref
new file mode 100644
index 00000000000..5d425772f9a
--- /dev/null
+++ b/python/ql/test/experimental/query-tests/Security/CWE-409/DecompressionBombs.qlref
@@ -0,0 +1 @@
+experimental/Security/CWE-409/DecompressionBombs.ql
\ No newline at end of file
diff --git a/python/ql/test/experimental/query-tests/Security/CWE-409/Other_compresstion_modules.py b/python/ql/test/experimental/query-tests/Security/CWE-409/Other_compresstion_modules.py
new file mode 100644
index 00000000000..0c5e51d24ca
--- /dev/null
+++ b/python/ql/test/experimental/query-tests/Security/CWE-409/Other_compresstion_modules.py
@@ -0,0 +1,54 @@
+import io
+from fastapi import FastAPI
+
+app = FastAPI()
+
+
+def bomb(bomb_input):
+ import shutil
+ shutil.unpack_archive(bomb_input)
+ import lzma
+ lzma.open(bomb_input)
+ lzma.LZMAFile(bomb_input).read()
+ import gzip
+ gzip.open(bomb_input)
+ gzip.GzipFile(bomb_input).read()
+
+ with gzip.open(bomb_input, 'rb') as ip:
+ with io.TextIOWrapper(ip, encoding='utf-8') as decoder:
+ content = decoder.read()
+ print(content)
+
+ import bz2
+ bz2.open(bomb_input)
+ bz2.BZ2File(bomb_input).read()
+
+ import pandas
+ pandas.read_csv(filepath_or_buffer=bomb_input)
+
+ pandas.read_table(bomb_input, compression='gzip')
+ pandas.read_xml(bomb_input, compression='gzip')
+
+ pandas.read_csv(filepath_or_buffer=bomb_input, compression='gzip')
+ pandas.read_json(bomb_input, compression='gzip')
+ pandas.read_sas(bomb_input, compression='gzip')
+ pandas.read_stata(filepath_or_buffer=bomb_input, compression='gzip')
+ pandas.read_table(bomb_input, compression='gzip')
+ pandas.read_xml(path_or_buffer=bomb_input, compression='gzip')
+
+ # no compression no DOS
+ pandas.read_table(bomb_input, compression='tar')
+ pandas.read_xml(bomb_input, compression='tar')
+
+ pandas.read_csv(filepath_or_buffer=bomb_input, compression='tar')
+ pandas.read_json(bomb_input, compression='tar')
+ pandas.read_sas(bomb_input, compression='tar')
+ pandas.read_stata(filepath_or_buffer=bomb_input, compression='tar')
+ pandas.read_table(bomb_input, compression='tar')
+ pandas.read_xml(path_or_buffer=bomb_input, compression='tar')
+
+
+@app.post("/bomb")
+async def bombs(bomb_input):
+ bomb(bomb_input)
+ return {"message": "non-async"}
diff --git a/python/ql/test/experimental/query-tests/Security/CWE-409/tarfile_module.py b/python/ql/test/experimental/query-tests/Security/CWE-409/tarfile_module.py
new file mode 100644
index 00000000000..cecfb8ee4d3
--- /dev/null
+++ b/python/ql/test/experimental/query-tests/Security/CWE-409/tarfile_module.py
@@ -0,0 +1,31 @@
+import tarfile
+from fastapi import FastAPI
+
+
+def tar_extract(tar_path):
+ tarfile.open(tar_path)
+ tarfile.open(tar_path)
+ tarfile.TarFile.open(tar_path).extract("somefile")
+ tarfile.TarFile.open(tar_path).extract("somefile")
+ tarfile.TarFile.xzopen(tar_path).extract("somefile")
+ tarfile.TarFile.gzopen(tar_path).extractall()
+ tarfile.TarFile.open(tar_path).extractfile("file1.txt")
+ tarfile.open(tar_path).extractfile("file1.txt")
+ # not working
+ tarInstance2 = tarfile.TarFile(tar_path)
+ tarInstance2.extractfile("file1.txt")
+ tarInstance2.extract("file1.txt")
+ tarfile.TarFile().open(tar_path)
+ # good
+ tarfile.open(tar_path, mode="w")
+ tarfile.TarFile.gzopen(tar_path, mode="w")
+ tarfile.TarFile.open(tar_path, mode="r:")
+
+
+app = FastAPI()
+
+
+@app.post("/zipbomb")
+async def zipbomb(bar_id):
+ tar_extract(bar_id)
+ return {"message": "non-async"}
diff --git a/python/ql/test/experimental/query-tests/Security/CWE-409/zipfile_module.py b/python/ql/test/experimental/query-tests/Security/CWE-409/zipfile_module.py
new file mode 100644
index 00000000000..ef235ba82fb
--- /dev/null
+++ b/python/ql/test/experimental/query-tests/Security/CWE-409/zipfile_module.py
@@ -0,0 +1,128 @@
+import zipfile
+from io import BytesIO
+from urllib import request
+import requests
+from fastapi import FastAPI, File, UploadFile
+from typing import List, Annotated
+
+
+def zip_extract(zip_path):
+ # we can have PyZipFile instead of ZipFile too
+ zipfile.ZipFile(zip_path, "r").extract("0", "./tmp/")
+ zipFileObj = zipfile.ZipFile(zip_path, "r")
+ zipFileObj2 = zipFileObj
+ # zipfile.ZipFile("user_input").extract(member="file_name") Consume a lot of CPU,Storage
+ zipFileObj2.extract("0", "./tmp/")
+ # zipfile.ZipFile("user_input").extract(member=ZipInfo_object) Consume a lot of CPU,Storage
+ zipFileObj.extract(zipFileObj.infolist()[0], "./tmp/")
+
+
+def zip_extractall(zip_path):
+ import httpx
+ filex = httpx.get(zip_path).read()
+ zipfile.ZipFile(BytesIO(filex), "r").extractall()
+ zipFileObj = zipfile.ZipFile(zip_path, "r")
+ for infolist in zipFileObj.infolist():
+ Decompression_ratio = infolist.file_size / infolist.compress_size
+ if Decompression_ratio > 10:
+ return
+ # zipfile.ZipFile("user_input").extractall() Consume a lot of CPU,Storage
+ zipFileObj.extractall("./tmp/") # Sensitive
+ zipFileObj.close()
+
+
+def zip_read_obj(zip_path):
+ # zipfile.ZipFile("user_input").open("file_name").read() Consume a lot of CPU,RAM,Storage
+ with zipfile.ZipFile(zip_path) as myzip:
+ with myzip.open('ZZ') as myfile:
+ a = myfile.readline()
+
+ file = requests.get(zip_path)
+ zipfile.ZipFile(BytesIO(file.raw), "w").open("aa")
+ zipfile.ZipFile(BytesIO(file.raw), "r").open("aa")
+
+ with zipfile.ZipFile(zip_path) as myzip:
+ with myzip.open('ZZ', mode="r") as myfile:
+ a = next(myfile)
+
+ with zipfile.ZipFile(zip_path) as myzip:
+ with myzip.open('ZZ', mode="w") as myfile:
+ a = myfile.write(b"tmpppp")
+
+ myzip = zipfile.PyZipFile(zip_path)
+ myzip.read(myzip.infolist()[0])
+
+ zipfile.ZipFile(BytesIO(request.urlopen(zip_path).read())
+ ).read("aFileNameInTheZipFile")
+
+ with zipfile.ZipFile(zip_path, 'w') as dst_zip:
+ with zipfile.ZipFile(zip_path) as src_zip:
+ for info in src_zip.infolist():
+ if info.filename.startswith('classes'):
+ dst_zip.writestr(info, src_zip.read(info))
+
+
+def safeUnzip(zipFileName):
+ buffer_size = 2 ** 10 * 8
+ total_size = 0
+ SIZE_THRESHOLD = 2 ** 10 * 100
+ with zipfile.ZipFile(zipFileName) as myzip:
+ for fileinfo in myzip.infolist():
+ content = b''
+ with myzip.open(fileinfo.filename, mode="r") as myfile:
+ chunk = myfile.read(buffer_size)
+ while chunk:
+ chunk = myfile.read(buffer_size)
+ total_size += buffer_size
+ if total_size > SIZE_THRESHOLD:
+ print("Bomb detected")
+ return
+ content += chunk
+ print(bytes.decode(content, 'utf-8'))
+ return "No Bombs!"
+
+
+app = FastAPI()
+
+
+@app.post("/zipbomb")
+async def zipbomb(bar_id):
+ zip_extractall(bar_id)
+ zip_extract(bar_id)
+ zip_read_obj(bar_id)
+ safeUnzip(bar_id)
+ return {"message": "non-async"}
+
+
+@app.post("/zipbomb2")
+async def zipbomb2(file: UploadFile):
+ with zipfile.ZipFile(file.file) as myzip:
+ with myzip.open('ZZ') as myfile:
+ a = myfile.readline()
+ return {"message": "non-async"}
+
+
+@app.post("/zipbomb2")
+async def create_file(file: Annotated[bytes | None, File()] = None):
+ with zipfile.ZipFile(file) as myzip:
+ with myzip.open('ZZ') as myfile:
+ a = myfile.readline()
+ return {"message": "non-async"}
+
+
+@app.post("/uploadMultipleFiles")
+def upload(files: List[UploadFile] = File(...)):
+ for file in files:
+ with zipfile.ZipFile(file.file) as myzip:
+ with myzip.open('ZZ') as myfile:
+ a = myfile.readline()
+ return {"message": "non-async"}
+
+
+@app.post("/files/")
+async def create_files(files: Annotated[list[bytes], File()]):
+ for file in files:
+ with zipfile.ZipFile(file.file) as myzip:
+ with myzip.open('ZZ') as myfile:
+ a = myfile.readline()
+ return {"file_sizes": [len(file) for file in files]}
diff --git a/python/ql/test/experimental/query-tests/Security/CWE-522/LDAPInsecureAuth.qlref b/python/ql/test/experimental/query-tests/Security/CWE-522/LDAPInsecureAuth.qlref
deleted file mode 100644
index 8bb2c1e9b52..00000000000
--- a/python/ql/test/experimental/query-tests/Security/CWE-522/LDAPInsecureAuth.qlref
+++ /dev/null
@@ -1 +0,0 @@
-experimental/Security/CWE-522/LDAPInsecureAuth.ql
\ No newline at end of file
From a38405e490b60366e696405d01f3ff9ce3a26b7c Mon Sep 17 00:00:00 2001
From: amammad
Date: Mon, 26 Jun 2023 16:43:18 +1000
Subject: [PATCH 002/378] fix formatting error/warnings
---
.../Security/CWE-409/DecompressionBombs.ql | 54 +++++++++----------
1 file changed, 24 insertions(+), 30 deletions(-)
diff --git a/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql b/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql
index 7f8f493decf..5aa3e90cae3 100644
--- a/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql
+++ b/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql
@@ -18,7 +18,7 @@ import semmle.python.ApiGraphs
import semmle.python.dataflow.new.RemoteFlowSources
import semmle.python.dataflow.new.internal.DataFlowPublic
-module pyZipFile {
+module PyZipFile {
/**
* ```python
* zipfile.PyZipFile()
@@ -78,7 +78,7 @@ module pyZipFile {
/**
* Same as ZipFile
- * I made PyZipFile seperated from ZipFile as in future this will be compatible
+ * I made PyZipFile separated from ZipFile as in future this will be compatible
* if anyone want to add new methods an sink to each object.
*/
predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
@@ -283,7 +283,7 @@ module ZipFile {
}
/**
- * a sanitizers which check if there is a managed read
+ * a sanitizers which check if there is a managed read
* ```python
* with zipfile.ZipFile(zipFileName) as myzip:
* with myzip.open(fileinfo.filename, mode="r") as myfile:
@@ -394,13 +394,11 @@ module TarFile {
module Shutil {
DataFlow::Node isSink() {
result =
- [
- API::moduleImport("shutil")
- .getMember("unpack_archive")
- .getACall()
- .getParameter(0, "filename")
- .asSink()
- ]
+ API::moduleImport("shutil")
+ .getMember("unpack_archive")
+ .getACall()
+ .getParameter(0, "filename")
+ .asSink()
}
}
@@ -445,8 +443,8 @@ module Pandas {
module FileAndFormRemoteFlowSource {
class FastAPI extends DataFlow::Node {
FastAPI() {
- exists(API::Node fastAPIParam |
- fastAPIParam =
+ exists(API::Node fastApiParam |
+ fastApiParam =
API::moduleImport("fastapi")
.getMember("FastAPI")
.getReturn()
@@ -459,11 +457,11 @@ module FileAndFormRemoteFlowSource {
.getASubclass*()
.getAValueReachableFromSource()
.asExpr() =
- fastAPIParam.asSource().asExpr().(Parameter).getAnnotation().getASubExpression*()
+ fastApiParam.asSource().asExpr().(Parameter).getAnnotation().getASubExpression*()
|
// in the case of List of files
exists(For f, Attribute attr, DataFlow::Node a, DataFlow::Node b |
- fastAPIParam.getAValueReachableFromSource().asExpr() = f.getIter().getASubExpression*()
+ fastApiParam.getAValueReachableFromSource().asExpr() = f.getIter().getASubExpression*()
|
// file.file in following
// def upload(files: List[UploadFile] = File(...)):
@@ -477,20 +475,18 @@ module FileAndFormRemoteFlowSource {
this.asExpr() = attr
)
or
- // exclude cases like type-annotated with `Response`
- // and not not any(Response::RequestHandlerParam src).asExpr() = result
this =
[
- fastAPIParam.asSource(),
- fastAPIParam.getMember(["filename", "content_type", "headers", "file"]).asSource(),
- fastAPIParam.getMember(["read"]).getReturn().asSource(),
+ fastApiParam.asSource(),
+ fastApiParam.getMember(["filename", "content_type", "headers", "file"]).asSource(),
+ fastApiParam.getMember("read").getReturn().asSource(),
// file-like object, I'm trying to not do additional work here by using already existing file-like objs if it is possible
- // fastAPIParam.getMember("file").getAMember().asSource(),
+ // fastApiParam.getMember("file").getAMember().asSource(),
]
)
or
- exists(API::Node fastAPIParam |
- fastAPIParam =
+ exists(API::Node fastApiParam |
+ fastApiParam =
API::moduleImport("fastapi")
.getMember("FastAPI")
.getReturn()
@@ -503,11 +499,11 @@ module FileAndFormRemoteFlowSource {
.getASubclass*()
.getAValueReachableFromSource()
.asExpr() =
- fastAPIParam.asSource().asExpr().(Parameter).getAnnotation().getASubExpression*()
+ fastApiParam.asSource().asExpr().(Parameter).getAnnotation().getASubExpression*()
|
// in the case of List of files
exists(For f, Attribute attr, DataFlow::Node a, DataFlow::Node b |
- fastAPIParam.getAValueReachableFromSource().asExpr() = f.getIter().getASubExpression*()
+ fastApiParam.getAValueReachableFromSource().asExpr() = f.getIter().getASubExpression*()
|
// file.file in following
// def upload(files: List[UploadFile] = File(...)):
@@ -521,9 +517,7 @@ module FileAndFormRemoteFlowSource {
this.asExpr() = attr
)
or
- // exclude cases like type-annotated with `Response`
- // and not not any(Response::RequestHandlerParam src).asExpr() = result
- this = fastAPIParam.asSource()
+ this = fastApiParam.asSource()
) and
exists(this.getLocation().getFile().getRelativePath())
}
@@ -575,8 +569,8 @@ module BombsConfig implements DataFlow::ConfigSig {
predicate isSink(DataFlow::Node sink) {
sink =
[
- pyZipFile::isSink(), ZipFile::isSink(), Gzip::isSink(), Lzma::isSink(), Bz2::isSink(),
- TarFile::isSink(), Lzma::isSink(), Shutil::isSink(), Pandas::isSink()
+ PyZipFile::isSink(), ZipFile::isSink(), Gzip::isSink(), Lzma::isSink(), Bz2::isSink(),
+ TarFile::isSink(), Shutil::isSink(), Pandas::isSink()
] and
exists(sink.getLocation().getFile().getRelativePath())
}
@@ -585,7 +579,7 @@ module BombsConfig implements DataFlow::ConfigSig {
(
isAdditionalTaintStepTextIOWrapper(nodeFrom, nodeTo) or
ZipFile::isAdditionalTaintStep(nodeFrom, nodeTo) or
- pyZipFile::isAdditionalTaintStep(nodeFrom, nodeTo) or
+ PyZipFile::isAdditionalTaintStep(nodeFrom, nodeTo) or
TarFile::isAdditionalTaintStep(nodeFrom, nodeTo)
) and
exists(nodeTo.getLocation().getFile().getRelativePath())
From b506b7d2985361791a302fb1c944b2bda206a7a9 Mon Sep 17 00:00:00 2001
From: amammad
Date: Mon, 26 Jun 2023 16:50:33 +1000
Subject: [PATCH 003/378] better documents, remove separate PyZipFile
---
.../Security/CWE-409/DecompressionBombs.ql | 97 +++----------------
1 file changed, 15 insertions(+), 82 deletions(-)
diff --git a/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql b/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql
index 5aa3e90cae3..2e1c87482ca 100644
--- a/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql
+++ b/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql
@@ -4,7 +4,7 @@
* @kind path-problem
* @problem.severity error
* @security-severity 7.8
- * @precision medium
+ * @precision high
* @id py/user-controlled-file-decompression
* @tags security
* experimental
@@ -18,85 +18,13 @@ import semmle.python.ApiGraphs
import semmle.python.dataflow.new.RemoteFlowSources
import semmle.python.dataflow.new.internal.DataFlowPublic
-module PyZipFile {
- /**
- * ```python
- * zipfile.PyZipFile()
- */
- private API::Node pyZipFileClass() {
- result = API::moduleImport("zipfile").getMember("PyZipFile")
- }
-
- /**
- * same as zipfileSinks
- */
- DataFlow::Node isSink() { result = sink(pyZipFileClass()).getACall() }
-
- private API::Node sink(API::Node pyZipFileClass) {
- result = pyZipFileClass.getReturn().getMember(["extractall", "read", "extract", "testzip"])
- or
- result = pyZipFileClass.getReturn().getMember("open") and
- // only read mode is sink
- // mode can be set in open() argument or in PyZipFile instantiation argument
- (
- not exists(
- result
- .getACall()
- .getParameter(1, "mode")
- .getAValueReachingSink()
- .asExpr()
- .(StrConst)
- .getText()
- ) or
- result
- .getACall()
- .getParameter(1, "mode")
- .getAValueReachingSink()
- .asExpr()
- .(StrConst)
- .getText() = "r"
- ) and
- (
- not exists(
- pyZipFileClass
- .getACall()
- .getParameter(1, "mode")
- .getAValueReachingSink()
- .asExpr()
- .(StrConst)
- .getText()
- ) or
- pyZipFileClass
- .getACall()
- .getParameter(1, "mode")
- .getAValueReachingSink()
- .asExpr()
- .(StrConst)
- .getText() = "r"
- )
- }
-
- /**
- * Same as ZipFile
- * I made PyZipFile separated from ZipFile as in future this will be compatible
- * if anyone want to add new methods an sink to each object.
- */
- predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
- exists(API::Node pyZipFileClass | pyZipFileClass = pyZipFileClass() |
- nodeFrom = pyZipFileClass.getACall().getParameter(0, "file").asSink() and
- nodeTo =
- [
- sink(pyZipFileClass).getACall(),
- pyZipFileClass
- .getACall()
- .getReturn()
- .getMember(["extractall", "read", "extract", "testzip"])
- .getACall()
- ]
- )
- }
-}
-
+// /**
+// * Same as ZipFile
+// * I can made PyZipFile separated from ZipFile
+// * as in future this will be more compatible if it has more differences from ZipFile
+// * and we can add new changes easier.
+// */
+// module PyZipFile { }
module Lzma {
private API::Node lzmaClass() {
result = API::moduleImport("lzma").getMember(["LZMAFile", "open"])
@@ -204,13 +132,18 @@ module Gzip {
}
module ZipFile {
- // more sinks file:///home/am/CodeQL-home/codeql-repo/python/ql/src/experimental/semmle/python/security/ZipSlip.qll
/**
* ```python
* zipfile.ZipFile()
* ```
*/
- private API::Node zipFileClass() { result = API::moduleImport("zipfile").getMember("ZipFile") }
+ private API::Node zipFileClass() {
+ result =
+ [
+ API::moduleImport("zipfile").getMember("ZipFile"),
+ API::moduleImport("zipfile").getMember("PyZipFile")
+ ]
+ }
/**
* ```python
From 8fccd65d34a2120769b5e90e83dbcc5dde31483f Mon Sep 17 00:00:00 2001
From: amammad
Date: Mon, 26 Jun 2023 16:51:14 +1000
Subject: [PATCH 004/378] fix a mistake :(
---
.../src/experimental/Security/CWE-409/DecompressionBombs.ql | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql b/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql
index 2e1c87482ca..86fd1a9e74b 100644
--- a/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql
+++ b/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql
@@ -502,8 +502,8 @@ module BombsConfig implements DataFlow::ConfigSig {
predicate isSink(DataFlow::Node sink) {
sink =
[
- PyZipFile::isSink(), ZipFile::isSink(), Gzip::isSink(), Lzma::isSink(), Bz2::isSink(),
- TarFile::isSink(), Shutil::isSink(), Pandas::isSink()
+ ZipFile::isSink(), Gzip::isSink(), Lzma::isSink(), Bz2::isSink(), TarFile::isSink(),
+ Shutil::isSink(), Pandas::isSink()
] and
exists(sink.getLocation().getFile().getRelativePath())
}
@@ -512,7 +512,6 @@ module BombsConfig implements DataFlow::ConfigSig {
(
isAdditionalTaintStepTextIOWrapper(nodeFrom, nodeTo) or
ZipFile::isAdditionalTaintStep(nodeFrom, nodeTo) or
- PyZipFile::isAdditionalTaintStep(nodeFrom, nodeTo) or
TarFile::isAdditionalTaintStep(nodeFrom, nodeTo)
) and
exists(nodeTo.getLocation().getFile().getRelativePath())
From 7aa002fa2a502f2400f9ddb40e5955310046238d Mon Sep 17 00:00:00 2001
From: amammad
Date: Thu, 29 Jun 2023 22:20:46 +1000
Subject: [PATCH 005/378] fix an accident :)
---
.../query-tests/Security/CWE-522/LDAPInsecureAuth.qlref | 1 +
1 file changed, 1 insertion(+)
create mode 100644 python/ql/test/experimental/query-tests/Security/CWE-522/LDAPInsecureAuth.qlref
diff --git a/python/ql/test/experimental/query-tests/Security/CWE-522/LDAPInsecureAuth.qlref b/python/ql/test/experimental/query-tests/Security/CWE-522/LDAPInsecureAuth.qlref
new file mode 100644
index 00000000000..8bb2c1e9b52
--- /dev/null
+++ b/python/ql/test/experimental/query-tests/Security/CWE-522/LDAPInsecureAuth.qlref
@@ -0,0 +1 @@
+experimental/Security/CWE-522/LDAPInsecureAuth.ql
\ No newline at end of file
From bcfc28aae0313ba7af6fbfa3d926112e36421131 Mon Sep 17 00:00:00 2001
From: amammad
Date: Thu, 7 Sep 2023 02:02:32 +1000
Subject: [PATCH 006/378] add sources to detect CVE completely
---
.../Security/CWE-409/DecompressionBombs.ql | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql b/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql
index 86fd1a9e74b..46b32c6880c 100644
--- a/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql
+++ b/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql
@@ -137,7 +137,7 @@ module ZipFile {
* zipfile.ZipFile()
* ```
*/
- private API::Node zipFileClass() {
+ API::Node zipFileClass() {
result =
[
API::moduleImport("zipfile").getMember("ZipFile"),
@@ -253,7 +253,8 @@ module ZipFile {
*/
predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
exists(API::Node zipFileInstance | zipFileInstance = zipFileClass() |
- nodeFrom = zipFileInstance.getACall().getParameter(0, "file").asSink() and
+ nodeFrom =
+ [zipFileInstance.getACall().getParameter(0, "file").asSink(), zipFileInstance.getACall()] and
nodeTo =
[
sink(zipFileInstance).getACall(),
@@ -317,7 +318,8 @@ module TarFile {
predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
exists(API::Node tarfileInstance | tarfileInstance = tarfileInstance() |
- nodeFrom = tarfileInstance.getACall().getParameter(0, "name").asSink() and
+ nodeFrom =
+ [tarfileInstance.getACall().getParameter(0, "name").asSink(), tarfileInstance.getACall()] and
nodeTo =
tarfileInstance.getReturn().getMember(["extractall", "extract", "extractfile"]).getACall()
)
@@ -497,6 +499,10 @@ module BombsConfig implements DataFlow::ConfigSig {
)
or
source instanceof FileAndFormRemoteFlowSource::FastAPI
+ or
+ source = TarFile::tarfileInstance().getACall()
+ or
+ source = ZipFile::zipFileClass().getACall()
}
predicate isSink(DataFlow::Node sink) {
From 6ee58657890e0588a176c2e3fea6449e50242536 Mon Sep 17 00:00:00 2001
From: amammad
Date: Thu, 7 Sep 2023 18:27:40 +1000
Subject: [PATCH 007/378] add sources to detect CVE completely
---
.../experimental/Security/CWE-409/DecompressionBombs.ql | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql b/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql
index 46b32c6880c..fd23eb1f278 100644
--- a/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql
+++ b/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql
@@ -1,11 +1,11 @@
/**
- * @name User-controlled file decompression
- * @description User-controlled data that flows into decompression library APIs without checking the compression rate is dangerous
+ * @name Uncontrolled file decompression
+ * @description Uncontrolled data that flows into decompression library APIs without checking the compression rate is dangerous
* @kind path-problem
* @problem.severity error
* @security-severity 7.8
* @precision high
- * @id py/user-controlled-file-decompression
+ * @id py/uncontrolled-file-decompression
* @tags security
* experimental
* external/cwe/cwe-409
@@ -530,5 +530,4 @@ import Bombs::PathGraph
from Bombs::PathNode source, Bombs::PathNode sink
where Bombs::flowPath(source, sink)
-select sink.getNode(), source, sink, "This file extraction depends on a $@.", source.getNode(),
- "potentially untrusted source"
+select sink.getNode(), source, sink, "This file extraction is $@.", source.getNode(), "uncontrolled"
From 3175db226e21a9b5217621887479f7f632281135 Mon Sep 17 00:00:00 2001
From: amammad <77095239+amammad@users.noreply.github.com>
Date: Mon, 9 Oct 2023 20:51:19 +0200
Subject: [PATCH 008/378] upgrade fastAPI remote sources
---
.../Security/CWE-409/DecompressionBombs.ql | 155 ++++++------------
1 file changed, 54 insertions(+), 101 deletions(-)
diff --git a/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql b/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql
index fd23eb1f278..6d0258f5b65 100644
--- a/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql
+++ b/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql
@@ -376,9 +376,9 @@ module Pandas {
}
module FileAndFormRemoteFlowSource {
- class FastAPI extends DataFlow::Node {
+ class FastAPI extends RemoteFlowSource::Range {
FastAPI() {
- exists(API::Node fastApiParam |
+ exists(API::Node fastApiParam, Expr fastApiUploadFile |
fastApiParam =
API::moduleImport("fastapi")
.getMember("FastAPI")
@@ -387,75 +387,72 @@ module FileAndFormRemoteFlowSource {
.getReturn()
.getParameter(0)
.getKeywordParameter(_) and
- API::moduleImport("fastapi")
- .getMember("UploadFile")
- .getASubclass*()
- .getAValueReachableFromSource()
- .asExpr() =
- fastApiParam.asSource().asExpr().(Parameter).getAnnotation().getASubExpression*()
+ fastApiUploadFile =
+ API::moduleImport("fastapi")
+ .getMember("UploadFile")
+ .getASubclass*()
+ .getAValueReachableFromSource()
+ .asExpr()
|
- // in the case of List of files
+ fastApiUploadFile =
+ fastApiParam.asSource().asExpr().(Parameter).getAnnotation().getASubExpression*() and
+ // Multiple Uploaded files as list of fastapi.UploadFile
exists(For f, Attribute attr, DataFlow::Node a, DataFlow::Node b |
fastApiParam.getAValueReachableFromSource().asExpr() = f.getIter().getASubExpression*()
|
- // file.file in following
- // def upload(files: List[UploadFile] = File(...)):
- // for file in files:
- // **file.file**
- // thanks Arthur Baars for helping me in following
- TaintTracking::localTaint(a, b) and
- a.asExpr() = f.getIter() and
- b.asExpr() = attr.getObject() and
+ TaintTracking::localExprTaint(f.getIter(), attr.getObject()) and
attr.getName() = ["filename", "content_type", "headers", "file", "read"] and
this.asExpr() = attr
)
or
+ // one Uploaded file as fastapi.UploadFile
this =
[
- fastApiParam.asSource(),
- fastApiParam.getMember(["filename", "content_type", "headers", "file"]).asSource(),
- fastApiParam.getMember("read").getReturn().asSource(),
- // file-like object, I'm trying to not do additional work here by using already existing file-like objs if it is possible
- // fastApiParam.getMember("file").getAMember().asSource(),
+ fastApiParam.getMember(["filename", "content_type", "headers"]).asSource(),
+ fastApiParam
+ .getMember("file")
+ .getMember(["readlines", "readline", "read"])
+ .getReturn()
+ .asSource(), fastApiParam.getMember("read").getReturn().asSource()
]
- )
- or
- exists(API::Node fastApiParam |
- fastApiParam =
- API::moduleImport("fastapi")
- .getMember("FastAPI")
- .getReturn()
- .getMember("post")
- .getReturn()
- .getParameter(0)
- .getKeywordParameter(_) and
- API::moduleImport("fastapi")
- .getMember("File")
- .getASubclass*()
- .getAValueReachableFromSource()
- .asExpr() =
- fastApiParam.asSource().asExpr().(Parameter).getAnnotation().getASubExpression*()
- |
- // in the case of List of files
- exists(For f, Attribute attr, DataFlow::Node a, DataFlow::Node b |
- fastApiParam.getAValueReachableFromSource().asExpr() = f.getIter().getASubExpression*()
- |
- // file.file in following
- // def upload(files: List[UploadFile] = File(...)):
- // for file in files:
- // **file.file**
- // thanks Arthur Baars for helping me in following
- TaintTracking::localTaint(a, b) and
- a.asExpr() = f.getIter() and
- b.asExpr() = attr.getObject() and
- attr.getName() = "file" and
- this.asExpr() = attr
- )
- or
- this = fastApiParam.asSource()
) and
exists(this.getLocation().getFile().getRelativePath())
}
+
+ override string getSourceType() { result = "HTTP FORM" }
+ }
+}
+
+module BombsConfig implements DataFlow::ConfigSig {
+ predicate isSource(DataFlow::Node source) {
+ source instanceof RemoteFlowSource and
+ // or
+ // source instanceof FileAndFormRemoteFlowSource::FastAPI
+ exists(source.getLocation().getFile().getRelativePath()) and
+ not source.getLocation().getFile().getRelativePath().matches("venv")
+ }
+
+ predicate isSink(DataFlow::Node sink) {
+ (
+ sink =
+ [
+ ZipFile::isSink(), Gzip::isSink(), Lzma::isSink(), Bz2::isSink(), TarFile::isSink(),
+ Shutil::isSink(), Pandas::isSink()
+ ] or
+ any()
+ ) and
+ exists(sink.getLocation().getFile().getRelativePath()) and
+ not sink.getLocation().getFile().getRelativePath().matches("venv")
+ }
+
+ predicate isAdditionalFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
+ (
+ isAdditionalTaintStepTextIOWrapper(nodeFrom, nodeTo) or
+ ZipFile::isAdditionalTaintStep(nodeFrom, nodeTo) or
+ TarFile::isAdditionalTaintStep(nodeFrom, nodeTo)
+ ) and
+ exists(nodeTo.getLocation().getFile().getRelativePath()) and
+ not nodeTo.getLocation().getFile().getRelativePath().matches("venv")
}
}
@@ -480,50 +477,6 @@ predicate isAdditionalTaintStepTextIOWrapper(DataFlow::Node nodeFrom, DataFlow::
exists(nodeTo.getLocation().getFile().getRelativePath())
}
-module BombsConfig implements DataFlow::ConfigSig {
- // borrowed from UnsafeUnpackQuery.qll
- predicate isSource(DataFlow::Node source) {
- source instanceof RemoteFlowSource
- or
- exists(MethodCallNode args |
- args = source.(AttrRead).getObject().getALocalSource() and
- args =
- [
- API::moduleImport("argparse")
- .getMember("ArgumentParser")
- .getReturn()
- .getMember("parse_args")
- .getACall(), API::moduleImport("os").getMember("getenv").getACall(),
- API::moduleImport("os").getMember("environ").getMember("get").getACall()
- ]
- )
- or
- source instanceof FileAndFormRemoteFlowSource::FastAPI
- or
- source = TarFile::tarfileInstance().getACall()
- or
- source = ZipFile::zipFileClass().getACall()
- }
-
- predicate isSink(DataFlow::Node sink) {
- sink =
- [
- ZipFile::isSink(), Gzip::isSink(), Lzma::isSink(), Bz2::isSink(), TarFile::isSink(),
- Shutil::isSink(), Pandas::isSink()
- ] and
- exists(sink.getLocation().getFile().getRelativePath())
- }
-
- predicate isAdditionalFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
- (
- isAdditionalTaintStepTextIOWrapper(nodeFrom, nodeTo) or
- ZipFile::isAdditionalTaintStep(nodeFrom, nodeTo) or
- TarFile::isAdditionalTaintStep(nodeFrom, nodeTo)
- ) and
- exists(nodeTo.getLocation().getFile().getRelativePath())
- }
-}
-
module Bombs = TaintTracking::Global;
import Bombs::PathGraph
From 1318afdb279c983ddf47e45ef605946a0e006fa2 Mon Sep 17 00:00:00 2001
From: amammad <77095239+amammad@users.noreply.github.com>
Date: Mon, 9 Oct 2023 23:07:52 +0200
Subject: [PATCH 009/378] modularize
---
.../Security/CWE-409/DecompressionBomb.qll | 430 ++++++++++++++++++
.../Security/CWE-409/DecompressionBombs.ql | 423 ++---------------
.../Security/CWE-409/example_bad.py | 2 +-
3 files changed, 461 insertions(+), 394 deletions(-)
create mode 100644 python/ql/src/experimental/Security/CWE-409/DecompressionBomb.qll
diff --git a/python/ql/src/experimental/Security/CWE-409/DecompressionBomb.qll b/python/ql/src/experimental/Security/CWE-409/DecompressionBomb.qll
new file mode 100644
index 00000000000..6659cb189b6
--- /dev/null
+++ b/python/ql/src/experimental/Security/CWE-409/DecompressionBomb.qll
@@ -0,0 +1,430 @@
+import python
+import semmle.python.dataflow.new.DataFlow
+import semmle.python.dataflow.new.TaintTracking
+import semmle.python.ApiGraphs
+import semmle.python.dataflow.new.RemoteFlowSources
+import semmle.python.dataflow.new.internal.DataFlowPublic
+
+module DecompressionBomb {
+ /**
+ * The Sinks of uncontrolled data decompression, use this class in your queries
+ */
+ class Sink extends DataFlow::Node {
+ Sink() { this = any(Range r).sink() }
+ }
+
+ /**
+ * The additional taint steps that need for creating taint tracking or dataflow.
+ */
+ abstract class AdditionalTaintStep extends string {
+ AdditionalTaintStep() { this = "AdditionalTaintStep" }
+
+ /**
+ * Holds if there is a additional taint step between pred and succ.
+ */
+ abstract predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ);
+ }
+
+ /**
+ * A abstract class responsible for extending new decompression sinks
+ */
+ abstract class Range extends API::Node {
+ /**
+ * Gets the sink of responsible for decompression node
+ *
+ * it can be a path, stream of compressed data,
+ * or a call to function that use pipe
+ */
+ abstract DataFlow::Node sink();
+ }
+}
+
+module ZipFile {
+ /**
+ * A `zipfile` Instance
+ *
+ * ```python
+ * zipfile.ZipFile()
+ * ```
+ */
+ API::Node zipFileClass() {
+ result =
+ [
+ API::moduleImport("zipfile").getMember("ZipFile"),
+ API::moduleImport("zipfile").getMember("PyZipFile")
+ ]
+ }
+
+ /**
+ * The Decompression Sinks of `zipfile` library
+ *
+ * ```python
+ * myzip = zipfile.ZipFile("zipfileName.zip")
+ * myzip.open('eggs.txt',"r").read()
+ * myzip.extractall()
+ * ```
+ */
+ class DecompressionSink extends DecompressionBomb::Range {
+ override string toString() { result = "DecompressionSink" }
+
+ DecompressionSink() { this = zipFileClass() }
+
+ /**
+ * An function call of tarfile for extracting compressed data
+ *
+ * `tarfile.open(filepath).extractall()` or `tarfile.open(filepath).extract()`or `tarfile.open(filepath).extractfile()`
+ * or `tarfile.Tarfile.xzopen()` or `tarfile.Tarfile.gzopen()` or `tarfile.Tarfile.bz2open()`
+ */
+ override DataFlow::Node sink() {
+ (
+ result = this.getReturn().getMember(["extractall", "read", "extract", "testzip"]).getACall()
+ or
+ exists(API::Node openInstance |
+ openInstance = this.getReturn().getMember("open") and
+ (
+ not exists(
+ openInstance
+ .getACall()
+ .getParameter(1, "mode")
+ .getAValueReachingSink()
+ .asExpr()
+ .(StrConst)
+ .getText()
+ ) or
+ openInstance
+ .getACall()
+ .getParameter(1, "mode")
+ .getAValueReachingSink()
+ .asExpr()
+ .(StrConst)
+ .getText() = "r"
+ ) and
+ (
+ not exists(
+ this.getACall()
+ .getParameter(1, "mode")
+ .getAValueReachingSink()
+ .asExpr()
+ .(StrConst)
+ .getText()
+ ) or
+ this.getACall()
+ .getParameter(1, "mode")
+ .getAValueReachingSink()
+ .asExpr()
+ .(StrConst)
+ .getText() = "r"
+ ) and
+ not zipFileDecompressionBombSanitizer(this) and
+ result = openInstance.getACall()
+ )
+ )
+ }
+ }
+
+ /**
+ * Gets a `zipfile.ZipFile` and checks if there is a size controlled read or not
+ * ```python
+ * with zipfile.ZipFile(zipFileName) as myzip:
+ * with myzip.open(fileinfo.filename, mode="r") as myfile:
+ * while chunk:
+ * chunk = myfile.read(buffer_size)
+ * total_size += buffer_size
+ * if total_size > SIZE_THRESHOLD:
+ * ...
+ * ```
+ */
+ predicate zipFileDecompressionBombSanitizer(API::Node n) {
+ TaintTracking::localExprTaint(n.getReturn().getMember("read").getParameter(0).asSink().asExpr(),
+ any(Compare i).getASubExpression*())
+ }
+
+ /**
+ * The Additional taint steps that are necessary for data flow query
+ *
+ * ```python
+ * nodeFrom = "zipFileName.zip"
+ * myZip = zipfile.ZipFile(nodeFrom)
+ * nodeTo = myZip.open('eggs.txt',"r")
+ * nodeTo = myZip.extractall()
+ * nodeTo = myZip.read()
+ * nodeTo = myZip.extract()
+ * # testzip not a RAM consumer but it uses as much CPU as possible
+ * nodeTo = myZip.testzip()
+ * ```
+ */
+ class DecompressionAdditionalTaintStep extends DecompressionBomb::AdditionalTaintStep {
+ DecompressionAdditionalTaintStep() { this = "AdditionalTaintStep" }
+
+ override predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
+ exists(DecompressionSink zipFileInstance |
+ nodeFrom =
+ [zipFileInstance.getACall().getParameter(0, "file").asSink(), zipFileInstance.getACall()] and
+ nodeTo =
+ [
+ zipFileInstance.sink(),
+ zipFileInstance
+ .getACall()
+ .getReturn()
+ .getMember(["extractall", "read", "extract", "testzip"])
+ .getACall()
+ ]
+ )
+ }
+ }
+}
+
+/**
+ * Provides Sinks and additional taint steps related to `tarfile` library
+ */
+module TarFile {
+ /**
+ * The Decompression Sinks of `tarfile` library
+ */
+ class DecompressionSink extends DecompressionBomb::Range {
+ override string toString() { result = "DecompressionSink" }
+
+ DecompressionSink() { this = tarfileExtractMember() }
+
+ /**
+ * An function call of tarfile for extracting compressed data
+ * `tarfile.open(filepath).extractall()` or `tarfile.open(filepath).extract()`or `tarfile.open(filepath).extractfile()`
+ * or `tarfile.Tarfile.xzopen()` or `tarfile.Tarfile.gzopen()` or `tarfile.Tarfile.bz2open()`
+ */
+ override DataFlow::Node sink() {
+ result = this.getReturn().getMember(["extractall", "extract", "extractfile"]).getACall()
+ }
+ }
+
+ /**
+ * A tarfile instance for extracting compressed data
+ */
+ API::Node tarfileExtractMember() {
+ result =
+ [
+ API::moduleImport("tarfile").getMember("open"),
+ API::moduleImport("tarfile")
+ .getMember("TarFile")
+ .getMember(["xzopen", "gzopen", "bz2open", "open"])
+ ] and
+ (
+ not exists(
+ result
+ .getACall()
+ .getParameter(1, "mode")
+ .getAValueReachingSink()
+ .asExpr()
+ .(StrConst)
+ .getText()
+ ) or
+ not result
+ .getACall()
+ .getParameter(1, "mode")
+ .getAValueReachingSink()
+ .asExpr()
+ .(StrConst)
+ .getText()
+ .matches("r:%")
+ )
+ }
+
+ /**
+ * The Additional taint steps that are necessary for data flow query
+ */
+ class DecompressionAdditionalTaintStep extends DecompressionBomb::AdditionalTaintStep {
+ DecompressionAdditionalTaintStep() { this = "AdditionalTaintStep" }
+
+ override predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
+ exists(API::Node tarfileInstance | tarfileInstance = tarfileExtractMember() |
+ nodeFrom = tarfileInstance.getACall().getParameter(0, "name").asSink() and
+ nodeTo =
+ tarfileInstance.getReturn().getMember(["extractall", "extract", "extractfile"]).getACall()
+ )
+ }
+ }
+}
+
+/**
+ * Provides Sinks and additional taint steps related to `pandas` library
+ */
+module Pandas {
+ /**
+ * The Decompression Sinks of `pandas` library
+ */
+ class DecompressionSink extends DecompressionBomb::Range {
+ override string toString() { result = "DecompressionSink" }
+
+ DecompressionSink() { this = API::moduleImport("pandas") }
+
+ override DataFlow::Node sink() {
+ exists(API::CallNode calltoPandasMethods |
+ (
+ calltoPandasMethods =
+ this.getMember([
+ "read_csv", "read_json", "read_sas", "read_stata", "read_table", "read_xml"
+ ]).getACall() and
+ result = calltoPandasMethods.getArg(0)
+ or
+ calltoPandasMethods =
+ this.getMember(["read_csv", "read_sas", "read_stata", "read_table"]).getACall() and
+ result = calltoPandasMethods.getArgByName("filepath_or_buffer")
+ or
+ calltoPandasMethods = this.getMember("read_json").getACall() and
+ result = calltoPandasMethods.getArgByName("path_or_buf")
+ or
+ calltoPandasMethods = this.getMember("read_xml").getACall() and
+ result = calltoPandasMethods.getArgByName("path_or_buffer")
+ ) and
+ (
+ not exists(calltoPandasMethods.getArgByName("compression"))
+ or
+ not calltoPandasMethods
+ .getKeywordParameter("compression")
+ .getAValueReachingSink()
+ .asExpr()
+ .(StrConst)
+ .getText() = "tar"
+ )
+ )
+ }
+ }
+}
+
+/**
+ * Provides Sinks and additional taint steps related to `shutil` library
+ */
+module Shutil {
+ /**
+ * The Decompression Sinks of `shutil` library
+ */
+ class DecompressionSink extends DecompressionBomb::Range {
+ override string toString() { result = "DecompressionSink" }
+
+ DecompressionSink() { this = API::moduleImport("shutil").getMember("unpack_archive") }
+
+ override DataFlow::Node sink() { result = this.getACall().getParameter(0, "filename").asSink() }
+ }
+}
+
+/**
+ * Provides Sinks and additional taint steps related to `gzip` library
+ */
+module Gzip {
+ private API::Node gzipInstance() {
+ result = API::moduleImport("gzip").getMember(["GzipFile", "open"])
+ }
+
+ /**
+ * The Decompression Sinks of `gzip` library
+ *
+ * `gzip.open(sink)`
+ * `gzip.GzipFile(sink)`
+ *
+ * only read mode is sink
+ */
+ class DecompressionSink extends DecompressionBomb::Range {
+ override string toString() { result = "DecompressionSink" }
+
+ DecompressionSink() { this = gzipInstance() }
+
+ override DataFlow::Node sink() {
+ exists(API::CallNode gzipCall | gzipCall = this.getACall() |
+ result = gzipCall.getParameter(0, "filename").asSink() and
+ (
+ not exists(
+ gzipCall.getParameter(1, "mode").getAValueReachingSink().asExpr().(StrConst).getText()
+ ) or
+ gzipCall
+ .getParameter(1, "mode")
+ .getAValueReachingSink()
+ .asExpr()
+ .(StrConst)
+ .getText()
+ .matches("%r%")
+ )
+ )
+ }
+ }
+}
+
+/**
+ * Provides Sinks and additional taint steps related to `bz2` library
+ */
+module Bz2 {
+ private API::Node bz2Instance() {
+ result = API::moduleImport("bz2").getMember(["BZ2File", "open"])
+ }
+
+ /**
+ * The Decompression Sinks of `bz2` library
+ *
+ * `bz2.open(sink)`
+ * `bz2.BZ2File(sink)`
+ *
+ * only read mode is sink
+ */
+ class DecompressionSink extends DecompressionBomb::Range {
+ override string toString() { result = "DecompressionSink" }
+
+ DecompressionSink() { this = bz2Instance() }
+
+ override DataFlow::Node sink() {
+ exists(API::CallNode bz2Call | bz2Call = this.getACall() |
+ result = bz2Call.getParameter(0, "filename").asSink() and
+ (
+ not exists(
+ bz2Call.getParameter(1, "mode").getAValueReachingSink().asExpr().(StrConst).getText()
+ ) or
+ bz2Call
+ .getParameter(1, "mode")
+ .getAValueReachingSink()
+ .asExpr()
+ .(StrConst)
+ .getText()
+ .matches("%r%")
+ )
+ )
+ }
+ }
+}
+
+/**
+ * Provides Sinks and additional taint steps related to `lzma` library
+ */
+module Lzma {
+ private API::Node lzmaInstance() {
+ result = API::moduleImport("lzma").getMember(["LZMAFile", "open"])
+ }
+
+ /**
+ * The Decompression Sinks of `bz2` library
+ *
+ * `lzma.open(sink)`
+ * `lzma.LZMAFile(sink)`
+ *
+ * only read mode is sink
+ */
+ class DecompressionSink extends DecompressionBomb::Range {
+ override string toString() { result = "DecompressionSink" }
+
+ DecompressionSink() { this = lzmaInstance() }
+
+ override DataFlow::Node sink() {
+ exists(API::CallNode lzmaCall | lzmaCall = this.getACall() |
+ result = lzmaCall.getParameter(0, "filename").asSink() and
+ (
+ not exists(
+ lzmaCall.getParameter(1, "mode").getAValueReachingSink().asExpr().(StrConst).getText()
+ ) or
+ lzmaCall
+ .getParameter(1, "mode")
+ .getAValueReachingSink()
+ .asExpr()
+ .(StrConst)
+ .getText()
+ .matches("%r%")
+ )
+ )
+ }
+ }
+}
diff --git a/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql b/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql
index 6d0258f5b65..c568e783305 100644
--- a/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql
+++ b/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql
@@ -17,362 +17,27 @@ import semmle.python.dataflow.new.TaintTracking
import semmle.python.ApiGraphs
import semmle.python.dataflow.new.RemoteFlowSources
import semmle.python.dataflow.new.internal.DataFlowPublic
+import DecompressionBomb
-// /**
-// * Same as ZipFile
-// * I can made PyZipFile separated from ZipFile
-// * as in future this will be more compatible if it has more differences from ZipFile
-// * and we can add new changes easier.
-// */
-// module PyZipFile { }
-module Lzma {
- private API::Node lzmaClass() {
- result = API::moduleImport("lzma").getMember(["LZMAFile", "open"])
- }
-
- /**
- * `lzma.open(sink)`
- * `lzma.LZMAFile(sink)`
- * only read mode is sink
- */
- DataFlow::Node isSink() {
- exists(API::Node lzmaClass | lzmaClass = lzmaClass() |
- result = lzmaClass.getACall().getParameter(0, "filename").asSink() and
- (
- not exists(
- lzmaClass
- .getACall()
- .getParameter(1, "mode")
- .getAValueReachingSink()
- .asExpr()
- .(StrConst)
- .getText()
- ) or
- lzmaClass
- .getACall()
- .getParameter(1, "mode")
- .getAValueReachingSink()
- .asExpr()
- .(StrConst)
- .getText()
- .matches("%r%")
- )
- )
- }
-}
-
-module Bz2 {
- private API::Node bz2Class() { result = API::moduleImport("bz2").getMember(["BZ2File", "open"]) }
-
- /**
- * `bz2.open(sink)`
- * `bz2.BZ2File(sink)`
- * only read mode is sink
- */
- DataFlow::Node isSink() {
- exists(API::Node bz2Class | bz2Class = bz2Class() |
- result = bz2Class.getACall().getParameter(0, "filename").asSink() and
- (
- not exists(
- bz2Class
- .getACall()
- .getParameter(1, "mode")
- .getAValueReachingSink()
- .asExpr()
- .(StrConst)
- .getText()
- ) or
- bz2Class
- .getACall()
- .getParameter(1, "mode")
- .getAValueReachingSink()
- .asExpr()
- .(StrConst)
- .getText()
- .matches("%r%")
- )
- )
- }
-}
-
-module Gzip {
- private API::Node gzipClass() {
- result = API::moduleImport("gzip").getMember(["GzipFile", "open"])
- }
-
- /**
- * `gzip.open(sink)`
- * `gzip.GzipFile(sink)`
- * only read mode is sink
- */
- DataFlow::Node isSink() {
- exists(API::Node gzipClass | gzipClass = gzipClass() |
- result = gzipClass.getACall().getParameter(0, "filename").asSink() and
- (
- not exists(
- gzipClass
- .getACall()
- .getParameter(1, "mode")
- .getAValueReachingSink()
- .asExpr()
- .(StrConst)
- .getText()
- ) or
- gzipClass
- .getACall()
- .getParameter(1, "mode")
- .getAValueReachingSink()
- .asExpr()
- .(StrConst)
- .getText()
- .matches("%r%")
- )
- )
- }
-}
-
-module ZipFile {
- /**
- * ```python
- * zipfile.ZipFile()
- * ```
- */
- API::Node zipFileClass() {
- result =
- [
- API::moduleImport("zipfile").getMember("ZipFile"),
- API::moduleImport("zipfile").getMember("PyZipFile")
- ]
- }
-
- /**
- * ```python
- * zipfile.ZipFile("zipfileName.zip")
- * # read() or one of ["read", "readline", "readlines", "seek", "tell", "__iter__", "__next__"]
- * myzip.open('eggs.txt',"r").read()
- * # I decided to choice open method with "r" mode as sink
- * # because opening zipfile with "r" mode mostly is for reading content of that file
- * # so we have a very few of FP here
- * next(myzip.open('eggs.txt'))
- * myzip.extractall()
- * myzip.read()
- * myzip.extract()
- * # testzip not a RAM consumer but it uses as much CPU as possible
- * myzip.testzip()
- *
- * ```
- */
- private API::Node sink(API::Node zipFileInstance) {
- // we can go forward one more step and check whether we call the required methods for read
- // or just opening zipfile for reading is enough ( mode = "r")
- // result =
- // zipfileReturnIOFile()
- // .getReturn()
- // .getMember(["read", "readline", "readlines", "seek", "tell", "__iter__", "__next__"])
- // or
- (
- result = zipFileInstance.getReturn().getMember(["extractall", "read", "extract", "testzip"])
- or
- result = zipFileInstance.getReturn().getMember("open") and
- (
- not exists(
- result
- .getACall()
- .getParameter(1, "mode")
- .getAValueReachingSink()
- .asExpr()
- .(StrConst)
- .getText()
- ) or
- result
- .getACall()
- .getParameter(1, "mode")
- .getAValueReachingSink()
- .asExpr()
- .(StrConst)
- .getText() = "r"
- ) and
- (
- not exists(
- zipFileInstance
- .getACall()
- .getParameter(1, "mode")
- .getAValueReachingSink()
- .asExpr()
- .(StrConst)
- .getText()
- ) or
- zipFileInstance
- .getACall()
- .getParameter(1, "mode")
- .getAValueReachingSink()
- .asExpr()
- .(StrConst)
- .getText() = "r"
- ) and
- zipFileSanitizer(result)
- ) and
- exists(result.getACall().getLocation().getFile().getRelativePath())
- }
-
- /**
- * a sanitizers which check if there is a managed read
- * ```python
- * with zipfile.ZipFile(zipFileName) as myzip:
- * with myzip.open(fileinfo.filename, mode="r") as myfile:
- * while chunk:
- * chunk = myfile.read(buffer_size)
- * total_size += buffer_size
- * if total_size > SIZE_THRESHOLD:
- * ...
- * ```
- */
- predicate zipFileSanitizer(API::Node n) {
- not TaintTracking::localExprTaint(n.getReturn()
- .getMember("read")
- .getParameter(0)
- .asSink()
- .asExpr(), any(Compare i).getASubExpression*())
- }
-
- DataFlow::Node isSink() { result = sink(zipFileClass()).getACall() }
-
- /**
- * ```python
- * nodeFrom = "zipFileName.zip"
- * myZip = zipfile.ZipFile(nodeFrom)
- * nodeTo2 = myZip.open('eggs.txt',"r")
- *
- * nodeTo = myZip.extractall()
- * nodeTo = myZip.read()
- * nodeTo = myZip.extract()
- * # testzip not a RAM consumer but it uses as much CPU as possible
- * nodeTo = myZip.testzip()
- *
- * ```
- */
- predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
- exists(API::Node zipFileInstance | zipFileInstance = zipFileClass() |
- nodeFrom =
- [zipFileInstance.getACall().getParameter(0, "file").asSink(), zipFileInstance.getACall()] and
- nodeTo =
- [
- sink(zipFileInstance).getACall(),
- zipFileInstance
- .getACall()
- .getReturn()
- .getMember(["extractall", "read", "extract", "testzip"])
- .getACall()
- ]
- ) and
- exists(nodeTo.getLocation().getFile().getRelativePath())
- }
-}
-
-module TarFile {
- /**
- * tarfile.open
- *
- * tarfile.Tarfile.open/xzopen/gzopen/bz2open
- * and not mode="r:" which means no compression accepted
- */
- API::Node tarfileInstance() {
- result =
- [
- API::moduleImport("tarfile").getMember("open"),
- API::moduleImport("tarfile")
- .getMember("TarFile")
- .getMember(["xzopen", "gzopen", "bz2open", "open"])
- ] and
- (
- not exists(
- result
- .getACall()
- .getParameter(1, "mode")
- .getAValueReachingSink()
- .asExpr()
- .(StrConst)
- .getText()
- ) or
- not result
- .getACall()
- .getParameter(1, "mode")
- .getAValueReachingSink()
- .asExpr()
- .(StrConst)
- .getText()
- .matches("r:%")
- )
- }
-
- /**
- * a Call of
- * `tarfile.open(filepath).extractall()/extract()/extractfile()`
- * or
- * `tarfile.Tarfile.xzopen()/gzopen()/bz2open()`
- */
- DataFlow::Node isSink() {
- result =
- tarfileInstance().getReturn().getMember(["extractall", "extract", "extractfile"]).getACall()
- }
-
- predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
- exists(API::Node tarfileInstance | tarfileInstance = tarfileInstance() |
- nodeFrom =
- [tarfileInstance.getACall().getParameter(0, "name").asSink(), tarfileInstance.getACall()] and
- nodeTo =
- tarfileInstance.getReturn().getMember(["extractall", "extract", "extractfile"]).getACall()
- )
- }
-}
-
-module Shutil {
- DataFlow::Node isSink() {
- result =
- API::moduleImport("shutil")
- .getMember("unpack_archive")
- .getACall()
- .getParameter(0, "filename")
- .asSink()
- }
-}
-
-module Pandas {
- DataFlow::Node isSink() {
- exists(API::CallNode calltoPandasMethods |
- (
- calltoPandasMethods =
- API::moduleImport("pandas")
- .getMember([
- "read_csv", "read_json", "read_sas", "read_stata", "read_table", "read_xml"
- ])
- .getACall() and
- result = calltoPandasMethods.getArg(0)
- or
- calltoPandasMethods =
- API::moduleImport("pandas")
- .getMember(["read_csv", "read_sas", "read_stata", "read_table"])
- .getACall() and
- result = calltoPandasMethods.getArgByName("filepath_or_buffer")
- or
- calltoPandasMethods = API::moduleImport("pandas").getMember("read_json").getACall() and
- result = calltoPandasMethods.getArgByName("path_or_buf")
- or
- calltoPandasMethods = API::moduleImport("pandas").getMember("read_xml").getACall() and
- result = calltoPandasMethods.getArgByName("path_or_buffer")
- ) and
- (
- not exists(calltoPandasMethods.getArgByName("compression"))
- or
- not calltoPandasMethods
- .getKeywordParameter("compression")
- .getAValueReachingSink()
- .asExpr()
- .(StrConst)
- .getText() = "tar"
- )
- )
- }
+/**
+ * `io.TextIOWrapper(ip, encoding='utf-8')` like following:
+ * ```python
+ * with gzip.open(bomb_input, 'rb') as ip:
+ * with io.TextIOWrapper(ip, encoding='utf-8') as decoder:
+ * content = decoder.read()
+ * print(content)
+ * ```
+ * I saw this builtin method many places so I added it as a AdditionalTaintStep.
+ * it would be nice if it is added as a global AdditionalTaintStep
+ */
+predicate isAdditionalTaintStepTextIOWrapper(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
+ exists(API::CallNode textIOWrapper |
+ textIOWrapper = API::moduleImport("io").getMember("TextIOWrapper").getACall()
+ |
+ nodeFrom = textIOWrapper.getParameter(0, "input").asSink() and
+ nodeTo = textIOWrapper
+ ) and
+ exists(nodeTo.getLocation().getFile().getRelativePath())
}
module FileAndFormRemoteFlowSource {
@@ -429,58 +94,30 @@ module BombsConfig implements DataFlow::ConfigSig {
// or
// source instanceof FileAndFormRemoteFlowSource::FastAPI
exists(source.getLocation().getFile().getRelativePath()) and
- not source.getLocation().getFile().getRelativePath().matches("venv")
+ not source.getLocation().getFile().getRelativePath().matches("%venv%")
}
predicate isSink(DataFlow::Node sink) {
- (
- sink =
- [
- ZipFile::isSink(), Gzip::isSink(), Lzma::isSink(), Bz2::isSink(), TarFile::isSink(),
- Shutil::isSink(), Pandas::isSink()
- ] or
- any()
- ) and
+ sink instanceof DecompressionBomb::Sink and
exists(sink.getLocation().getFile().getRelativePath()) and
- not sink.getLocation().getFile().getRelativePath().matches("venv")
+ not sink.getLocation().getFile().getRelativePath().matches("%venv%")
}
- predicate isAdditionalFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
+ predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) {
(
- isAdditionalTaintStepTextIOWrapper(nodeFrom, nodeTo) or
- ZipFile::isAdditionalTaintStep(nodeFrom, nodeTo) or
- TarFile::isAdditionalTaintStep(nodeFrom, nodeTo)
+ any(DecompressionBomb::AdditionalTaintStep a).isAdditionalTaintStep(pred, succ) or
+ isAdditionalTaintStepTextIOWrapper(pred, succ)
) and
- exists(nodeTo.getLocation().getFile().getRelativePath()) and
- not nodeTo.getLocation().getFile().getRelativePath().matches("venv")
+ not succ.getLocation().getFile().inStdlib() and
+ not succ.getLocation().getFile().getRelativePath().matches("%venv%")
}
}
-/**
- * `io.TextIOWrapper(ip, encoding='utf-8')` like following:
- * ```python
- * with gzip.open(bomb_input, 'rb') as ip:
- * with io.TextIOWrapper(ip, encoding='utf-8') as decoder:
- * content = decoder.read()
- * print(content)
- * ```
- * I saw this builtin method many places so I added it as a AdditionalTaintStep.
- * it would be nice if it is added as a global AdditionalTaintStep
- */
-predicate isAdditionalTaintStepTextIOWrapper(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
- exists(API::CallNode textIOWrapper |
- textIOWrapper = API::moduleImport("io").getMember("TextIOWrapper").getACall()
- |
- nodeFrom = textIOWrapper.getParameter(0, "input").asSink() and
- nodeTo = textIOWrapper
- ) and
- exists(nodeTo.getLocation().getFile().getRelativePath())
-}
-
module Bombs = TaintTracking::Global;
import Bombs::PathGraph
from Bombs::PathNode source, Bombs::PathNode sink
where Bombs::flowPath(source, sink)
-select sink.getNode(), source, sink, "This file extraction is $@.", source.getNode(), "uncontrolled"
+select sink.getNode(), source, sink, "This uncontrolled file extraction is $@.", source.getNode(),
+ "depends on this user controlled data"
diff --git a/python/ql/src/experimental/Security/CWE-409/example_bad.py b/python/ql/src/experimental/Security/CWE-409/example_bad.py
index 32442366458..ae636511db5 100644
--- a/python/ql/src/experimental/Security/CWE-409/example_bad.py
+++ b/python/ql/src/experimental/Security/CWE-409/example_bad.py
@@ -2,4 +2,4 @@ import zipfile
def Bad(zip_path):
- zipfile.ZipFile(zip_path, "r").extract("filename", "./tmp/")
+ zipfile.ZipFile(zip_path, "r").extractall()
From 9d86e7946c7bf8bb2f1916510003fd167833d8d8 Mon Sep 17 00:00:00 2001
From: amammad <77095239+amammad@users.noreply.github.com>
Date: Mon, 9 Oct 2023 23:10:30 +0200
Subject: [PATCH 010/378] move library file to experimental lib directory
---
.../Security/CWE-409/DecompressionBombs.ql | 14 ++++++++------
.../python/security}/DecompressionBomb.qll | 0
2 files changed, 8 insertions(+), 6 deletions(-)
rename python/ql/src/experimental/{Security/CWE-409 => semmle/python/security}/DecompressionBomb.qll (100%)
diff --git a/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql b/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql
index c568e783305..a0332c86d4d 100644
--- a/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql
+++ b/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql
@@ -17,7 +17,7 @@ import semmle.python.dataflow.new.TaintTracking
import semmle.python.ApiGraphs
import semmle.python.dataflow.new.RemoteFlowSources
import semmle.python.dataflow.new.internal.DataFlowPublic
-import DecompressionBomb
+import experimental.semmle.python.security.DecompressionBomb
/**
* `io.TextIOWrapper(ip, encoding='utf-8')` like following:
@@ -90,16 +90,18 @@ module FileAndFormRemoteFlowSource {
module BombsConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
- source instanceof RemoteFlowSource and
- // or
- // source instanceof FileAndFormRemoteFlowSource::FastAPI
- exists(source.getLocation().getFile().getRelativePath()) and
+ (
+ source instanceof RemoteFlowSource
+ or
+ source instanceof FileAndFormRemoteFlowSource::FastAPI
+ ) and
+ not source.getLocation().getFile().inStdlib() and
not source.getLocation().getFile().getRelativePath().matches("%venv%")
}
predicate isSink(DataFlow::Node sink) {
sink instanceof DecompressionBomb::Sink and
- exists(sink.getLocation().getFile().getRelativePath()) and
+ not sink.getLocation().getFile().inStdlib() and
not sink.getLocation().getFile().getRelativePath().matches("%venv%")
}
diff --git a/python/ql/src/experimental/Security/CWE-409/DecompressionBomb.qll b/python/ql/src/experimental/semmle/python/security/DecompressionBomb.qll
similarity index 100%
rename from python/ql/src/experimental/Security/CWE-409/DecompressionBomb.qll
rename to python/ql/src/experimental/semmle/python/security/DecompressionBomb.qll
From 4283bb7d483da1d66cc7a9e8b315b4622985dece Mon Sep 17 00:00:00 2001
From: amammad <77095239+amammad@users.noreply.github.com>
Date: Mon, 9 Oct 2023 23:15:58 +0200
Subject: [PATCH 011/378] clean up unused vars,fix tests
---
.../Security/CWE-409/DecompressionBombs.ql | 10 +-
.../CWE-409/DecompressionBombs.expected | 375 ++++++------------
.../CWE-409/Other_compresstion_modules.py | 54 ---
.../Security/CWE-409/tarfile_module.py | 31 --
.../query-tests/Security/CWE-409/test.py | 76 ++++
.../Security/CWE-409/zipfile_module.py | 128 ------
6 files changed, 201 insertions(+), 473 deletions(-)
delete mode 100644 python/ql/test/experimental/query-tests/Security/CWE-409/Other_compresstion_modules.py
delete mode 100644 python/ql/test/experimental/query-tests/Security/CWE-409/tarfile_module.py
create mode 100644 python/ql/test/experimental/query-tests/Security/CWE-409/test.py
delete mode 100644 python/ql/test/experimental/query-tests/Security/CWE-409/zipfile_module.py
diff --git a/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql b/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql
index a0332c86d4d..8cc1e45f34a 100644
--- a/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql
+++ b/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql
@@ -36,8 +36,7 @@ predicate isAdditionalTaintStepTextIOWrapper(DataFlow::Node nodeFrom, DataFlow::
|
nodeFrom = textIOWrapper.getParameter(0, "input").asSink() and
nodeTo = textIOWrapper
- ) and
- exists(nodeTo.getLocation().getFile().getRelativePath())
+ )
}
module FileAndFormRemoteFlowSource {
@@ -62,7 +61,7 @@ module FileAndFormRemoteFlowSource {
fastApiUploadFile =
fastApiParam.asSource().asExpr().(Parameter).getAnnotation().getASubExpression*() and
// Multiple Uploaded files as list of fastapi.UploadFile
- exists(For f, Attribute attr, DataFlow::Node a, DataFlow::Node b |
+ exists(For f, Attribute attr |
fastApiParam.getAValueReachableFromSource().asExpr() = f.getIter().getASubExpression*()
|
TaintTracking::localExprTaint(f.getIter(), attr.getObject()) and
@@ -80,11 +79,10 @@ module FileAndFormRemoteFlowSource {
.getReturn()
.asSource(), fastApiParam.getMember("read").getReturn().asSource()
]
- ) and
- exists(this.getLocation().getFile().getRelativePath())
+ )
}
- override string getSourceType() { result = "HTTP FORM" }
+ override string getSourceType() { result = "fastapi HTTP FORM files" }
}
}
diff --git a/python/ql/test/experimental/query-tests/Security/CWE-409/DecompressionBombs.expected b/python/ql/test/experimental/query-tests/Security/CWE-409/DecompressionBombs.expected
index 94839132e56..e8e2f0776ee 100644
--- a/python/ql/test/experimental/query-tests/Security/CWE-409/DecompressionBombs.expected
+++ b/python/ql/test/experimental/query-tests/Security/CWE-409/DecompressionBombs.expected
@@ -1,258 +1,125 @@
edges
-| Other_compresstion_modules.py:7:10:7:19 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:9:27:9:36 | ControlFlowNode for bomb_input |
-| Other_compresstion_modules.py:7:10:7:19 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:11:15:11:24 | ControlFlowNode for bomb_input |
-| Other_compresstion_modules.py:7:10:7:19 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:12:19:12:28 | ControlFlowNode for bomb_input |
-| Other_compresstion_modules.py:7:10:7:19 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:14:15:14:24 | ControlFlowNode for bomb_input |
-| Other_compresstion_modules.py:7:10:7:19 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:15:19:15:28 | ControlFlowNode for bomb_input |
-| Other_compresstion_modules.py:7:10:7:19 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:17:20:17:29 | ControlFlowNode for bomb_input |
-| Other_compresstion_modules.py:7:10:7:19 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:23:14:23:23 | ControlFlowNode for bomb_input |
-| Other_compresstion_modules.py:7:10:7:19 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:24:17:24:26 | ControlFlowNode for bomb_input |
-| Other_compresstion_modules.py:7:10:7:19 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:27:40:27:49 | ControlFlowNode for bomb_input |
-| Other_compresstion_modules.py:7:10:7:19 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:29:23:29:32 | ControlFlowNode for bomb_input |
-| Other_compresstion_modules.py:7:10:7:19 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:30:21:30:30 | ControlFlowNode for bomb_input |
-| Other_compresstion_modules.py:7:10:7:19 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:32:40:32:49 | ControlFlowNode for bomb_input |
-| Other_compresstion_modules.py:7:10:7:19 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:33:22:33:31 | ControlFlowNode for bomb_input |
-| Other_compresstion_modules.py:7:10:7:19 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:34:21:34:30 | ControlFlowNode for bomb_input |
-| Other_compresstion_modules.py:7:10:7:19 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:35:42:35:51 | ControlFlowNode for bomb_input |
-| Other_compresstion_modules.py:7:10:7:19 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:36:23:36:32 | ControlFlowNode for bomb_input |
-| Other_compresstion_modules.py:7:10:7:19 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:37:36:37:45 | ControlFlowNode for bomb_input |
-| Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:53:10:53:19 | ControlFlowNode for bomb_input |
-| Other_compresstion_modules.py:53:10:53:19 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:7:10:7:19 | ControlFlowNode for bomb_input |
-| file:///usr/lib/python3.10/zipfile.py:344:24:344:31 | ControlFlowNode for filename | file:///usr/lib/python3.10/zipfile.py:351:24:351:44 | ControlFlowNode for Subscript |
-| file:///usr/lib/python3.10/zipfile.py:344:24:344:31 | ControlFlowNode for filename | file:///usr/lib/python3.10/zipfile.py:358:25:358:32 | ControlFlowNode for filename |
-| file:///usr/lib/python3.10/zipfile.py:351:24:351:44 | ControlFlowNode for Subscript | file:///usr/lib/python3.10/zipfile.py:358:25:358:32 | ControlFlowNode for filename |
-| file:///usr/lib/python3.10/zipfile.py:358:25:358:32 | ControlFlowNode for filename | file:///usr/lib/python3.10/zipfile.py:358:9:358:12 | [post] ControlFlowNode for self [Attribute filename] |
-| file:///usr/lib/python3.10/zipfile.py:1216:24:1216:27 | ControlFlowNode for file | file:///usr/lib/python3.10/zipfile.py:1258:23:1258:26 | ControlFlowNode for file |
-| file:///usr/lib/python3.10/zipfile.py:1258:13:1258:16 | [post] ControlFlowNode for self [Attribute fp] | file:///usr/lib/python3.10/zipfile.py:1259:13:1259:16 | ControlFlowNode for self [Attribute fp] |
-| file:///usr/lib/python3.10/zipfile.py:1258:23:1258:26 | ControlFlowNode for file | file:///usr/lib/python3.10/zipfile.py:1258:13:1258:16 | [post] ControlFlowNode for self [Attribute fp] |
-| file:///usr/lib/python3.10/zipfile.py:1259:13:1259:16 | ControlFlowNode for self [Attribute fp] | file:///usr/lib/python3.10/zipfile.py:1260:9:1260:12 | ControlFlowNode for self [Attribute fp] |
-| file:///usr/lib/python3.10/zipfile.py:1260:9:1260:12 | ControlFlowNode for self [Attribute fp] | file:///usr/lib/python3.10/zipfile.py:1261:9:1261:12 | ControlFlowNode for self [Attribute fp] |
-| file:///usr/lib/python3.10/zipfile.py:1261:9:1261:12 | ControlFlowNode for self [Attribute fp] | file:///usr/lib/python3.10/zipfile.py:1262:9:1262:12 | ControlFlowNode for self [Attribute fp] |
-| file:///usr/lib/python3.10/zipfile.py:1262:9:1262:12 | ControlFlowNode for self [Attribute fp] | file:///usr/lib/python3.10/zipfile.py:1263:9:1263:12 | ControlFlowNode for self [Attribute fp] |
-| file:///usr/lib/python3.10/zipfile.py:1263:9:1263:12 | ControlFlowNode for self [Attribute fp] | file:///usr/lib/python3.10/zipfile.py:1267:17:1267:20 | ControlFlowNode for self [Attribute fp] |
-| file:///usr/lib/python3.10/zipfile.py:1263:9:1263:12 | ControlFlowNode for self [Attribute fp] | file:///usr/lib/python3.10/zipfile.py:1287:21:1287:24 | ControlFlowNode for self [Attribute fp] |
-| file:///usr/lib/python3.10/zipfile.py:1267:17:1267:20 | ControlFlowNode for self [Attribute fp] | file:///usr/lib/python3.10/zipfile.py:1267:17:1267:20 | [post] ControlFlowNode for self [Attribute filelist, List element, Attribute filename] |
-| file:///usr/lib/python3.10/zipfile.py:1267:17:1267:20 | ControlFlowNode for self [Attribute fp] | file:///usr/lib/python3.10/zipfile.py:1326:26:1326:29 | ControlFlowNode for self [Attribute fp] |
-| file:///usr/lib/python3.10/zipfile.py:1287:21:1287:24 | ControlFlowNode for self [Attribute fp] | file:///usr/lib/python3.10/zipfile.py:1287:21:1287:24 | [post] ControlFlowNode for self [Attribute filelist, List element, Attribute filename] |
-| file:///usr/lib/python3.10/zipfile.py:1287:21:1287:24 | ControlFlowNode for self [Attribute fp] | file:///usr/lib/python3.10/zipfile.py:1326:26:1326:29 | ControlFlowNode for self [Attribute fp] |
-| file:///usr/lib/python3.10/zipfile.py:1326:26:1326:29 | ControlFlowNode for self [Attribute fp] | file:///usr/lib/python3.10/zipfile.py:1328:14:1328:17 | ControlFlowNode for self [Attribute fp] |
-| file:///usr/lib/python3.10/zipfile.py:1328:14:1328:17 | ControlFlowNode for self [Attribute fp] | file:///usr/lib/python3.10/zipfile.py:1328:14:1328:20 | ControlFlowNode for Attribute |
-| file:///usr/lib/python3.10/zipfile.py:1328:14:1328:20 | ControlFlowNode for Attribute | file:///usr/lib/python3.10/zipfile.py:1376:25:1376:32 | ControlFlowNode for filename |
-| file:///usr/lib/python3.10/zipfile.py:1376:17:1376:33 | ControlFlowNode for ZipInfo() [Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1377:13:1377:13 | ControlFlowNode for x [Attribute filename] |
-| file:///usr/lib/python3.10/zipfile.py:1376:25:1376:32 | ControlFlowNode for filename | file:///usr/lib/python3.10/zipfile.py:344:24:344:31 | ControlFlowNode for filename |
-| file:///usr/lib/python3.10/zipfile.py:1376:25:1376:32 | ControlFlowNode for filename | file:///usr/lib/python3.10/zipfile.py:1376:17:1376:33 | ControlFlowNode for ZipInfo() [Attribute filename] |
-| file:///usr/lib/python3.10/zipfile.py:1377:13:1377:13 | ControlFlowNode for x [Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1378:13:1378:13 | ControlFlowNode for x [Attribute filename] |
-| file:///usr/lib/python3.10/zipfile.py:1378:13:1378:13 | ControlFlowNode for x [Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1379:13:1379:13 | ControlFlowNode for x [Attribute filename] |
-| file:///usr/lib/python3.10/zipfile.py:1379:13:1379:13 | ControlFlowNode for x [Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1388:13:1388:13 | ControlFlowNode for x [Attribute filename] |
-| file:///usr/lib/python3.10/zipfile.py:1388:13:1388:13 | ControlFlowNode for x [Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1389:13:1389:13 | ControlFlowNode for x [Attribute filename] |
-| file:///usr/lib/python3.10/zipfile.py:1389:13:1389:13 | ControlFlowNode for x [Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1393:13:1393:13 | ControlFlowNode for x [Attribute filename] |
-| file:///usr/lib/python3.10/zipfile.py:1393:13:1393:13 | ControlFlowNode for x [Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1394:34:1394:34 | ControlFlowNode for x [Attribute filename] |
-| file:///usr/lib/python3.10/zipfile.py:1394:13:1394:25 | [post] ControlFlowNode for Attribute [List element, Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1394:13:1394:16 | [post] ControlFlowNode for self [Attribute filelist, List element, Attribute filename] |
-| file:///usr/lib/python3.10/zipfile.py:1394:34:1394:34 | ControlFlowNode for x [Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1394:13:1394:25 | [post] ControlFlowNode for Attribute [List element, Attribute filename] |
-| file:///usr/lib/python3.10/zipfile.py:1410:18:1410:21 | ControlFlowNode for self [Attribute filelist, List element, Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1413:16:1413:19 | ControlFlowNode for self [Attribute filelist, List element, Attribute filename] |
-| file:///usr/lib/python3.10/zipfile.py:1413:16:1413:19 | ControlFlowNode for self [Attribute filelist, List element, Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1413:16:1413:28 | ControlFlowNode for Attribute [List element, Attribute filename] |
-| file:///usr/lib/python3.10/zipfile.py:1617:23:1617:28 | ControlFlowNode for member [Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1628:37:1628:42 | ControlFlowNode for member [Attribute filename] |
-| file:///usr/lib/python3.10/zipfile.py:1628:37:1628:42 | ControlFlowNode for member [Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1628:16:1628:54 | ControlFlowNode for Attribute() |
-| file:///usr/lib/python3.10/zipfile.py:1628:37:1628:42 | ControlFlowNode for member [Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1662:31:1662:36 | ControlFlowNode for member [Attribute filename] |
-| file:///usr/lib/python3.10/zipfile.py:1662:31:1662:36 | ControlFlowNode for member [Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1671:19:1671:24 | ControlFlowNode for member [Attribute filename] |
-| file:///usr/lib/python3.10/zipfile.py:1671:19:1671:24 | ControlFlowNode for member [Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1671:19:1671:33 | ControlFlowNode for Attribute |
-| file:///usr/lib/python3.10/zipfile.py:1671:19:1671:33 | ControlFlowNode for Attribute | file:///usr/lib/python3.10/zipfile.py:1677:19:1677:48 | ControlFlowNode for Subscript |
-| file:///usr/lib/python3.10/zipfile.py:1677:19:1677:48 | ControlFlowNode for Subscript | file:///usr/lib/python3.10/zipfile.py:1679:42:1679:42 | SSA variable x |
-| file:///usr/lib/python3.10/zipfile.py:1679:36:1680:65 | ControlFlowNode for GeneratorExp | file:///usr/lib/python3.10/zipfile.py:1696:20:1696:29 | ControlFlowNode for targetpath |
-| file:///usr/lib/python3.10/zipfile.py:1679:36:1680:65 | ControlFlowNode for GeneratorExp | file:///usr/lib/python3.10/zipfile.py:1702:16:1702:25 | ControlFlowNode for targetpath |
-| file:///usr/lib/python3.10/zipfile.py:1679:42:1679:42 | SSA variable x | file:///usr/lib/python3.10/zipfile.py:1679:36:1680:65 | ControlFlowNode for GeneratorExp |
-| tarfile_module.py:5:17:5:24 | ControlFlowNode for tar_path | tarfile_module.py:8:5:8:54 | ControlFlowNode for Attribute() |
-| tarfile_module.py:5:17:5:24 | ControlFlowNode for tar_path | tarfile_module.py:9:5:9:54 | ControlFlowNode for Attribute() |
-| tarfile_module.py:5:17:5:24 | ControlFlowNode for tar_path | tarfile_module.py:10:5:10:56 | ControlFlowNode for Attribute() |
-| tarfile_module.py:5:17:5:24 | ControlFlowNode for tar_path | tarfile_module.py:11:5:11:49 | ControlFlowNode for Attribute() |
-| tarfile_module.py:5:17:5:24 | ControlFlowNode for tar_path | tarfile_module.py:12:5:12:59 | ControlFlowNode for Attribute() |
-| tarfile_module.py:5:17:5:24 | ControlFlowNode for tar_path | tarfile_module.py:13:5:13:51 | ControlFlowNode for Attribute() |
-| tarfile_module.py:29:19:29:24 | ControlFlowNode for bar_id | tarfile_module.py:30:17:30:22 | ControlFlowNode for bar_id |
-| tarfile_module.py:30:17:30:22 | ControlFlowNode for bar_id | tarfile_module.py:5:17:5:24 | ControlFlowNode for tar_path |
-| zipfile_module.py:9:17:9:24 | ControlFlowNode for zip_path | zipfile_module.py:11:5:11:57 | ControlFlowNode for Attribute() |
-| zipfile_module.py:9:17:9:24 | ControlFlowNode for zip_path | zipfile_module.py:12:34:12:41 | ControlFlowNode for zip_path |
-| zipfile_module.py:9:17:9:24 | ControlFlowNode for zip_path | zipfile_module.py:15:5:15:38 | ControlFlowNode for Attribute() |
-| zipfile_module.py:9:17:9:24 | ControlFlowNode for zip_path | zipfile_module.py:17:5:17:58 | ControlFlowNode for Attribute() |
-| zipfile_module.py:12:18:12:47 | ControlFlowNode for Attribute() [Attribute filelist, List element, Attribute filename] | zipfile_module.py:17:24:17:33 | ControlFlowNode for zipFileObj [Attribute filelist, List element, Attribute filename] |
-| zipfile_module.py:12:34:12:41 | ControlFlowNode for zip_path | file:///usr/lib/python3.10/zipfile.py:1216:24:1216:27 | ControlFlowNode for file |
-| zipfile_module.py:12:34:12:41 | ControlFlowNode for zip_path | zipfile_module.py:12:18:12:47 | ControlFlowNode for Attribute() [Attribute filelist, List element, Attribute filename] |
-| zipfile_module.py:17:24:17:33 | ControlFlowNode for zipFileObj [Attribute filelist, List element, Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1410:18:1410:21 | ControlFlowNode for self [Attribute filelist, List element, Attribute filename] |
-| zipfile_module.py:17:24:17:33 | ControlFlowNode for zipFileObj [Attribute filelist, List element, Attribute filename] | zipfile_module.py:17:24:17:44 | ControlFlowNode for Attribute() [List element, Attribute filename] |
-| zipfile_module.py:17:24:17:44 | ControlFlowNode for Attribute() [List element, Attribute filename] | zipfile_module.py:17:24:17:47 | ControlFlowNode for Subscript [Attribute filename] |
-| zipfile_module.py:17:24:17:47 | ControlFlowNode for Subscript [Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1617:23:1617:28 | ControlFlowNode for member [Attribute filename] |
-| zipfile_module.py:17:24:17:47 | ControlFlowNode for Subscript [Attribute filename] | zipfile_module.py:17:5:17:58 | ControlFlowNode for Attribute() |
-| zipfile_module.py:20:20:20:27 | ControlFlowNode for zip_path | zipfile_module.py:30:5:30:35 | ControlFlowNode for Attribute() |
-| zipfile_module.py:34:18:34:25 | ControlFlowNode for zip_path | zipfile_module.py:37:14:37:29 | ControlFlowNode for Attribute() |
-| zipfile_module.py:34:18:34:25 | ControlFlowNode for zip_path | zipfile_module.py:42:29:42:36 | ControlFlowNode for Attribute |
-| zipfile_module.py:34:18:34:25 | ControlFlowNode for zip_path | zipfile_module.py:45:14:45:39 | ControlFlowNode for Attribute() |
-| zipfile_module.py:34:18:34:25 | ControlFlowNode for zip_path | zipfile_module.py:53:5:53:35 | ControlFlowNode for Attribute() |
-| zipfile_module.py:34:18:34:25 | ControlFlowNode for zip_path | zipfile_module.py:62:44:62:61 | ControlFlowNode for Attribute() |
-| zipfile_module.py:42:21:42:37 | ControlFlowNode for BytesIO() | file:///usr/lib/python3.10/zipfile.py:1216:24:1216:27 | ControlFlowNode for file |
-| zipfile_module.py:42:21:42:37 | ControlFlowNode for BytesIO() | zipfile_module.py:42:5:42:54 | ControlFlowNode for Attribute() |
-| zipfile_module.py:42:29:42:36 | ControlFlowNode for Attribute | zipfile_module.py:42:5:42:54 | ControlFlowNode for Attribute() |
-| zipfile_module.py:42:29:42:36 | ControlFlowNode for Attribute | zipfile_module.py:42:21:42:37 | ControlFlowNode for BytesIO() |
-| zipfile_module.py:89:19:89:24 | ControlFlowNode for bar_id | zipfile_module.py:90:20:90:25 | ControlFlowNode for bar_id |
-| zipfile_module.py:89:19:89:24 | ControlFlowNode for bar_id | zipfile_module.py:91:17:91:22 | ControlFlowNode for bar_id |
-| zipfile_module.py:89:19:89:24 | ControlFlowNode for bar_id | zipfile_module.py:92:18:92:23 | ControlFlowNode for bar_id |
-| zipfile_module.py:90:20:90:25 | ControlFlowNode for bar_id | zipfile_module.py:20:20:20:27 | ControlFlowNode for zip_path |
-| zipfile_module.py:91:17:91:22 | ControlFlowNode for bar_id | zipfile_module.py:9:17:9:24 | ControlFlowNode for zip_path |
-| zipfile_module.py:92:18:92:23 | ControlFlowNode for bar_id | zipfile_module.py:34:18:34:25 | ControlFlowNode for zip_path |
-| zipfile_module.py:99:26:99:34 | ControlFlowNode for Attribute | file:///usr/lib/python3.10/zipfile.py:1216:24:1216:27 | ControlFlowNode for file |
-| zipfile_module.py:99:26:99:34 | ControlFlowNode for Attribute | zipfile_module.py:100:14:100:29 | ControlFlowNode for Attribute() |
-| zipfile_module.py:106:23:106:26 | ControlFlowNode for file | zipfile_module.py:108:14:108:29 | ControlFlowNode for Attribute() |
-| zipfile_module.py:116:30:116:38 | ControlFlowNode for Attribute | file:///usr/lib/python3.10/zipfile.py:1216:24:1216:27 | ControlFlowNode for file |
-| zipfile_module.py:116:30:116:38 | ControlFlowNode for Attribute | zipfile_module.py:117:18:117:33 | ControlFlowNode for Attribute() |
-| zipfile_module.py:125:30:125:38 | ControlFlowNode for Attribute | file:///usr/lib/python3.10/zipfile.py:1216:24:1216:27 | ControlFlowNode for file |
-| zipfile_module.py:125:30:125:38 | ControlFlowNode for Attribute | zipfile_module.py:126:18:126:33 | ControlFlowNode for Attribute() |
+| file:///usr/lib/python3.10/tarfile.py:1850:21:1850:24 | ControlFlowNode for name | file:///usr/lib/python3.10/tarfile.py:1863:32:1863:35 | ControlFlowNode for name |
+| file:///usr/lib/python3.10/tarfile.py:1911:21:1911:24 | ControlFlowNode for name | file:///usr/lib/python3.10/tarfile.py:1923:28:1923:42 | ControlFlowNode for BoolExpr |
+| file:///usr/lib/python3.10/tarfile.py:2373:24:2373:72 | ControlFlowNode for Attribute() | file:///usr/lib/python3.10/tarfile.py:2373:24:2373:72 | ControlFlowNode for Attribute() |
+| file:///usr/lib/python3.10/tarfile.py:2373:24:2373:72 | ControlFlowNode for Attribute() | file:///usr/lib/python3.10/tarfile.py:2373:24:2373:72 | ControlFlowNode for Attribute() |
+| file:///usr/lib/python3.10/tarfile.py:2373:24:2373:72 | ControlFlowNode for Attribute() | test.py:23:5:23:52 | ControlFlowNode for Attribute() |
+| file:///usr/lib/python3.10/tarfile.py:2373:24:2373:72 | ControlFlowNode for Attribute() | test.py:27:5:27:60 | ControlFlowNode for Attribute() |
+| test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:10:5:10:52 | ControlFlowNode for Attribute() |
+| test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:10:21:10:29 | ControlFlowNode for file_path |
+| test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:11:5:11:48 | ControlFlowNode for Attribute() |
+| test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:11:21:11:29 | ControlFlowNode for file_path |
+| test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:14:14:14:29 | ControlFlowNode for Attribute() |
+| test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:21:5:21:60 | ControlFlowNode for Attribute() |
+| test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:21:21:21:29 | ControlFlowNode for file_path |
+| test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:23:5:23:52 | ControlFlowNode for Attribute() |
+| test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:23:18:23:26 | ControlFlowNode for file_path |
+| test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:24:5:24:55 | ControlFlowNode for Attribute() |
+| test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:25:5:25:57 | ControlFlowNode for Attribute() |
+| test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:25:28:25:36 | ControlFlowNode for file_path |
+| test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:26:5:26:50 | ControlFlowNode for Attribute() |
+| test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:26:28:26:36 | ControlFlowNode for file_path |
+| test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:27:5:27:60 | ControlFlowNode for Attribute() |
+| test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:27:26:27:34 | ControlFlowNode for file_path |
+| test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:30:28:30:36 | ControlFlowNode for file_path |
+| test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:34:27:34:35 | ControlFlowNode for file_path |
+| test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:38:15:38:23 | ControlFlowNode for file_path |
+| test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:39:19:39:27 | ControlFlowNode for file_path |
+| test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:43:14:43:22 | ControlFlowNode for file_path |
+| test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:44:17:44:25 | ControlFlowNode for file_path |
+| test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:48:15:48:23 | ControlFlowNode for file_path |
+| test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:49:19:49:27 | ControlFlowNode for file_path |
+| test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:53:40:53:48 | ControlFlowNode for file_path |
+| test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:55:23:55:31 | ControlFlowNode for file_path |
+| test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:56:21:56:29 | ControlFlowNode for file_path |
+| test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:58:40:58:48 | ControlFlowNode for file_path |
+| test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:59:22:59:30 | ControlFlowNode for file_path |
+| test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:60:21:60:29 | ControlFlowNode for file_path |
+| test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:61:42:61:50 | ControlFlowNode for file_path |
+| test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:62:23:62:31 | ControlFlowNode for file_path |
+| test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:63:36:63:44 | ControlFlowNode for file_path |
+| test.py:10:21:10:29 | ControlFlowNode for file_path | file:///usr/lib/python3.10/zipfile.py:1700:14:1700:39 | ControlFlowNode for Attribute() |
+| test.py:11:21:11:29 | ControlFlowNode for file_path | file:///usr/lib/python3.10/zipfile.py:1700:14:1700:39 | ControlFlowNode for Attribute() |
+| test.py:21:21:21:29 | ControlFlowNode for file_path | file:///usr/lib/python3.10/zipfile.py:1477:14:1477:38 | ControlFlowNode for Attribute() |
+| test.py:23:18:23:26 | ControlFlowNode for file_path | file:///usr/lib/python3.10/tarfile.py:2373:24:2373:72 | ControlFlowNode for Attribute() |
+| test.py:23:18:23:26 | ControlFlowNode for file_path | file:///usr/lib/python3.10/tarfile.py:2373:24:2373:72 | ControlFlowNode for Attribute() |
+| test.py:25:28:25:36 | ControlFlowNode for file_path | file:///usr/lib/python3.10/tarfile.py:1911:21:1911:24 | ControlFlowNode for name |
+| test.py:26:28:26:36 | ControlFlowNode for file_path | file:///usr/lib/python3.10/tarfile.py:1850:21:1850:24 | ControlFlowNode for name |
+| test.py:27:26:27:34 | ControlFlowNode for file_path | file:///usr/lib/python3.10/tarfile.py:2373:24:2373:72 | ControlFlowNode for Attribute() |
+| test.py:27:26:27:34 | ControlFlowNode for file_path | file:///usr/lib/python3.10/tarfile.py:2373:24:2373:72 | ControlFlowNode for Attribute() |
+| test.py:30:28:30:36 | ControlFlowNode for file_path | file:///usr/lib/python3.10/tarfile.py:1850:21:1850:24 | ControlFlowNode for name |
nodes
-| Other_compresstion_modules.py:7:10:7:19 | ControlFlowNode for bomb_input | semmle.label | ControlFlowNode for bomb_input |
-| Other_compresstion_modules.py:9:27:9:36 | ControlFlowNode for bomb_input | semmle.label | ControlFlowNode for bomb_input |
-| Other_compresstion_modules.py:11:15:11:24 | ControlFlowNode for bomb_input | semmle.label | ControlFlowNode for bomb_input |
-| Other_compresstion_modules.py:12:19:12:28 | ControlFlowNode for bomb_input | semmle.label | ControlFlowNode for bomb_input |
-| Other_compresstion_modules.py:14:15:14:24 | ControlFlowNode for bomb_input | semmle.label | ControlFlowNode for bomb_input |
-| Other_compresstion_modules.py:15:19:15:28 | ControlFlowNode for bomb_input | semmle.label | ControlFlowNode for bomb_input |
-| Other_compresstion_modules.py:17:20:17:29 | ControlFlowNode for bomb_input | semmle.label | ControlFlowNode for bomb_input |
-| Other_compresstion_modules.py:23:14:23:23 | ControlFlowNode for bomb_input | semmle.label | ControlFlowNode for bomb_input |
-| Other_compresstion_modules.py:24:17:24:26 | ControlFlowNode for bomb_input | semmle.label | ControlFlowNode for bomb_input |
-| Other_compresstion_modules.py:27:40:27:49 | ControlFlowNode for bomb_input | semmle.label | ControlFlowNode for bomb_input |
-| Other_compresstion_modules.py:29:23:29:32 | ControlFlowNode for bomb_input | semmle.label | ControlFlowNode for bomb_input |
-| Other_compresstion_modules.py:30:21:30:30 | ControlFlowNode for bomb_input | semmle.label | ControlFlowNode for bomb_input |
-| Other_compresstion_modules.py:32:40:32:49 | ControlFlowNode for bomb_input | semmle.label | ControlFlowNode for bomb_input |
-| Other_compresstion_modules.py:33:22:33:31 | ControlFlowNode for bomb_input | semmle.label | ControlFlowNode for bomb_input |
-| Other_compresstion_modules.py:34:21:34:30 | ControlFlowNode for bomb_input | semmle.label | ControlFlowNode for bomb_input |
-| Other_compresstion_modules.py:35:42:35:51 | ControlFlowNode for bomb_input | semmle.label | ControlFlowNode for bomb_input |
-| Other_compresstion_modules.py:36:23:36:32 | ControlFlowNode for bomb_input | semmle.label | ControlFlowNode for bomb_input |
-| Other_compresstion_modules.py:37:36:37:45 | ControlFlowNode for bomb_input | semmle.label | ControlFlowNode for bomb_input |
-| Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | semmle.label | ControlFlowNode for bomb_input |
-| Other_compresstion_modules.py:53:10:53:19 | ControlFlowNode for bomb_input | semmle.label | ControlFlowNode for bomb_input |
-| file:///usr/lib/python3.10/zipfile.py:344:24:344:31 | ControlFlowNode for filename | semmle.label | ControlFlowNode for filename |
-| file:///usr/lib/python3.10/zipfile.py:351:24:351:44 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
-| file:///usr/lib/python3.10/zipfile.py:358:9:358:12 | [post] ControlFlowNode for self [Attribute filename] | semmle.label | [post] ControlFlowNode for self [Attribute filename] |
-| file:///usr/lib/python3.10/zipfile.py:358:25:358:32 | ControlFlowNode for filename | semmle.label | ControlFlowNode for filename |
-| file:///usr/lib/python3.10/zipfile.py:1216:24:1216:27 | ControlFlowNode for file | semmle.label | ControlFlowNode for file |
-| file:///usr/lib/python3.10/zipfile.py:1258:13:1258:16 | [post] ControlFlowNode for self [Attribute fp] | semmle.label | [post] ControlFlowNode for self [Attribute fp] |
-| file:///usr/lib/python3.10/zipfile.py:1258:23:1258:26 | ControlFlowNode for file | semmle.label | ControlFlowNode for file |
-| file:///usr/lib/python3.10/zipfile.py:1259:13:1259:16 | ControlFlowNode for self [Attribute fp] | semmle.label | ControlFlowNode for self [Attribute fp] |
-| file:///usr/lib/python3.10/zipfile.py:1260:9:1260:12 | ControlFlowNode for self [Attribute fp] | semmle.label | ControlFlowNode for self [Attribute fp] |
-| file:///usr/lib/python3.10/zipfile.py:1261:9:1261:12 | ControlFlowNode for self [Attribute fp] | semmle.label | ControlFlowNode for self [Attribute fp] |
-| file:///usr/lib/python3.10/zipfile.py:1262:9:1262:12 | ControlFlowNode for self [Attribute fp] | semmle.label | ControlFlowNode for self [Attribute fp] |
-| file:///usr/lib/python3.10/zipfile.py:1263:9:1263:12 | ControlFlowNode for self [Attribute fp] | semmle.label | ControlFlowNode for self [Attribute fp] |
-| file:///usr/lib/python3.10/zipfile.py:1267:17:1267:20 | ControlFlowNode for self [Attribute fp] | semmle.label | ControlFlowNode for self [Attribute fp] |
-| file:///usr/lib/python3.10/zipfile.py:1267:17:1267:20 | [post] ControlFlowNode for self [Attribute filelist, List element, Attribute filename] | semmle.label | [post] ControlFlowNode for self [Attribute filelist, List element, Attribute filename] |
-| file:///usr/lib/python3.10/zipfile.py:1287:21:1287:24 | ControlFlowNode for self [Attribute fp] | semmle.label | ControlFlowNode for self [Attribute fp] |
-| file:///usr/lib/python3.10/zipfile.py:1287:21:1287:24 | [post] ControlFlowNode for self [Attribute filelist, List element, Attribute filename] | semmle.label | [post] ControlFlowNode for self [Attribute filelist, List element, Attribute filename] |
-| file:///usr/lib/python3.10/zipfile.py:1326:26:1326:29 | ControlFlowNode for self [Attribute fp] | semmle.label | ControlFlowNode for self [Attribute fp] |
-| file:///usr/lib/python3.10/zipfile.py:1328:14:1328:17 | ControlFlowNode for self [Attribute fp] | semmle.label | ControlFlowNode for self [Attribute fp] |
-| file:///usr/lib/python3.10/zipfile.py:1328:14:1328:20 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
-| file:///usr/lib/python3.10/zipfile.py:1376:17:1376:33 | ControlFlowNode for ZipInfo() [Attribute filename] | semmle.label | ControlFlowNode for ZipInfo() [Attribute filename] |
-| file:///usr/lib/python3.10/zipfile.py:1376:25:1376:32 | ControlFlowNode for filename | semmle.label | ControlFlowNode for filename |
-| file:///usr/lib/python3.10/zipfile.py:1377:13:1377:13 | ControlFlowNode for x [Attribute filename] | semmle.label | ControlFlowNode for x [Attribute filename] |
-| file:///usr/lib/python3.10/zipfile.py:1378:13:1378:13 | ControlFlowNode for x [Attribute filename] | semmle.label | ControlFlowNode for x [Attribute filename] |
-| file:///usr/lib/python3.10/zipfile.py:1379:13:1379:13 | ControlFlowNode for x [Attribute filename] | semmle.label | ControlFlowNode for x [Attribute filename] |
-| file:///usr/lib/python3.10/zipfile.py:1388:13:1388:13 | ControlFlowNode for x [Attribute filename] | semmle.label | ControlFlowNode for x [Attribute filename] |
-| file:///usr/lib/python3.10/zipfile.py:1389:13:1389:13 | ControlFlowNode for x [Attribute filename] | semmle.label | ControlFlowNode for x [Attribute filename] |
-| file:///usr/lib/python3.10/zipfile.py:1393:13:1393:13 | ControlFlowNode for x [Attribute filename] | semmle.label | ControlFlowNode for x [Attribute filename] |
-| file:///usr/lib/python3.10/zipfile.py:1394:13:1394:16 | [post] ControlFlowNode for self [Attribute filelist, List element, Attribute filename] | semmle.label | [post] ControlFlowNode for self [Attribute filelist, List element, Attribute filename] |
-| file:///usr/lib/python3.10/zipfile.py:1394:13:1394:25 | [post] ControlFlowNode for Attribute [List element, Attribute filename] | semmle.label | [post] ControlFlowNode for Attribute [List element, Attribute filename] |
-| file:///usr/lib/python3.10/zipfile.py:1394:34:1394:34 | ControlFlowNode for x [Attribute filename] | semmle.label | ControlFlowNode for x [Attribute filename] |
-| file:///usr/lib/python3.10/zipfile.py:1410:18:1410:21 | ControlFlowNode for self [Attribute filelist, List element, Attribute filename] | semmle.label | ControlFlowNode for self [Attribute filelist, List element, Attribute filename] |
-| file:///usr/lib/python3.10/zipfile.py:1413:16:1413:19 | ControlFlowNode for self [Attribute filelist, List element, Attribute filename] | semmle.label | ControlFlowNode for self [Attribute filelist, List element, Attribute filename] |
-| file:///usr/lib/python3.10/zipfile.py:1413:16:1413:28 | ControlFlowNode for Attribute [List element, Attribute filename] | semmle.label | ControlFlowNode for Attribute [List element, Attribute filename] |
-| file:///usr/lib/python3.10/zipfile.py:1617:23:1617:28 | ControlFlowNode for member [Attribute filename] | semmle.label | ControlFlowNode for member [Attribute filename] |
-| file:///usr/lib/python3.10/zipfile.py:1628:16:1628:54 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
-| file:///usr/lib/python3.10/zipfile.py:1628:37:1628:42 | ControlFlowNode for member [Attribute filename] | semmle.label | ControlFlowNode for member [Attribute filename] |
-| file:///usr/lib/python3.10/zipfile.py:1662:31:1662:36 | ControlFlowNode for member [Attribute filename] | semmle.label | ControlFlowNode for member [Attribute filename] |
-| file:///usr/lib/python3.10/zipfile.py:1671:19:1671:24 | ControlFlowNode for member [Attribute filename] | semmle.label | ControlFlowNode for member [Attribute filename] |
-| file:///usr/lib/python3.10/zipfile.py:1671:19:1671:33 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
-| file:///usr/lib/python3.10/zipfile.py:1677:19:1677:48 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript |
-| file:///usr/lib/python3.10/zipfile.py:1679:36:1680:65 | ControlFlowNode for GeneratorExp | semmle.label | ControlFlowNode for GeneratorExp |
-| file:///usr/lib/python3.10/zipfile.py:1679:42:1679:42 | SSA variable x | semmle.label | SSA variable x |
-| file:///usr/lib/python3.10/zipfile.py:1696:20:1696:29 | ControlFlowNode for targetpath | semmle.label | ControlFlowNode for targetpath |
-| file:///usr/lib/python3.10/zipfile.py:1702:16:1702:25 | ControlFlowNode for targetpath | semmle.label | ControlFlowNode for targetpath |
-| tarfile_module.py:5:17:5:24 | ControlFlowNode for tar_path | semmle.label | ControlFlowNode for tar_path |
-| tarfile_module.py:8:5:8:54 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
-| tarfile_module.py:9:5:9:54 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
-| tarfile_module.py:10:5:10:56 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
-| tarfile_module.py:11:5:11:49 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
-| tarfile_module.py:12:5:12:59 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
-| tarfile_module.py:13:5:13:51 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
-| tarfile_module.py:29:19:29:24 | ControlFlowNode for bar_id | semmle.label | ControlFlowNode for bar_id |
-| tarfile_module.py:30:17:30:22 | ControlFlowNode for bar_id | semmle.label | ControlFlowNode for bar_id |
-| zipfile_module.py:9:17:9:24 | ControlFlowNode for zip_path | semmle.label | ControlFlowNode for zip_path |
-| zipfile_module.py:11:5:11:57 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
-| zipfile_module.py:12:18:12:47 | ControlFlowNode for Attribute() [Attribute filelist, List element, Attribute filename] | semmle.label | ControlFlowNode for Attribute() [Attribute filelist, List element, Attribute filename] |
-| zipfile_module.py:12:34:12:41 | ControlFlowNode for zip_path | semmle.label | ControlFlowNode for zip_path |
-| zipfile_module.py:15:5:15:38 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
-| zipfile_module.py:17:5:17:58 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
-| zipfile_module.py:17:24:17:33 | ControlFlowNode for zipFileObj [Attribute filelist, List element, Attribute filename] | semmle.label | ControlFlowNode for zipFileObj [Attribute filelist, List element, Attribute filename] |
-| zipfile_module.py:17:24:17:44 | ControlFlowNode for Attribute() [List element, Attribute filename] | semmle.label | ControlFlowNode for Attribute() [List element, Attribute filename] |
-| zipfile_module.py:17:24:17:47 | ControlFlowNode for Subscript [Attribute filename] | semmle.label | ControlFlowNode for Subscript [Attribute filename] |
-| zipfile_module.py:20:20:20:27 | ControlFlowNode for zip_path | semmle.label | ControlFlowNode for zip_path |
-| zipfile_module.py:30:5:30:35 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
-| zipfile_module.py:34:18:34:25 | ControlFlowNode for zip_path | semmle.label | ControlFlowNode for zip_path |
-| zipfile_module.py:37:14:37:29 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
-| zipfile_module.py:42:5:42:54 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
-| zipfile_module.py:42:21:42:37 | ControlFlowNode for BytesIO() | semmle.label | ControlFlowNode for BytesIO() |
-| zipfile_module.py:42:29:42:36 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
-| zipfile_module.py:45:14:45:39 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
-| zipfile_module.py:53:5:53:35 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
-| zipfile_module.py:62:44:62:61 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
-| zipfile_module.py:89:19:89:24 | ControlFlowNode for bar_id | semmle.label | ControlFlowNode for bar_id |
-| zipfile_module.py:90:20:90:25 | ControlFlowNode for bar_id | semmle.label | ControlFlowNode for bar_id |
-| zipfile_module.py:91:17:91:22 | ControlFlowNode for bar_id | semmle.label | ControlFlowNode for bar_id |
-| zipfile_module.py:92:18:92:23 | ControlFlowNode for bar_id | semmle.label | ControlFlowNode for bar_id |
-| zipfile_module.py:99:26:99:34 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
-| zipfile_module.py:100:14:100:29 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
-| zipfile_module.py:106:23:106:26 | ControlFlowNode for file | semmle.label | ControlFlowNode for file |
-| zipfile_module.py:108:14:108:29 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
-| zipfile_module.py:116:30:116:38 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
-| zipfile_module.py:117:18:117:33 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
-| zipfile_module.py:125:30:125:38 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
-| zipfile_module.py:126:18:126:33 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| file:///usr/lib/python3.10/tarfile.py:1850:21:1850:24 | ControlFlowNode for name | semmle.label | ControlFlowNode for name |
+| file:///usr/lib/python3.10/tarfile.py:1863:32:1863:35 | ControlFlowNode for name | semmle.label | ControlFlowNode for name |
+| file:///usr/lib/python3.10/tarfile.py:1911:21:1911:24 | ControlFlowNode for name | semmle.label | ControlFlowNode for name |
+| file:///usr/lib/python3.10/tarfile.py:1923:28:1923:42 | ControlFlowNode for BoolExpr | semmle.label | ControlFlowNode for BoolExpr |
+| file:///usr/lib/python3.10/tarfile.py:2373:24:2373:72 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| file:///usr/lib/python3.10/tarfile.py:2373:24:2373:72 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| file:///usr/lib/python3.10/zipfile.py:1477:14:1477:38 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| file:///usr/lib/python3.10/zipfile.py:1700:14:1700:39 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:9:16:9:24 | ControlFlowNode for file_path | semmle.label | ControlFlowNode for file_path |
+| test.py:10:5:10:52 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:10:21:10:29 | ControlFlowNode for file_path | semmle.label | ControlFlowNode for file_path |
+| test.py:11:5:11:48 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:11:21:11:29 | ControlFlowNode for file_path | semmle.label | ControlFlowNode for file_path |
+| test.py:14:14:14:29 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:21:5:21:60 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:21:21:21:29 | ControlFlowNode for file_path | semmle.label | ControlFlowNode for file_path |
+| test.py:23:5:23:52 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:23:18:23:26 | ControlFlowNode for file_path | semmle.label | ControlFlowNode for file_path |
+| test.py:24:5:24:55 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:25:5:25:57 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:25:28:25:36 | ControlFlowNode for file_path | semmle.label | ControlFlowNode for file_path |
+| test.py:26:5:26:50 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:26:28:26:36 | ControlFlowNode for file_path | semmle.label | ControlFlowNode for file_path |
+| test.py:27:5:27:60 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
+| test.py:27:26:27:34 | ControlFlowNode for file_path | semmle.label | ControlFlowNode for file_path |
+| test.py:30:28:30:36 | ControlFlowNode for file_path | semmle.label | ControlFlowNode for file_path |
+| test.py:34:27:34:35 | ControlFlowNode for file_path | semmle.label | ControlFlowNode for file_path |
+| test.py:38:15:38:23 | ControlFlowNode for file_path | semmle.label | ControlFlowNode for file_path |
+| test.py:39:19:39:27 | ControlFlowNode for file_path | semmle.label | ControlFlowNode for file_path |
+| test.py:43:14:43:22 | ControlFlowNode for file_path | semmle.label | ControlFlowNode for file_path |
+| test.py:44:17:44:25 | ControlFlowNode for file_path | semmle.label | ControlFlowNode for file_path |
+| test.py:48:15:48:23 | ControlFlowNode for file_path | semmle.label | ControlFlowNode for file_path |
+| test.py:49:19:49:27 | ControlFlowNode for file_path | semmle.label | ControlFlowNode for file_path |
+| test.py:53:40:53:48 | ControlFlowNode for file_path | semmle.label | ControlFlowNode for file_path |
+| test.py:55:23:55:31 | ControlFlowNode for file_path | semmle.label | ControlFlowNode for file_path |
+| test.py:56:21:56:29 | ControlFlowNode for file_path | semmle.label | ControlFlowNode for file_path |
+| test.py:58:40:58:48 | ControlFlowNode for file_path | semmle.label | ControlFlowNode for file_path |
+| test.py:59:22:59:30 | ControlFlowNode for file_path | semmle.label | ControlFlowNode for file_path |
+| test.py:60:21:60:29 | ControlFlowNode for file_path | semmle.label | ControlFlowNode for file_path |
+| test.py:61:42:61:50 | ControlFlowNode for file_path | semmle.label | ControlFlowNode for file_path |
+| test.py:62:23:62:31 | ControlFlowNode for file_path | semmle.label | ControlFlowNode for file_path |
+| test.py:63:36:63:44 | ControlFlowNode for file_path | semmle.label | ControlFlowNode for file_path |
subpaths
-| file:///usr/lib/python3.10/zipfile.py:1267:17:1267:20 | ControlFlowNode for self [Attribute fp] | file:///usr/lib/python3.10/zipfile.py:1326:26:1326:29 | ControlFlowNode for self [Attribute fp] | file:///usr/lib/python3.10/zipfile.py:1394:13:1394:16 | [post] ControlFlowNode for self [Attribute filelist, List element, Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1267:17:1267:20 | [post] ControlFlowNode for self [Attribute filelist, List element, Attribute filename] |
-| file:///usr/lib/python3.10/zipfile.py:1287:21:1287:24 | ControlFlowNode for self [Attribute fp] | file:///usr/lib/python3.10/zipfile.py:1326:26:1326:29 | ControlFlowNode for self [Attribute fp] | file:///usr/lib/python3.10/zipfile.py:1394:13:1394:16 | [post] ControlFlowNode for self [Attribute filelist, List element, Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1287:21:1287:24 | [post] ControlFlowNode for self [Attribute filelist, List element, Attribute filename] |
-| file:///usr/lib/python3.10/zipfile.py:1376:25:1376:32 | ControlFlowNode for filename | file:///usr/lib/python3.10/zipfile.py:344:24:344:31 | ControlFlowNode for filename | file:///usr/lib/python3.10/zipfile.py:358:9:358:12 | [post] ControlFlowNode for self [Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1376:17:1376:33 | ControlFlowNode for ZipInfo() [Attribute filename] |
-| file:///usr/lib/python3.10/zipfile.py:1628:37:1628:42 | ControlFlowNode for member [Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1662:31:1662:36 | ControlFlowNode for member [Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1696:20:1696:29 | ControlFlowNode for targetpath | file:///usr/lib/python3.10/zipfile.py:1628:16:1628:54 | ControlFlowNode for Attribute() |
-| file:///usr/lib/python3.10/zipfile.py:1628:37:1628:42 | ControlFlowNode for member [Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1662:31:1662:36 | ControlFlowNode for member [Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1702:16:1702:25 | ControlFlowNode for targetpath | file:///usr/lib/python3.10/zipfile.py:1628:16:1628:54 | ControlFlowNode for Attribute() |
-| zipfile_module.py:12:34:12:41 | ControlFlowNode for zip_path | file:///usr/lib/python3.10/zipfile.py:1216:24:1216:27 | ControlFlowNode for file | file:///usr/lib/python3.10/zipfile.py:1267:17:1267:20 | [post] ControlFlowNode for self [Attribute filelist, List element, Attribute filename] | zipfile_module.py:12:18:12:47 | ControlFlowNode for Attribute() [Attribute filelist, List element, Attribute filename] |
-| zipfile_module.py:12:34:12:41 | ControlFlowNode for zip_path | file:///usr/lib/python3.10/zipfile.py:1216:24:1216:27 | ControlFlowNode for file | file:///usr/lib/python3.10/zipfile.py:1287:21:1287:24 | [post] ControlFlowNode for self [Attribute filelist, List element, Attribute filename] | zipfile_module.py:12:18:12:47 | ControlFlowNode for Attribute() [Attribute filelist, List element, Attribute filename] |
-| zipfile_module.py:17:24:17:33 | ControlFlowNode for zipFileObj [Attribute filelist, List element, Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1410:18:1410:21 | ControlFlowNode for self [Attribute filelist, List element, Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1413:16:1413:28 | ControlFlowNode for Attribute [List element, Attribute filename] | zipfile_module.py:17:24:17:44 | ControlFlowNode for Attribute() [List element, Attribute filename] |
-| zipfile_module.py:17:24:17:47 | ControlFlowNode for Subscript [Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1617:23:1617:28 | ControlFlowNode for member [Attribute filename] | file:///usr/lib/python3.10/zipfile.py:1628:16:1628:54 | ControlFlowNode for Attribute() | zipfile_module.py:17:5:17:58 | ControlFlowNode for Attribute() |
#select
-| Other_compresstion_modules.py:9:27:9:36 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:9:27:9:36 | ControlFlowNode for bomb_input | This file extraction depends on a $@. | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | potentially untrusted source |
-| Other_compresstion_modules.py:11:15:11:24 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:11:15:11:24 | ControlFlowNode for bomb_input | This file extraction depends on a $@. | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | potentially untrusted source |
-| Other_compresstion_modules.py:12:19:12:28 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:12:19:12:28 | ControlFlowNode for bomb_input | This file extraction depends on a $@. | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | potentially untrusted source |
-| Other_compresstion_modules.py:14:15:14:24 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:14:15:14:24 | ControlFlowNode for bomb_input | This file extraction depends on a $@. | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | potentially untrusted source |
-| Other_compresstion_modules.py:15:19:15:28 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:15:19:15:28 | ControlFlowNode for bomb_input | This file extraction depends on a $@. | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | potentially untrusted source |
-| Other_compresstion_modules.py:17:20:17:29 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:17:20:17:29 | ControlFlowNode for bomb_input | This file extraction depends on a $@. | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | potentially untrusted source |
-| Other_compresstion_modules.py:23:14:23:23 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:23:14:23:23 | ControlFlowNode for bomb_input | This file extraction depends on a $@. | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | potentially untrusted source |
-| Other_compresstion_modules.py:24:17:24:26 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:24:17:24:26 | ControlFlowNode for bomb_input | This file extraction depends on a $@. | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | potentially untrusted source |
-| Other_compresstion_modules.py:27:40:27:49 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:27:40:27:49 | ControlFlowNode for bomb_input | This file extraction depends on a $@. | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | potentially untrusted source |
-| Other_compresstion_modules.py:29:23:29:32 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:29:23:29:32 | ControlFlowNode for bomb_input | This file extraction depends on a $@. | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | potentially untrusted source |
-| Other_compresstion_modules.py:30:21:30:30 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:30:21:30:30 | ControlFlowNode for bomb_input | This file extraction depends on a $@. | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | potentially untrusted source |
-| Other_compresstion_modules.py:32:40:32:49 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:32:40:32:49 | ControlFlowNode for bomb_input | This file extraction depends on a $@. | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | potentially untrusted source |
-| Other_compresstion_modules.py:33:22:33:31 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:33:22:33:31 | ControlFlowNode for bomb_input | This file extraction depends on a $@. | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | potentially untrusted source |
-| Other_compresstion_modules.py:34:21:34:30 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:34:21:34:30 | ControlFlowNode for bomb_input | This file extraction depends on a $@. | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | potentially untrusted source |
-| Other_compresstion_modules.py:35:42:35:51 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:35:42:35:51 | ControlFlowNode for bomb_input | This file extraction depends on a $@. | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | potentially untrusted source |
-| Other_compresstion_modules.py:36:23:36:32 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:36:23:36:32 | ControlFlowNode for bomb_input | This file extraction depends on a $@. | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | potentially untrusted source |
-| Other_compresstion_modules.py:37:36:37:45 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | Other_compresstion_modules.py:37:36:37:45 | ControlFlowNode for bomb_input | This file extraction depends on a $@. | Other_compresstion_modules.py:52:17:52:26 | ControlFlowNode for bomb_input | potentially untrusted source |
-| tarfile_module.py:8:5:8:54 | ControlFlowNode for Attribute() | tarfile_module.py:29:19:29:24 | ControlFlowNode for bar_id | tarfile_module.py:8:5:8:54 | ControlFlowNode for Attribute() | This file extraction depends on a $@. | tarfile_module.py:29:19:29:24 | ControlFlowNode for bar_id | potentially untrusted source |
-| tarfile_module.py:9:5:9:54 | ControlFlowNode for Attribute() | tarfile_module.py:29:19:29:24 | ControlFlowNode for bar_id | tarfile_module.py:9:5:9:54 | ControlFlowNode for Attribute() | This file extraction depends on a $@. | tarfile_module.py:29:19:29:24 | ControlFlowNode for bar_id | potentially untrusted source |
-| tarfile_module.py:10:5:10:56 | ControlFlowNode for Attribute() | tarfile_module.py:29:19:29:24 | ControlFlowNode for bar_id | tarfile_module.py:10:5:10:56 | ControlFlowNode for Attribute() | This file extraction depends on a $@. | tarfile_module.py:29:19:29:24 | ControlFlowNode for bar_id | potentially untrusted source |
-| tarfile_module.py:11:5:11:49 | ControlFlowNode for Attribute() | tarfile_module.py:29:19:29:24 | ControlFlowNode for bar_id | tarfile_module.py:11:5:11:49 | ControlFlowNode for Attribute() | This file extraction depends on a $@. | tarfile_module.py:29:19:29:24 | ControlFlowNode for bar_id | potentially untrusted source |
-| tarfile_module.py:12:5:12:59 | ControlFlowNode for Attribute() | tarfile_module.py:29:19:29:24 | ControlFlowNode for bar_id | tarfile_module.py:12:5:12:59 | ControlFlowNode for Attribute() | This file extraction depends on a $@. | tarfile_module.py:29:19:29:24 | ControlFlowNode for bar_id | potentially untrusted source |
-| tarfile_module.py:13:5:13:51 | ControlFlowNode for Attribute() | tarfile_module.py:29:19:29:24 | ControlFlowNode for bar_id | tarfile_module.py:13:5:13:51 | ControlFlowNode for Attribute() | This file extraction depends on a $@. | tarfile_module.py:29:19:29:24 | ControlFlowNode for bar_id | potentially untrusted source |
-| zipfile_module.py:11:5:11:57 | ControlFlowNode for Attribute() | zipfile_module.py:89:19:89:24 | ControlFlowNode for bar_id | zipfile_module.py:11:5:11:57 | ControlFlowNode for Attribute() | This file extraction depends on a $@. | zipfile_module.py:89:19:89:24 | ControlFlowNode for bar_id | potentially untrusted source |
-| zipfile_module.py:15:5:15:38 | ControlFlowNode for Attribute() | zipfile_module.py:89:19:89:24 | ControlFlowNode for bar_id | zipfile_module.py:15:5:15:38 | ControlFlowNode for Attribute() | This file extraction depends on a $@. | zipfile_module.py:89:19:89:24 | ControlFlowNode for bar_id | potentially untrusted source |
-| zipfile_module.py:17:5:17:58 | ControlFlowNode for Attribute() | zipfile_module.py:89:19:89:24 | ControlFlowNode for bar_id | zipfile_module.py:17:5:17:58 | ControlFlowNode for Attribute() | This file extraction depends on a $@. | zipfile_module.py:89:19:89:24 | ControlFlowNode for bar_id | potentially untrusted source |
-| zipfile_module.py:30:5:30:35 | ControlFlowNode for Attribute() | zipfile_module.py:89:19:89:24 | ControlFlowNode for bar_id | zipfile_module.py:30:5:30:35 | ControlFlowNode for Attribute() | This file extraction depends on a $@. | zipfile_module.py:89:19:89:24 | ControlFlowNode for bar_id | potentially untrusted source |
-| zipfile_module.py:37:14:37:29 | ControlFlowNode for Attribute() | zipfile_module.py:89:19:89:24 | ControlFlowNode for bar_id | zipfile_module.py:37:14:37:29 | ControlFlowNode for Attribute() | This file extraction depends on a $@. | zipfile_module.py:89:19:89:24 | ControlFlowNode for bar_id | potentially untrusted source |
-| zipfile_module.py:42:5:42:54 | ControlFlowNode for Attribute() | zipfile_module.py:89:19:89:24 | ControlFlowNode for bar_id | zipfile_module.py:42:5:42:54 | ControlFlowNode for Attribute() | This file extraction depends on a $@. | zipfile_module.py:89:19:89:24 | ControlFlowNode for bar_id | potentially untrusted source |
-| zipfile_module.py:45:14:45:39 | ControlFlowNode for Attribute() | zipfile_module.py:89:19:89:24 | ControlFlowNode for bar_id | zipfile_module.py:45:14:45:39 | ControlFlowNode for Attribute() | This file extraction depends on a $@. | zipfile_module.py:89:19:89:24 | ControlFlowNode for bar_id | potentially untrusted source |
-| zipfile_module.py:53:5:53:35 | ControlFlowNode for Attribute() | zipfile_module.py:89:19:89:24 | ControlFlowNode for bar_id | zipfile_module.py:53:5:53:35 | ControlFlowNode for Attribute() | This file extraction depends on a $@. | zipfile_module.py:89:19:89:24 | ControlFlowNode for bar_id | potentially untrusted source |
-| zipfile_module.py:62:44:62:61 | ControlFlowNode for Attribute() | zipfile_module.py:89:19:89:24 | ControlFlowNode for bar_id | zipfile_module.py:62:44:62:61 | ControlFlowNode for Attribute() | This file extraction depends on a $@. | zipfile_module.py:89:19:89:24 | ControlFlowNode for bar_id | potentially untrusted source |
-| zipfile_module.py:100:14:100:29 | ControlFlowNode for Attribute() | zipfile_module.py:99:26:99:34 | ControlFlowNode for Attribute | zipfile_module.py:100:14:100:29 | ControlFlowNode for Attribute() | This file extraction depends on a $@. | zipfile_module.py:99:26:99:34 | ControlFlowNode for Attribute | potentially untrusted source |
-| zipfile_module.py:108:14:108:29 | ControlFlowNode for Attribute() | zipfile_module.py:106:23:106:26 | ControlFlowNode for file | zipfile_module.py:108:14:108:29 | ControlFlowNode for Attribute() | This file extraction depends on a $@. | zipfile_module.py:106:23:106:26 | ControlFlowNode for file | potentially untrusted source |
-| zipfile_module.py:117:18:117:33 | ControlFlowNode for Attribute() | zipfile_module.py:116:30:116:38 | ControlFlowNode for Attribute | zipfile_module.py:117:18:117:33 | ControlFlowNode for Attribute() | This file extraction depends on a $@. | zipfile_module.py:116:30:116:38 | ControlFlowNode for Attribute | potentially untrusted source |
-| zipfile_module.py:126:18:126:33 | ControlFlowNode for Attribute() | zipfile_module.py:125:30:125:38 | ControlFlowNode for Attribute | zipfile_module.py:126:18:126:33 | ControlFlowNode for Attribute() | This file extraction depends on a $@. | zipfile_module.py:125:30:125:38 | ControlFlowNode for Attribute | potentially untrusted source |
+| file:///usr/lib/python3.10/tarfile.py:1863:32:1863:35 | ControlFlowNode for name | test.py:9:16:9:24 | ControlFlowNode for file_path | file:///usr/lib/python3.10/tarfile.py:1863:32:1863:35 | ControlFlowNode for name | This uncontrolled file extraction is $@. | test.py:9:16:9:24 | ControlFlowNode for file_path | depends on this user controlled data |
+| file:///usr/lib/python3.10/tarfile.py:1923:28:1923:42 | ControlFlowNode for BoolExpr | test.py:9:16:9:24 | ControlFlowNode for file_path | file:///usr/lib/python3.10/tarfile.py:1923:28:1923:42 | ControlFlowNode for BoolExpr | This uncontrolled file extraction is $@. | test.py:9:16:9:24 | ControlFlowNode for file_path | depends on this user controlled data |
+| file:///usr/lib/python3.10/tarfile.py:2373:24:2373:72 | ControlFlowNode for Attribute() | test.py:9:16:9:24 | ControlFlowNode for file_path | file:///usr/lib/python3.10/tarfile.py:2373:24:2373:72 | ControlFlowNode for Attribute() | This uncontrolled file extraction is $@. | test.py:9:16:9:24 | ControlFlowNode for file_path | depends on this user controlled data |
+| file:///usr/lib/python3.10/zipfile.py:1477:14:1477:38 | ControlFlowNode for Attribute() | test.py:9:16:9:24 | ControlFlowNode for file_path | file:///usr/lib/python3.10/zipfile.py:1477:14:1477:38 | ControlFlowNode for Attribute() | This uncontrolled file extraction is $@. | test.py:9:16:9:24 | ControlFlowNode for file_path | depends on this user controlled data |
+| file:///usr/lib/python3.10/zipfile.py:1700:14:1700:39 | ControlFlowNode for Attribute() | test.py:9:16:9:24 | ControlFlowNode for file_path | file:///usr/lib/python3.10/zipfile.py:1700:14:1700:39 | ControlFlowNode for Attribute() | This uncontrolled file extraction is $@. | test.py:9:16:9:24 | ControlFlowNode for file_path | depends on this user controlled data |
+| test.py:10:5:10:52 | ControlFlowNode for Attribute() | test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:10:5:10:52 | ControlFlowNode for Attribute() | This uncontrolled file extraction is $@. | test.py:9:16:9:24 | ControlFlowNode for file_path | depends on this user controlled data |
+| test.py:11:5:11:48 | ControlFlowNode for Attribute() | test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:11:5:11:48 | ControlFlowNode for Attribute() | This uncontrolled file extraction is $@. | test.py:9:16:9:24 | ControlFlowNode for file_path | depends on this user controlled data |
+| test.py:14:14:14:29 | ControlFlowNode for Attribute() | test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:14:14:14:29 | ControlFlowNode for Attribute() | This uncontrolled file extraction is $@. | test.py:9:16:9:24 | ControlFlowNode for file_path | depends on this user controlled data |
+| test.py:21:5:21:60 | ControlFlowNode for Attribute() | test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:21:5:21:60 | ControlFlowNode for Attribute() | This uncontrolled file extraction is $@. | test.py:9:16:9:24 | ControlFlowNode for file_path | depends on this user controlled data |
+| test.py:23:5:23:52 | ControlFlowNode for Attribute() | test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:23:5:23:52 | ControlFlowNode for Attribute() | This uncontrolled file extraction is $@. | test.py:9:16:9:24 | ControlFlowNode for file_path | depends on this user controlled data |
+| test.py:24:5:24:55 | ControlFlowNode for Attribute() | test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:24:5:24:55 | ControlFlowNode for Attribute() | This uncontrolled file extraction is $@. | test.py:9:16:9:24 | ControlFlowNode for file_path | depends on this user controlled data |
+| test.py:25:5:25:57 | ControlFlowNode for Attribute() | test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:25:5:25:57 | ControlFlowNode for Attribute() | This uncontrolled file extraction is $@. | test.py:9:16:9:24 | ControlFlowNode for file_path | depends on this user controlled data |
+| test.py:26:5:26:50 | ControlFlowNode for Attribute() | test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:26:5:26:50 | ControlFlowNode for Attribute() | This uncontrolled file extraction is $@. | test.py:9:16:9:24 | ControlFlowNode for file_path | depends on this user controlled data |
+| test.py:27:5:27:60 | ControlFlowNode for Attribute() | test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:27:5:27:60 | ControlFlowNode for Attribute() | This uncontrolled file extraction is $@. | test.py:9:16:9:24 | ControlFlowNode for file_path | depends on this user controlled data |
+| test.py:34:27:34:35 | ControlFlowNode for file_path | test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:34:27:34:35 | ControlFlowNode for file_path | This uncontrolled file extraction is $@. | test.py:9:16:9:24 | ControlFlowNode for file_path | depends on this user controlled data |
+| test.py:38:15:38:23 | ControlFlowNode for file_path | test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:38:15:38:23 | ControlFlowNode for file_path | This uncontrolled file extraction is $@. | test.py:9:16:9:24 | ControlFlowNode for file_path | depends on this user controlled data |
+| test.py:39:19:39:27 | ControlFlowNode for file_path | test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:39:19:39:27 | ControlFlowNode for file_path | This uncontrolled file extraction is $@. | test.py:9:16:9:24 | ControlFlowNode for file_path | depends on this user controlled data |
+| test.py:43:14:43:22 | ControlFlowNode for file_path | test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:43:14:43:22 | ControlFlowNode for file_path | This uncontrolled file extraction is $@. | test.py:9:16:9:24 | ControlFlowNode for file_path | depends on this user controlled data |
+| test.py:44:17:44:25 | ControlFlowNode for file_path | test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:44:17:44:25 | ControlFlowNode for file_path | This uncontrolled file extraction is $@. | test.py:9:16:9:24 | ControlFlowNode for file_path | depends on this user controlled data |
+| test.py:48:15:48:23 | ControlFlowNode for file_path | test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:48:15:48:23 | ControlFlowNode for file_path | This uncontrolled file extraction is $@. | test.py:9:16:9:24 | ControlFlowNode for file_path | depends on this user controlled data |
+| test.py:49:19:49:27 | ControlFlowNode for file_path | test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:49:19:49:27 | ControlFlowNode for file_path | This uncontrolled file extraction is $@. | test.py:9:16:9:24 | ControlFlowNode for file_path | depends on this user controlled data |
+| test.py:53:40:53:48 | ControlFlowNode for file_path | test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:53:40:53:48 | ControlFlowNode for file_path | This uncontrolled file extraction is $@. | test.py:9:16:9:24 | ControlFlowNode for file_path | depends on this user controlled data |
+| test.py:55:23:55:31 | ControlFlowNode for file_path | test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:55:23:55:31 | ControlFlowNode for file_path | This uncontrolled file extraction is $@. | test.py:9:16:9:24 | ControlFlowNode for file_path | depends on this user controlled data |
+| test.py:56:21:56:29 | ControlFlowNode for file_path | test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:56:21:56:29 | ControlFlowNode for file_path | This uncontrolled file extraction is $@. | test.py:9:16:9:24 | ControlFlowNode for file_path | depends on this user controlled data |
+| test.py:58:40:58:48 | ControlFlowNode for file_path | test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:58:40:58:48 | ControlFlowNode for file_path | This uncontrolled file extraction is $@. | test.py:9:16:9:24 | ControlFlowNode for file_path | depends on this user controlled data |
+| test.py:59:22:59:30 | ControlFlowNode for file_path | test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:59:22:59:30 | ControlFlowNode for file_path | This uncontrolled file extraction is $@. | test.py:9:16:9:24 | ControlFlowNode for file_path | depends on this user controlled data |
+| test.py:60:21:60:29 | ControlFlowNode for file_path | test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:60:21:60:29 | ControlFlowNode for file_path | This uncontrolled file extraction is $@. | test.py:9:16:9:24 | ControlFlowNode for file_path | depends on this user controlled data |
+| test.py:61:42:61:50 | ControlFlowNode for file_path | test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:61:42:61:50 | ControlFlowNode for file_path | This uncontrolled file extraction is $@. | test.py:9:16:9:24 | ControlFlowNode for file_path | depends on this user controlled data |
+| test.py:62:23:62:31 | ControlFlowNode for file_path | test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:62:23:62:31 | ControlFlowNode for file_path | This uncontrolled file extraction is $@. | test.py:9:16:9:24 | ControlFlowNode for file_path | depends on this user controlled data |
+| test.py:63:36:63:44 | ControlFlowNode for file_path | test.py:9:16:9:24 | ControlFlowNode for file_path | test.py:63:36:63:44 | ControlFlowNode for file_path | This uncontrolled file extraction is $@. | test.py:9:16:9:24 | ControlFlowNode for file_path | depends on this user controlled data |
diff --git a/python/ql/test/experimental/query-tests/Security/CWE-409/Other_compresstion_modules.py b/python/ql/test/experimental/query-tests/Security/CWE-409/Other_compresstion_modules.py
deleted file mode 100644
index 0c5e51d24ca..00000000000
--- a/python/ql/test/experimental/query-tests/Security/CWE-409/Other_compresstion_modules.py
+++ /dev/null
@@ -1,54 +0,0 @@
-import io
-from fastapi import FastAPI
-
-app = FastAPI()
-
-
-def bomb(bomb_input):
- import shutil
- shutil.unpack_archive(bomb_input)
- import lzma
- lzma.open(bomb_input)
- lzma.LZMAFile(bomb_input).read()
- import gzip
- gzip.open(bomb_input)
- gzip.GzipFile(bomb_input).read()
-
- with gzip.open(bomb_input, 'rb') as ip:
- with io.TextIOWrapper(ip, encoding='utf-8') as decoder:
- content = decoder.read()
- print(content)
-
- import bz2
- bz2.open(bomb_input)
- bz2.BZ2File(bomb_input).read()
-
- import pandas
- pandas.read_csv(filepath_or_buffer=bomb_input)
-
- pandas.read_table(bomb_input, compression='gzip')
- pandas.read_xml(bomb_input, compression='gzip')
-
- pandas.read_csv(filepath_or_buffer=bomb_input, compression='gzip')
- pandas.read_json(bomb_input, compression='gzip')
- pandas.read_sas(bomb_input, compression='gzip')
- pandas.read_stata(filepath_or_buffer=bomb_input, compression='gzip')
- pandas.read_table(bomb_input, compression='gzip')
- pandas.read_xml(path_or_buffer=bomb_input, compression='gzip')
-
- # no compression no DOS
- pandas.read_table(bomb_input, compression='tar')
- pandas.read_xml(bomb_input, compression='tar')
-
- pandas.read_csv(filepath_or_buffer=bomb_input, compression='tar')
- pandas.read_json(bomb_input, compression='tar')
- pandas.read_sas(bomb_input, compression='tar')
- pandas.read_stata(filepath_or_buffer=bomb_input, compression='tar')
- pandas.read_table(bomb_input, compression='tar')
- pandas.read_xml(path_or_buffer=bomb_input, compression='tar')
-
-
-@app.post("/bomb")
-async def bombs(bomb_input):
- bomb(bomb_input)
- return {"message": "non-async"}
diff --git a/python/ql/test/experimental/query-tests/Security/CWE-409/tarfile_module.py b/python/ql/test/experimental/query-tests/Security/CWE-409/tarfile_module.py
deleted file mode 100644
index cecfb8ee4d3..00000000000
--- a/python/ql/test/experimental/query-tests/Security/CWE-409/tarfile_module.py
+++ /dev/null
@@ -1,31 +0,0 @@
-import tarfile
-from fastapi import FastAPI
-
-
-def tar_extract(tar_path):
- tarfile.open(tar_path)
- tarfile.open(tar_path)
- tarfile.TarFile.open(tar_path).extract("somefile")
- tarfile.TarFile.open(tar_path).extract("somefile")
- tarfile.TarFile.xzopen(tar_path).extract("somefile")
- tarfile.TarFile.gzopen(tar_path).extractall()
- tarfile.TarFile.open(tar_path).extractfile("file1.txt")
- tarfile.open(tar_path).extractfile("file1.txt")
- # not working
- tarInstance2 = tarfile.TarFile(tar_path)
- tarInstance2.extractfile("file1.txt")
- tarInstance2.extract("file1.txt")
- tarfile.TarFile().open(tar_path)
- # good
- tarfile.open(tar_path, mode="w")
- tarfile.TarFile.gzopen(tar_path, mode="w")
- tarfile.TarFile.open(tar_path, mode="r:")
-
-
-app = FastAPI()
-
-
-@app.post("/zipbomb")
-async def zipbomb(bar_id):
- tar_extract(bar_id)
- return {"message": "non-async"}
diff --git a/python/ql/test/experimental/query-tests/Security/CWE-409/test.py b/python/ql/test/experimental/query-tests/Security/CWE-409/test.py
new file mode 100644
index 00000000000..848c2123db8
--- /dev/null
+++ b/python/ql/test/experimental/query-tests/Security/CWE-409/test.py
@@ -0,0 +1,76 @@
+import tarfile
+import zipfile
+from fastapi import FastAPI
+
+app = FastAPI()
+
+
+@app.post("/bomb")
+async def bomb(file_path):
+ zipfile.ZipFile(file_path, "r").extract("file1")
+ zipfile.ZipFile(file_path, "r").extractall()
+
+ with zipfile.ZipFile(file_path) as myzip:
+ with myzip.open('ZZ') as myfile:
+ a = myfile.readline()
+
+ with zipfile.ZipFile(file_path) as myzip:
+ with myzip.open('ZZ', mode="w") as myfile:
+ myfile.write(b"tmpppp")
+
+ zipfile.ZipFile(file_path).read("aFileNameInTheZipFile")
+
+ tarfile.open(file_path).extractfile("file1.txt")
+ tarfile.TarFile.open(file_path).extract("somefile")
+ tarfile.TarFile.xzopen(file_path).extract("somefile")
+ tarfile.TarFile.gzopen(file_path).extractall()
+ tarfile.TarFile.open(file_path).extractfile("file1.txt")
+
+ tarfile.open(file_path, mode="w")
+ tarfile.TarFile.gzopen(file_path, mode="w")
+ tarfile.TarFile.open(file_path, mode="r:")
+ import shutil
+
+ shutil.unpack_archive(file_path)
+
+ import lzma
+
+ lzma.open(file_path)
+ lzma.LZMAFile(file_path).read()
+
+ import bz2
+
+ bz2.open(file_path)
+ bz2.BZ2File(file_path).read()
+
+ import gzip
+
+ gzip.open(file_path)
+ gzip.GzipFile(file_path)
+
+ import pandas
+
+ pandas.read_csv(filepath_or_buffer=file_path)
+
+ pandas.read_table(file_path, compression='gzip')
+ pandas.read_xml(file_path, compression='gzip')
+
+ pandas.read_csv(filepath_or_buffer=file_path, compression='gzip')
+ pandas.read_json(file_path, compression='gzip')
+ pandas.read_sas(file_path, compression='gzip')
+ pandas.read_stata(filepath_or_buffer=file_path, compression='gzip')
+ pandas.read_table(file_path, compression='gzip')
+ pandas.read_xml(path_or_buffer=file_path, compression='gzip')
+
+ # no compression no DOS
+ pandas.read_table(file_path, compression='tar')
+ pandas.read_xml(file_path, compression='tar')
+
+ pandas.read_csv(filepath_or_buffer=file_path, compression='tar')
+ pandas.read_json(file_path, compression='tar')
+ pandas.read_sas(file_path, compression='tar')
+ pandas.read_stata(filepath_or_buffer=file_path, compression='tar')
+ pandas.read_table(file_path, compression='tar')
+ pandas.read_xml(path_or_buffer=file_path, compression='tar')
+
+ return {"message": "bomb"}
diff --git a/python/ql/test/experimental/query-tests/Security/CWE-409/zipfile_module.py b/python/ql/test/experimental/query-tests/Security/CWE-409/zipfile_module.py
deleted file mode 100644
index ef235ba82fb..00000000000
--- a/python/ql/test/experimental/query-tests/Security/CWE-409/zipfile_module.py
+++ /dev/null
@@ -1,128 +0,0 @@
-import zipfile
-from io import BytesIO
-from urllib import request
-import requests
-from fastapi import FastAPI, File, UploadFile
-from typing import List, Annotated
-
-
-def zip_extract(zip_path):
- # we can have PyZipFile instead of ZipFile too
- zipfile.ZipFile(zip_path, "r").extract("0", "./tmp/")
- zipFileObj = zipfile.ZipFile(zip_path, "r")
- zipFileObj2 = zipFileObj
- # zipfile.ZipFile("user_input").extract(member="file_name") Consume a lot of CPU,Storage
- zipFileObj2.extract("0", "./tmp/")
- # zipfile.ZipFile("user_input").extract(member=ZipInfo_object) Consume a lot of CPU,Storage
- zipFileObj.extract(zipFileObj.infolist()[0], "./tmp/")
-
-
-def zip_extractall(zip_path):
- import httpx
- filex = httpx.get(zip_path).read()
- zipfile.ZipFile(BytesIO(filex), "r").extractall()
- zipFileObj = zipfile.ZipFile(zip_path, "r")
- for infolist in zipFileObj.infolist():
- Decompression_ratio = infolist.file_size / infolist.compress_size
- if Decompression_ratio > 10:
- return
- # zipfile.ZipFile("user_input").extractall() Consume a lot of CPU,Storage
- zipFileObj.extractall("./tmp/") # Sensitive
- zipFileObj.close()
-
-
-def zip_read_obj(zip_path):
- # zipfile.ZipFile("user_input").open("file_name").read() Consume a lot of CPU,RAM,Storage
- with zipfile.ZipFile(zip_path) as myzip:
- with myzip.open('ZZ') as myfile:
- a = myfile.readline()
-
- file = requests.get(zip_path)
- zipfile.ZipFile(BytesIO(file.raw), "w").open("aa")
- zipfile.ZipFile(BytesIO(file.raw), "r").open("aa")
-
- with zipfile.ZipFile(zip_path) as myzip:
- with myzip.open('ZZ', mode="r") as myfile:
- a = next(myfile)
-
- with zipfile.ZipFile(zip_path) as myzip:
- with myzip.open('ZZ', mode="w") as myfile:
- a = myfile.write(b"tmpppp")
-
- myzip = zipfile.PyZipFile(zip_path)
- myzip.read(myzip.infolist()[0])
-
- zipfile.ZipFile(BytesIO(request.urlopen(zip_path).read())
- ).read("aFileNameInTheZipFile")
-
- with zipfile.ZipFile(zip_path, 'w') as dst_zip:
- with zipfile.ZipFile(zip_path) as src_zip:
- for info in src_zip.infolist():
- if info.filename.startswith('classes'):
- dst_zip.writestr(info, src_zip.read(info))
-
-
-def safeUnzip(zipFileName):
- buffer_size = 2 ** 10 * 8
- total_size = 0
- SIZE_THRESHOLD = 2 ** 10 * 100
- with zipfile.ZipFile(zipFileName) as myzip:
- for fileinfo in myzip.infolist():
- content = b''
- with myzip.open(fileinfo.filename, mode="r") as myfile:
- chunk = myfile.read(buffer_size)
- while chunk:
- chunk = myfile.read(buffer_size)
- total_size += buffer_size
- if total_size > SIZE_THRESHOLD:
- print("Bomb detected")
- return
- content += chunk
- print(bytes.decode(content, 'utf-8'))
- return "No Bombs!"
-
-
-app = FastAPI()
-
-
-@app.post("/zipbomb")
-async def zipbomb(bar_id):
- zip_extractall(bar_id)
- zip_extract(bar_id)
- zip_read_obj(bar_id)
- safeUnzip(bar_id)
- return {"message": "non-async"}
-
-
-@app.post("/zipbomb2")
-async def zipbomb2(file: UploadFile):
- with zipfile.ZipFile(file.file) as myzip:
- with myzip.open('ZZ') as myfile:
- a = myfile.readline()
- return {"message": "non-async"}
-
-
-@app.post("/zipbomb2")
-async def create_file(file: Annotated[bytes | None, File()] = None):
- with zipfile.ZipFile(file) as myzip:
- with myzip.open('ZZ') as myfile:
- a = myfile.readline()
- return {"message": "non-async"}
-
-
-@app.post("/uploadMultipleFiles")
-def upload(files: List[UploadFile] = File(...)):
- for file in files:
- with zipfile.ZipFile(file.file) as myzip:
- with myzip.open('ZZ') as myfile:
- a = myfile.readline()
- return {"message": "non-async"}
-
-
-@app.post("/files/")
-async def create_files(files: Annotated[list[bytes], File()]):
- for file in files:
- with zipfile.ZipFile(file.file) as myzip:
- with myzip.open('ZZ') as myfile:
- a = myfile.readline()
- return {"file_sizes": [len(file) for file in files]}
From 6739750d2abd0b6d0f51546e668d1ecbc89c653d Mon Sep 17 00:00:00 2001
From: Maiky <76447395+maikypedia@users.noreply.github.com>
Date: Thu, 23 Nov 2023 12:48:33 +0100
Subject: [PATCH 012/378] Add Unsafe Unpacking Query (CWE-022)
---
.../swift/security/UnsafeUnpackExtensions.qll | 78 +++++++++++++++++++
.../swift/security/UnsafeUnpackQuery.qll | 33 ++++++++
.../Security/CWE-022/UnsafeUnpack.qhelp | 43 ++++++++++
.../Security/CWE-022/UnsafeUnpack.ql | 24 ++++++
.../Security/CWE-022/ZIPFoundationBad.swift | 28 +++++++
.../Security/CWE-022/ZipArchiveGood.swift | 25 ++++++
.../Security/CWE-022/ZipBad.swift | 28 +++++++
.../UnsafeUnpack.expected | 13 ++++
.../CWE-022-Unsafe-Unpack/UnsafeUnpack.qlref | 1 +
.../CWE-022-Unsafe-Unpack/UnsafeUnpack.swift | 71 +++++++++++++++++
10 files changed, 344 insertions(+)
create mode 100644 swift/ql/lib/codeql/swift/security/UnsafeUnpackExtensions.qll
create mode 100644 swift/ql/lib/codeql/swift/security/UnsafeUnpackQuery.qll
create mode 100644 swift/ql/src/experimental/Security/CWE-022/UnsafeUnpack.qhelp
create mode 100644 swift/ql/src/experimental/Security/CWE-022/UnsafeUnpack.ql
create mode 100644 swift/ql/src/experimental/Security/CWE-022/ZIPFoundationBad.swift
create mode 100644 swift/ql/src/experimental/Security/CWE-022/ZipArchiveGood.swift
create mode 100644 swift/ql/src/experimental/Security/CWE-022/ZipBad.swift
create mode 100644 swift/ql/test/query-tests/Security/CWE-022-Unsafe-Unpack/UnsafeUnpack.expected
create mode 100644 swift/ql/test/query-tests/Security/CWE-022-Unsafe-Unpack/UnsafeUnpack.qlref
create mode 100644 swift/ql/test/query-tests/Security/CWE-022-Unsafe-Unpack/UnsafeUnpack.swift
diff --git a/swift/ql/lib/codeql/swift/security/UnsafeUnpackExtensions.qll b/swift/ql/lib/codeql/swift/security/UnsafeUnpackExtensions.qll
new file mode 100644
index 00000000000..c102aa40a1e
--- /dev/null
+++ b/swift/ql/lib/codeql/swift/security/UnsafeUnpackExtensions.qll
@@ -0,0 +1,78 @@
+/**
+ * Provides default sources, sinks and sanitizers for reasoning about
+ * unsafe unpack vulnerabilities, as well as extension points for
+ * adding your own.
+ */
+
+import swift
+import codeql.swift.dataflow.DataFlow
+import codeql.swift.dataflow.ExternalFlow
+
+/**
+ * A dataflow source for unsafe unpack vulnerabilities.
+ */
+abstract class UnsafeUnpackSource extends DataFlow::Node { }
+
+/**
+ * A dataflow sink for unsafe unpack vulnerabilities.
+ */
+abstract class UnsafeUnpackSink extends DataFlow::Node { }
+
+/**
+ * A barrier for unsafe unpack vulnerabilities.
+ */
+abstract class UnsafeUnpackBarrier extends DataFlow::Node { }
+
+/**
+ * A unit class for adding additional flow steps.
+ */
+class UnsafeUnpackAdditionalFlowStep extends Unit {
+ /**
+ * Holds if the step from `node1` to `node2` should be considered a flow
+ * step for paths related to unsafe unpack vulnerabilities.
+ */
+ abstract predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo);
+}
+
+/**
+ * A sink defined in a CSV model.
+ */
+private class DefaultUnsafeUnpackSink extends UnsafeUnpackSink {
+ DefaultUnsafeUnpackSink() { sinkNode(this, "unsafe-unpack") }
+}
+
+private class UnsafeUnpackSinks extends SinkModelCsv {
+ override predicate row(string row) {
+ row =
+ [
+ ";Zip;true;unzipFile(_:destination:overwrite:password:progress:fileOutputHandler:);;;Argument[0];unsafe-unpack",
+ ";FileManager;true;unzipItem(at:to:skipCRC32:progress:pathEncoding:);;;Argument[0];unsafe-unpack",
+ ]
+ }
+}
+
+/**
+ * An additional taint step for unsafe unpack vulnerabilities.
+ */
+private class UnsafeUnpackAdditionalDataFlowStep extends UnsafeUnpackAdditionalFlowStep {
+ override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
+ exists(CallExpr initCall, CallExpr call |
+ // If a zip file is remotely downloaded the destination path is tainted
+ call.getStaticTarget().(Method).hasQualifiedName("Data", "write(to:options:)") and
+ call.getQualifier() = initCall and
+ initCall.getStaticTarget().(Initializer).getEnclosingDecl().(TypeDecl).getName() = "Data" and
+ nodeFrom.asExpr() = initCall and
+ nodeTo.asExpr() = call.getAnArgument().getExpr()
+ )
+ }
+}
+
+/**
+ * A barrier for unsafe unpack vulnerabilities.
+ */
+private class UnsafeUnpackDefaultBarrier extends UnsafeUnpackBarrier {
+ UnsafeUnpackDefaultBarrier() {
+ // any numeric type
+ this.asExpr().getType().getUnderlyingType().getABaseType*().getName() = "Numeric"
+ }
+}
diff --git a/swift/ql/lib/codeql/swift/security/UnsafeUnpackQuery.qll b/swift/ql/lib/codeql/swift/security/UnsafeUnpackQuery.qll
new file mode 100644
index 00000000000..dbc0f733b52
--- /dev/null
+++ b/swift/ql/lib/codeql/swift/security/UnsafeUnpackQuery.qll
@@ -0,0 +1,33 @@
+/**
+ * Provides default sources, sinks and sanitizers for reasoning about
+ * unsafe unpack vulnerabilities, as well as extension points for
+ * adding your own.
+ */
+
+import swift
+import codeql.swift.dataflow.DataFlow
+import codeql.swift.dataflow.TaintTracking
+import codeql.swift.dataflow.FlowSources
+import codeql.swift.security.UnsafeUnpackExtensions
+
+/**
+ * A taint configuration for tainted data that reaches a unsafe unpack sink.
+ */
+module UnsafeUnpackConfig implements DataFlow::ConfigSig {
+ predicate isSource(DataFlow::Node node) {
+ node instanceof FlowSource or node instanceof RemoteFlowSource
+ }
+
+ predicate isSink(DataFlow::Node node) { node instanceof UnsafeUnpackSink }
+
+ predicate isBarrier(DataFlow::Node barrier) { barrier instanceof UnsafeUnpackBarrier }
+
+ predicate isAdditionalFlowStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
+ any(UnsafeUnpackAdditionalFlowStep s).step(nodeFrom, nodeTo)
+ }
+}
+
+/**
+ * Detect taint flow of tainted data that reaches a unsafe unpack sink.
+ */
+module UnsafeUnpackFlow = TaintTracking::Global;
diff --git a/swift/ql/src/experimental/Security/CWE-022/UnsafeUnpack.qhelp b/swift/ql/src/experimental/Security/CWE-022/UnsafeUnpack.qhelp
new file mode 100644
index 00000000000..df162180c53
--- /dev/null
+++ b/swift/ql/src/experimental/Security/CWE-022/UnsafeUnpack.qhelp
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+Unpacking files from a malicious zip without properly validating that the destination file path
+is within the destination directory, or allowing symlinks to point to files outside the extraction directory,
+allows an attacker to extract files to arbitrary locations outside the extraction directory. This helps
+overwrite sensitive user data and, in some cases, can lead to code execution if an
+attacker overwrites an application's shared object file.
+
+
+
+
+
Consider using a safer module, such as: ZIPArchive
+
+
+
+
+The following examples unpacks a remote zip using `Zip.unzipFile()` which is vulnerable to path traversal.
+
+
+
+
+The following examples unpacks a remote zip using `fileManager.unzipItem()` which is vulnerable to symlink path traversal.
+
+
+
+
+
Consider using a safer module, such as: ZIPArchive
+
+
\ No newline at end of file
diff --git a/swift/ql/src/experimental/Security/CWE-022/UnsafeUnpack.ql b/swift/ql/src/experimental/Security/CWE-022/UnsafeUnpack.ql
new file mode 100644
index 00000000000..c50cc6c3b4f
--- /dev/null
+++ b/swift/ql/src/experimental/Security/CWE-022/UnsafeUnpack.ql
@@ -0,0 +1,24 @@
+/**
+ * @name Arbitrary file write during a zip extraction from a user controlled source
+ * @description Unpacking user controlled zips without validating if destination path file
+ * is within the destination directory can cause files outside
+ * the destination directory to be overwritten.
+ * @kind path-problem
+ * @problem.severity error
+ * @security-severity 9.8
+ * @precision high
+ * @id swift/unsafe-unpacking
+ * @tags security
+ * experimental
+ * external/cwe/cwe-022
+ */
+
+import swift
+import codeql.swift.dataflow.DataFlow
+import codeql.swift.security.UnsafeUnpackQuery
+import UnsafeUnpackFlow::PathGraph
+
+from UnsafeUnpackFlow::PathNode sourceNode, UnsafeUnpackFlow::PathNode sinkNode
+where UnsafeUnpackFlow::flowPath(sourceNode, sinkNode)
+select sinkNode.getNode(), sourceNode, sinkNode,
+ "Unsafe unpacking from a malicious zip retrieved from a remote location."
diff --git a/swift/ql/src/experimental/Security/CWE-022/ZIPFoundationBad.swift b/swift/ql/src/experimental/Security/CWE-022/ZIPFoundationBad.swift
new file mode 100644
index 00000000000..7dfd86feffd
--- /dev/null
+++ b/swift/ql/src/experimental/Security/CWE-022/ZIPFoundationBad.swift
@@ -0,0 +1,28 @@
+import Foundation
+import ZIPFoundation
+
+
+func unzipFile(at sourcePath: String, to destinationPath: String) {
+ do {
+ let remoteURL = URL(string: "https://example.com/")!
+
+ let source = URL(fileURLWithPath: sourcePath)
+ let destination = URL(fileURLWithPath: destinationPath)
+
+ // Malicious zip is downloaded
+ try Data(contentsOf: remoteURL).write(to: source)
+
+ let fileManager = FileManager()
+ // Malicious zip is unpacked
+ try fileManager.unzipItem(at:source, to: destination)
+ } catch {
+ }
+}
+
+func main() {
+ let sourcePath = "/sourcePath"
+ let destinationPath = "/destinationPath"
+ unzipFile(at: sourcePath, to: destinationPath)
+}
+
+main()
\ No newline at end of file
diff --git a/swift/ql/src/experimental/Security/CWE-022/ZipArchiveGood.swift b/swift/ql/src/experimental/Security/CWE-022/ZipArchiveGood.swift
new file mode 100644
index 00000000000..e30f17c1692
--- /dev/null
+++ b/swift/ql/src/experimental/Security/CWE-022/ZipArchiveGood.swift
@@ -0,0 +1,25 @@
+import Foundation
+import ZipArchive
+
+func unzipFile(at sourcePath: String, to destinationPath: String) {
+ do {
+ let remoteURL = URL(string: "https://example.com/")!
+
+ let source = URL(fileURLWithPath: sourcePath)
+
+ // Malicious zip is downloaded
+ try Data(contentsOf: remoteURL).write(to: source)
+
+ // ZipArchive is safe
+ try SSZipArchive.unzipFile(atPath: sourcePath, toDestination: destinationPath, delegate: self)
+ } catch {
+ }
+}
+
+func main() {
+ let sourcePath = "/sourcePath"
+ let destinationPath = "/destinationPath"
+ unzipFile(at: sourcePath, to: destinationPath)
+}
+
+main()
\ No newline at end of file
diff --git a/swift/ql/src/experimental/Security/CWE-022/ZipBad.swift b/swift/ql/src/experimental/Security/CWE-022/ZipBad.swift
new file mode 100644
index 00000000000..4a90e6b3ea8
--- /dev/null
+++ b/swift/ql/src/experimental/Security/CWE-022/ZipBad.swift
@@ -0,0 +1,28 @@
+import Foundation
+import Zip
+
+
+func unzipFile(at sourcePath: String, to destinationPath: String) {
+ do {
+ let remoteURL = URL(string: "https://example.com/")!
+
+ let source = URL(fileURLWithPath: sourcePath)
+ let destination = URL(fileURLWithPath: destinationPath)
+
+ // Malicious zip is downloaded
+ try Data(contentsOf: remoteURL).write(to: source)
+
+ let fileManager = FileManager()
+ // Malicious zip is unpacked
+ try Zip.unzipFile(source, destination: destination, overwrite: true, password: nil)
+ } catch {
+ }
+}
+
+func main() {
+ let sourcePath = "/sourcePath"
+ let destinationPath = "/destinationPath"
+ unzipFile(at: sourcePath, to: destinationPath)
+}
+
+main()
\ No newline at end of file
diff --git a/swift/ql/test/query-tests/Security/CWE-022-Unsafe-Unpack/UnsafeUnpack.expected b/swift/ql/test/query-tests/Security/CWE-022-Unsafe-Unpack/UnsafeUnpack.expected
new file mode 100644
index 00000000000..4e79ee8b503
--- /dev/null
+++ b/swift/ql/test/query-tests/Security/CWE-022-Unsafe-Unpack/UnsafeUnpack.expected
@@ -0,0 +1,13 @@
+edges
+| UnsafeUnpack.swift:60:9:60:48 | call to Data.init(contentsOf:options:) | UnsafeUnpack.swift:60:60:60:60 | source |
+| UnsafeUnpack.swift:60:60:60:60 | source | UnsafeUnpack.swift:62:27:62:27 | source |
+| UnsafeUnpack.swift:60:60:60:60 | source | UnsafeUnpack.swift:65:39:65:39 | source |
+nodes
+| UnsafeUnpack.swift:60:9:60:48 | call to Data.init(contentsOf:options:) | semmle.label | call to Data.init(contentsOf:options:) |
+| UnsafeUnpack.swift:60:60:60:60 | source | semmle.label | source |
+| UnsafeUnpack.swift:62:27:62:27 | source | semmle.label | source |
+| UnsafeUnpack.swift:65:39:65:39 | source | semmle.label | source |
+subpaths
+#select
+| UnsafeUnpack.swift:62:27:62:27 | source | UnsafeUnpack.swift:60:9:60:48 | call to Data.init(contentsOf:options:) | UnsafeUnpack.swift:62:27:62:27 | source | Unsafe unpacking from a malicious zip retrieved from a remote location. |
+| UnsafeUnpack.swift:65:39:65:39 | source | UnsafeUnpack.swift:60:9:60:48 | call to Data.init(contentsOf:options:) | UnsafeUnpack.swift:65:39:65:39 | source | Unsafe unpacking from a malicious zip retrieved from a remote location. |
diff --git a/swift/ql/test/query-tests/Security/CWE-022-Unsafe-Unpack/UnsafeUnpack.qlref b/swift/ql/test/query-tests/Security/CWE-022-Unsafe-Unpack/UnsafeUnpack.qlref
new file mode 100644
index 00000000000..1d1a5a3a84c
--- /dev/null
+++ b/swift/ql/test/query-tests/Security/CWE-022-Unsafe-Unpack/UnsafeUnpack.qlref
@@ -0,0 +1 @@
+experimental/Security/CWE-022/UnsafeUnpack.ql
\ No newline at end of file
diff --git a/swift/ql/test/query-tests/Security/CWE-022-Unsafe-Unpack/UnsafeUnpack.swift b/swift/ql/test/query-tests/Security/CWE-022-Unsafe-Unpack/UnsafeUnpack.swift
new file mode 100644
index 00000000000..2f599b89150
--- /dev/null
+++ b/swift/ql/test/query-tests/Security/CWE-022-Unsafe-Unpack/UnsafeUnpack.swift
@@ -0,0 +1,71 @@
+
+// --- stubs ---
+struct URL
+{
+ init?(string: String) {}
+ init(fileURLWithPath: String) {}
+}
+
+class Zip {
+ class func unzipFile(_ zipFilePath: URL, destination: URL, overwrite: Bool, password: String?, progress: ((_ progress: Double) -> ())? = nil, fileOutputHandler: ((_ unzippedFile: URL) -> Void)? = nil) throws {}
+}
+
+
+class NSObject {
+}
+
+class Progress : NSObject {
+
+}
+
+class FileManager : NSObject {
+ func unzipItem(at sourceURL: URL, to destinationURL: URL, skipCRC32: Bool = false,
+ progress: Progress? = nil, pathEncoding: String.Encoding? = nil) throws {}
+}
+
+protocol DataProtocol { }
+class Data : DataProtocol {
+ struct ReadingOptions : OptionSet { let rawValue: Int }
+ struct WritingOptions : OptionSet { let rawValue: Int }
+
+ init(_ elements: S) { count = 0 }
+ init(contentsOf: URL, options: ReadingOptions) { count = 0 }
+ func write(to: URL, options: Data.WritingOptions = []) {}
+
+ var count: Int
+}
+
+extension String {
+
+ struct Encoding {
+ var rawValue: UInt
+
+ init(rawValue: UInt) { self.rawValue = rawValue }
+
+ static let ascii = Encoding(rawValue: 1)
+ }
+ init(contentsOf url: URL) throws {
+ self.init("")
+ }
+}
+
+// --- tests ---
+
+func testCommandInjectionQhelpExamples() {
+ guard let remoteURL = URL(string: "https://example.com/") else {
+ return
+ }
+
+ let source = URL(fileURLWithPath: "/sourcePath")
+ let destination = URL(fileURLWithPath: "/destination")
+
+ try Data(contentsOf: remoteURL, options: []).write(to: source)
+ do {
+ try Zip.unzipFile(source, destination: destination, overwrite: true, password: nil) // BAD
+
+ let fileManager = FileManager()
+ try fileManager.unzipItem(at: source, to: destination) // BAD
+ } catch {
+ print("Error: \(error)")
+ }
+}
\ No newline at end of file
From 2d0067d6183b7964770d7e48757706d2d5b71e67 Mon Sep 17 00:00:00 2001
From: amammad <77095239+amammad@users.noreply.github.com>
Date: Thu, 7 Dec 2023 13:45:28 +0100
Subject: [PATCH 013/378] fix some qldocs, change Sink extenstion model, deduct
some not necessarily checks :)
---
.../Security/CWE-409/DecompressionBombs.ql | 58 +----
.../CWE-409/FileAndFormRemoteFlowSource.qll | 63 +++++
.../Security/CWE-409/example_good.py | 23 +-
.../python/security/DecompressionBomb.qll | 218 ++++++------------
4 files changed, 161 insertions(+), 201 deletions(-)
create mode 100644 python/ql/src/experimental/Security/CWE-409/FileAndFormRemoteFlowSource.qll
diff --git a/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql b/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql
index 8cc1e45f34a..044afff97b4 100644
--- a/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql
+++ b/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql
@@ -18,6 +18,7 @@ import semmle.python.ApiGraphs
import semmle.python.dataflow.new.RemoteFlowSources
import semmle.python.dataflow.new.internal.DataFlowPublic
import experimental.semmle.python.security.DecompressionBomb
+import FileAndFormRemoteFlowSource::FileAndFormRemoteFlowSource
/**
* `io.TextIOWrapper(ip, encoding='utf-8')` like following:
@@ -39,59 +40,12 @@ predicate isAdditionalTaintStepTextIOWrapper(DataFlow::Node nodeFrom, DataFlow::
)
}
-module FileAndFormRemoteFlowSource {
- class FastAPI extends RemoteFlowSource::Range {
- FastAPI() {
- exists(API::Node fastApiParam, Expr fastApiUploadFile |
- fastApiParam =
- API::moduleImport("fastapi")
- .getMember("FastAPI")
- .getReturn()
- .getMember("post")
- .getReturn()
- .getParameter(0)
- .getKeywordParameter(_) and
- fastApiUploadFile =
- API::moduleImport("fastapi")
- .getMember("UploadFile")
- .getASubclass*()
- .getAValueReachableFromSource()
- .asExpr()
- |
- fastApiUploadFile =
- fastApiParam.asSource().asExpr().(Parameter).getAnnotation().getASubExpression*() and
- // Multiple Uploaded files as list of fastapi.UploadFile
- exists(For f, Attribute attr |
- fastApiParam.getAValueReachableFromSource().asExpr() = f.getIter().getASubExpression*()
- |
- TaintTracking::localExprTaint(f.getIter(), attr.getObject()) and
- attr.getName() = ["filename", "content_type", "headers", "file", "read"] and
- this.asExpr() = attr
- )
- or
- // one Uploaded file as fastapi.UploadFile
- this =
- [
- fastApiParam.getMember(["filename", "content_type", "headers"]).asSource(),
- fastApiParam
- .getMember("file")
- .getMember(["readlines", "readline", "read"])
- .getReturn()
- .asSource(), fastApiParam.getMember("read").getReturn().asSource()
- ]
- )
- }
-
- override string getSourceType() { result = "fastapi HTTP FORM files" }
- }
-}
-
module BombsConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
(
source instanceof RemoteFlowSource
or
- source instanceof FileAndFormRemoteFlowSource::FastAPI
+ source instanceof FastAPI
) and
not source.getLocation().getFile().inStdlib() and
not source.getLocation().getFile().getRelativePath().matches("%venv%")
@@ -113,11 +67,11 @@ module BombsConfig implements DataFlow::ConfigSig {
}
}
-module Bombs = TaintTracking::Global;
+module BombsFlow = TaintTracking::Global;
-import Bombs::PathGraph
+import BombsFlow::PathGraph
-from Bombs::PathNode source, Bombs::PathNode sink
-where Bombs::flowPath(source, sink)
+from BombsFlow::PathNode source, BombsFlow::PathNode sink
+where BombsFlow::flowPath(source, sink)
select sink.getNode(), source, sink, "This uncontrolled file extraction is $@.", source.getNode(),
"depends on this user controlled data"
diff --git a/python/ql/src/experimental/Security/CWE-409/FileAndFormRemoteFlowSource.qll b/python/ql/src/experimental/Security/CWE-409/FileAndFormRemoteFlowSource.qll
new file mode 100644
index 00000000000..ae9691d77e1
--- /dev/null
+++ b/python/ql/src/experimental/Security/CWE-409/FileAndFormRemoteFlowSource.qll
@@ -0,0 +1,63 @@
+import python
+import semmle.python.dataflow.new.DataFlow
+import semmle.python.dataflow.new.TaintTracking
+import semmle.python.ApiGraphs
+
+/**
+ * Provides user-controllable Remote sources for file(s) upload and Multipart-Form
+ */
+module FileAndFormRemoteFlowSource {
+ /**
+ * A
+ */
+ class FastAPI extends DataFlow::Node {
+ FastAPI() {
+ exists(API::Node fastApiParam, Expr fastApiUploadFile |
+ fastApiParam =
+ API::moduleImport("fastapi")
+ .getMember("FastAPI")
+ .getReturn()
+ .getMember("post")
+ .getReturn()
+ .getParameter(0)
+ .getKeywordParameter(_) and
+ fastApiUploadFile =
+ API::moduleImport("fastapi")
+ .getMember("UploadFile")
+ .getASubclass*()
+ .getAValueReachableFromSource()
+ .asExpr()
+ |
+ // Multiple uploaded files as list of fastapi.UploadFile
+ // @app.post("/")
+ // def upload(files: List[UploadFile] = File(...)):
+ // for file in files:
+ fastApiUploadFile =
+ fastApiParam.asSource().asExpr().(Parameter).getAnnotation().getASubExpression*() and
+ exists(For f, Attribute attr |
+ fastApiParam.getAValueReachableFromSource().asExpr() = f.getIter().getASubExpression*()
+ |
+ TaintTracking::localExprTaint(f.getIter(), attr.getObject()) and
+ attr.getName() = ["filename", "content_type", "headers", "file", "read"] and
+ this.asExpr() = attr
+ )
+ or
+ // One uploaded file as fastapi.UploadFile
+ // @app.post("/zipbomb2")
+ // async def zipbomb2(file: UploadFile):
+ // print(file.filename)
+ this =
+ [
+ fastApiParam.getMember(["filename", "content_type", "headers"]).asSource(),
+ fastApiParam
+ .getMember("file")
+ .getMember(["readlines", "readline", "read"])
+ .getReturn()
+ .asSource(), fastApiParam.getMember("read").getReturn().asSource()
+ ]
+ )
+ }
+
+ string getSourceType() { result = "fastapi HTTP FORM files" }
+ }
+}
diff --git a/python/ql/src/experimental/Security/CWE-409/example_good.py b/python/ql/src/experimental/Security/CWE-409/example_good.py
index f11c7bba097..f0dcd2efcc0 100644
--- a/python/ql/src/experimental/Security/CWE-409/example_good.py
+++ b/python/ql/src/experimental/Security/CWE-409/example_good.py
@@ -2,20 +2,33 @@ import zipfile
def safeUnzip(zipFileName):
- buffer_size = 2 ** 10 * 8
+ '''
+ safeUnzip reads each file inside the zipfile 1 MB by 1 MB
+ and during extraction or reading of these files it checks the total decompressed size
+ doesn't exceed the SIZE_THRESHOLD
+ '''
+ buffer_size = 1024 * 1024 * 1 # 1 MB
total_size = 0
- SIZE_THRESHOLD = 2 ** 10 * 100
+ SIZE_THRESHOLD = 1024 * 1024 * 10 # 10 MB
with zipfile.ZipFile(zipFileName) as myzip:
for fileinfo in myzip.infolist():
- content = b''
with myzip.open(fileinfo.filename, mode="r") as myfile:
+ content = b''
chunk = myfile.read(buffer_size)
+ total_size += buffer_size
+ if total_size > SIZE_THRESHOLD:
+ print("Bomb detected")
+ return False # it isn't a successful extract or read
+ content += chunk
+ # reading next bytes of uncompressed data
while chunk:
chunk = myfile.read(buffer_size)
total_size += buffer_size
if total_size > SIZE_THRESHOLD:
print("Bomb detected")
- return
+ return False # it isn't a successful extract or read
content += chunk
+
+ # An example of extracting or reading each decompressed file here
print(bytes.decode(content, 'utf-8'))
- return "No Bombs!"
+ return True # it is a successful extract or read
diff --git a/python/ql/src/experimental/semmle/python/security/DecompressionBomb.qll b/python/ql/src/experimental/semmle/python/security/DecompressionBomb.qll
index 6659cb189b6..129e92554ef 100644
--- a/python/ql/src/experimental/semmle/python/security/DecompressionBomb.qll
+++ b/python/ql/src/experimental/semmle/python/security/DecompressionBomb.qll
@@ -6,13 +6,6 @@ import semmle.python.dataflow.new.RemoteFlowSources
import semmle.python.dataflow.new.internal.DataFlowPublic
module DecompressionBomb {
- /**
- * The Sinks of uncontrolled data decompression, use this class in your queries
- */
- class Sink extends DataFlow::Node {
- Sink() { this = any(Range r).sink() }
- }
-
/**
* The additional taint steps that need for creating taint tracking or dataflow.
*/
@@ -28,15 +21,7 @@ module DecompressionBomb {
/**
* A abstract class responsible for extending new decompression sinks
*/
- abstract class Range extends API::Node {
- /**
- * Gets the sink of responsible for decompression node
- *
- * it can be a path, stream of compressed data,
- * or a call to function that use pipe
- */
- abstract DataFlow::Node sink();
- }
+ abstract class Sink extends DataFlow::Node { }
}
module ZipFile {
@@ -56,68 +41,26 @@ module ZipFile {
}
/**
- * The Decompression Sinks of `zipfile` library
- *
- * ```python
- * myzip = zipfile.ZipFile("zipfileName.zip")
- * myzip.open('eggs.txt',"r").read()
- * myzip.extractall()
- * ```
+ * A Call to `extractall`, `extract()`, or `extractfile()` on an open ZipFile.
*/
- class DecompressionSink extends DecompressionBomb::Range {
- override string toString() { result = "DecompressionSink" }
-
- DecompressionSink() { this = zipFileClass() }
-
- /**
- * An function call of tarfile for extracting compressed data
- *
- * `tarfile.open(filepath).extractall()` or `tarfile.open(filepath).extract()`or `tarfile.open(filepath).extractfile()`
- * or `tarfile.Tarfile.xzopen()` or `tarfile.Tarfile.gzopen()` or `tarfile.Tarfile.bz2open()`
- */
- override DataFlow::Node sink() {
- (
- result = this.getReturn().getMember(["extractall", "read", "extract", "testzip"]).getACall()
- or
- exists(API::Node openInstance |
- openInstance = this.getReturn().getMember("open") and
- (
- not exists(
- openInstance
- .getACall()
- .getParameter(1, "mode")
- .getAValueReachingSink()
- .asExpr()
- .(StrConst)
- .getText()
- ) or
- openInstance
- .getACall()
- .getParameter(1, "mode")
- .getAValueReachingSink()
- .asExpr()
- .(StrConst)
- .getText() = "r"
- ) and
- (
- not exists(
- this.getACall()
- .getParameter(1, "mode")
- .getAValueReachingSink()
- .asExpr()
- .(StrConst)
- .getText()
- ) or
- this.getACall()
- .getParameter(1, "mode")
- .getAValueReachingSink()
- .asExpr()
- .(StrConst)
- .getText() = "r"
- ) and
- not zipFileDecompressionBombSanitizer(this) and
- result = openInstance.getACall()
- )
+ class DecompressionSink extends DecompressionBomb::Sink {
+ DecompressionSink() {
+ this =
+ zipFileClass()
+ .getReturn()
+ .getMember(["extractall", "read", "extract", "testzip"])
+ .getACall()
+ or
+ exists(API::Node zipOpen | zipOpen = zipFileClass().getReturn().getMember("open") |
+ // this open function must reads uncompressed data with buffer
+ // and checks the accumulated size at the end of each read to be called safe
+ not TaintTracking::localExprTaint(zipOpen
+ .getReturn()
+ .getMember("read")
+ .getParameter(0)
+ .asSink()
+ .asExpr(), any(Compare i).getASubExpression*()) and
+ this = zipOpen.getACall()
)
}
}
@@ -157,17 +100,16 @@ module ZipFile {
DecompressionAdditionalTaintStep() { this = "AdditionalTaintStep" }
override predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
- exists(DecompressionSink zipFileInstance |
+ exists(API::Node zipFileInstance | zipFileInstance = zipFileClass() |
nodeFrom =
[zipFileInstance.getACall().getParameter(0, "file").asSink(), zipFileInstance.getACall()] and
nodeTo =
[
- zipFileInstance.sink(),
zipFileInstance
.getACall()
.getReturn()
.getMember(["extractall", "read", "extract", "testzip"])
- .getACall()
+ .getACall(), zipFileInstance.getReturn().getMember("open").getACall()
]
)
}
@@ -179,20 +121,17 @@ module ZipFile {
*/
module TarFile {
/**
- * The Decompression Sinks of `tarfile` library
+ * A Call to `extractall`, `extract()`, or `extractfile()` on an open tarfile,
+ * or
+ * A Call to `gzopen`, `xzopen()`, or `bz2open()` on a tarfile.Tarfile.
*/
- class DecompressionSink extends DecompressionBomb::Range {
- override string toString() { result = "DecompressionSink" }
-
- DecompressionSink() { this = tarfileExtractMember() }
-
- /**
- * An function call of tarfile for extracting compressed data
- * `tarfile.open(filepath).extractall()` or `tarfile.open(filepath).extract()`or `tarfile.open(filepath).extractfile()`
- * or `tarfile.Tarfile.xzopen()` or `tarfile.Tarfile.gzopen()` or `tarfile.Tarfile.bz2open()`
- */
- override DataFlow::Node sink() {
- result = this.getReturn().getMember(["extractall", "extract", "extractfile"]).getACall()
+ class DecompressionSink extends DecompressionBomb::Sink {
+ DecompressionSink() {
+ this =
+ tarfileExtractMember()
+ .getReturn()
+ .getMember(["extractall", "extract", "extractfile"])
+ .getACall()
}
}
@@ -251,34 +190,34 @@ module Pandas {
/**
* The Decompression Sinks of `pandas` library
*/
- class DecompressionSink extends DecompressionBomb::Range {
- override string toString() { result = "DecompressionSink" }
-
- DecompressionSink() { this = API::moduleImport("pandas") }
-
- override DataFlow::Node sink() {
- exists(API::CallNode calltoPandasMethods |
+ class DecompressionSink extends DecompressionBomb::Sink {
+ DecompressionSink() {
+ exists(API::CallNode callToPandasMethods |
(
- calltoPandasMethods =
- this.getMember([
- "read_csv", "read_json", "read_sas", "read_stata", "read_table", "read_xml"
- ]).getACall() and
- result = calltoPandasMethods.getArg(0)
+ callToPandasMethods =
+ API::moduleImport("pandas")
+ .getMember([
+ "read_csv", "read_json", "read_sas", "read_stata", "read_table", "read_xml"
+ ])
+ .getACall() and
+ this = callToPandasMethods.getArg(0)
or
- calltoPandasMethods =
- this.getMember(["read_csv", "read_sas", "read_stata", "read_table"]).getACall() and
- result = calltoPandasMethods.getArgByName("filepath_or_buffer")
+ callToPandasMethods =
+ API::moduleImport("pandas")
+ .getMember(["read_csv", "read_sas", "read_stata", "read_table"])
+ .getACall() and
+ this = callToPandasMethods.getArgByName("filepath_or_buffer")
or
- calltoPandasMethods = this.getMember("read_json").getACall() and
- result = calltoPandasMethods.getArgByName("path_or_buf")
+ callToPandasMethods = API::moduleImport("pandas").getMember("read_json").getACall() and
+ this = callToPandasMethods.getArgByName("path_or_buf")
or
- calltoPandasMethods = this.getMember("read_xml").getACall() and
- result = calltoPandasMethods.getArgByName("path_or_buffer")
+ callToPandasMethods = API::moduleImport("pandas").getMember("read_xml").getACall() and
+ this = callToPandasMethods.getArgByName("path_or_buffer")
) and
(
- not exists(calltoPandasMethods.getArgByName("compression"))
+ not exists(callToPandasMethods.getArgByName("compression"))
or
- not calltoPandasMethods
+ not callToPandasMethods
.getKeywordParameter("compression")
.getAValueReachingSink()
.asExpr()
@@ -297,12 +236,15 @@ module Shutil {
/**
* The Decompression Sinks of `shutil` library
*/
- class DecompressionSink extends DecompressionBomb::Range {
- override string toString() { result = "DecompressionSink" }
-
- DecompressionSink() { this = API::moduleImport("shutil").getMember("unpack_archive") }
-
- override DataFlow::Node sink() { result = this.getACall().getParameter(0, "filename").asSink() }
+ class DecompressionSink extends DecompressionBomb::Sink {
+ DecompressionSink() {
+ this =
+ API::moduleImport("shutil")
+ .getMember("unpack_archive")
+ .getACall()
+ .getParameter(0, "filename")
+ .asSink()
+ }
}
}
@@ -322,14 +264,10 @@ module Gzip {
*
* only read mode is sink
*/
- class DecompressionSink extends DecompressionBomb::Range {
- override string toString() { result = "DecompressionSink" }
-
- DecompressionSink() { this = gzipInstance() }
-
- override DataFlow::Node sink() {
- exists(API::CallNode gzipCall | gzipCall = this.getACall() |
- result = gzipCall.getParameter(0, "filename").asSink() and
+ class DecompressionSink extends DecompressionBomb::Sink {
+ DecompressionSink() {
+ exists(API::CallNode gzipCall | gzipCall = gzipInstance().getACall() |
+ this = gzipCall.getParameter(0, "filename").asSink() and
(
not exists(
gzipCall.getParameter(1, "mode").getAValueReachingSink().asExpr().(StrConst).getText()
@@ -363,14 +301,10 @@ module Bz2 {
*
* only read mode is sink
*/
- class DecompressionSink extends DecompressionBomb::Range {
- override string toString() { result = "DecompressionSink" }
-
- DecompressionSink() { this = bz2Instance() }
-
- override DataFlow::Node sink() {
- exists(API::CallNode bz2Call | bz2Call = this.getACall() |
- result = bz2Call.getParameter(0, "filename").asSink() and
+ class DecompressionSink extends DecompressionBomb::Sink {
+ DecompressionSink() {
+ exists(API::CallNode bz2Call | bz2Call = bz2Instance().getACall() |
+ this = bz2Call.getParameter(0, "filename").asSink() and
(
not exists(
bz2Call.getParameter(1, "mode").getAValueReachingSink().asExpr().(StrConst).getText()
@@ -404,14 +338,10 @@ module Lzma {
*
* only read mode is sink
*/
- class DecompressionSink extends DecompressionBomb::Range {
- override string toString() { result = "DecompressionSink" }
-
- DecompressionSink() { this = lzmaInstance() }
-
- override DataFlow::Node sink() {
- exists(API::CallNode lzmaCall | lzmaCall = this.getACall() |
- result = lzmaCall.getParameter(0, "filename").asSink() and
+ class DecompressionSink extends DecompressionBomb::Sink {
+ DecompressionSink() {
+ exists(API::CallNode lzmaCall | lzmaCall = lzmaInstance().getACall() |
+ this = lzmaCall.getParameter(0, "filename").asSink() and
(
not exists(
lzmaCall.getParameter(1, "mode").getAValueReachingSink().asExpr().(StrConst).getText()
From 5795c72a9942e0a8b7666251a8bc64dad4e876b1 Mon Sep 17 00:00:00 2001
From: amammad <77095239+amammad@users.noreply.github.com>
Date: Thu, 7 Dec 2023 14:04:33 +0100
Subject: [PATCH 014/378] added inline tests
---
.../Security/CWE-409/DecompressionBombs.ql | 51 ------------
.../python/security/DecompressionBomb.qll | 40 ++++++++++
.../security/FileAndFormRemoteFlowSource.qll | 63 +++++++++++++++
.../Security/CWE-409/DataflowQueryTest.ql | 4 +
.../query-tests/Security/CWE-409/test.py | 79 ++++++++++---------
5 files changed, 149 insertions(+), 88 deletions(-)
create mode 100644 python/ql/src/experimental/semmle/python/security/FileAndFormRemoteFlowSource.qll
create mode 100644 python/ql/test/experimental/query-tests/Security/CWE-409/DataflowQueryTest.ql
diff --git a/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql b/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql
index 044afff97b4..0670f92e81c 100644
--- a/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql
+++ b/python/ql/src/experimental/Security/CWE-409/DecompressionBombs.ql
@@ -18,57 +18,6 @@ import semmle.python.ApiGraphs
import semmle.python.dataflow.new.RemoteFlowSources
import semmle.python.dataflow.new.internal.DataFlowPublic
import experimental.semmle.python.security.DecompressionBomb
-import FileAndFormRemoteFlowSource::FileAndFormRemoteFlowSource
-
-/**
- * `io.TextIOWrapper(ip, encoding='utf-8')` like following:
- * ```python
- * with gzip.open(bomb_input, 'rb') as ip:
- * with io.TextIOWrapper(ip, encoding='utf-8') as decoder:
- * content = decoder.read()
- * print(content)
- * ```
- * I saw this builtin method many places so I added it as a AdditionalTaintStep.
- * it would be nice if it is added as a global AdditionalTaintStep
- */
-predicate isAdditionalTaintStepTextIOWrapper(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
- exists(API::CallNode textIOWrapper |
- textIOWrapper = API::moduleImport("io").getMember("TextIOWrapper").getACall()
- |
- nodeFrom = textIOWrapper.getParameter(0, "input").asSink() and
- nodeTo = textIOWrapper
- )
-}
-
-module BombsConfig implements DataFlow::ConfigSig {
- predicate isSource(DataFlow::Node source) {
- (
- source instanceof RemoteFlowSource
- or
- source instanceof FastAPI
- ) and
- not source.getLocation().getFile().inStdlib() and
- not source.getLocation().getFile().getRelativePath().matches("%venv%")
- }
-
- predicate isSink(DataFlow::Node sink) {
- sink instanceof DecompressionBomb::Sink and
- not sink.getLocation().getFile().inStdlib() and
- not sink.getLocation().getFile().getRelativePath().matches("%venv%")
- }
-
- predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) {
- (
- any(DecompressionBomb::AdditionalTaintStep a).isAdditionalTaintStep(pred, succ) or
- isAdditionalTaintStepTextIOWrapper(pred, succ)
- ) and
- not succ.getLocation().getFile().inStdlib() and
- not succ.getLocation().getFile().getRelativePath().matches("%venv%")
- }
-}
-
-module BombsFlow = TaintTracking::Global;
-
import BombsFlow::PathGraph
from BombsFlow::PathNode source, BombsFlow::PathNode sink
diff --git a/python/ql/src/experimental/semmle/python/security/DecompressionBomb.qll b/python/ql/src/experimental/semmle/python/security/DecompressionBomb.qll
index 129e92554ef..c1a29241273 100644
--- a/python/ql/src/experimental/semmle/python/security/DecompressionBomb.qll
+++ b/python/ql/src/experimental/semmle/python/security/DecompressionBomb.qll
@@ -4,6 +4,7 @@ import semmle.python.dataflow.new.TaintTracking
import semmle.python.ApiGraphs
import semmle.python.dataflow.new.RemoteFlowSources
import semmle.python.dataflow.new.internal.DataFlowPublic
+import FileAndFormRemoteFlowSource::FileAndFormRemoteFlowSource
module DecompressionBomb {
/**
@@ -358,3 +359,42 @@ module Lzma {
}
}
}
+
+/**
+ * `io.TextIOWrapper(ip, encoding='utf-8')` like following:
+ * ```python
+ * with gzip.open(bomb_input, 'rb') as ip:
+ * with io.TextIOWrapper(ip, encoding='utf-8') as decoder:
+ * content = decoder.read()
+ * print(content)
+ * ```
+ * I saw this builtin method many places so I added it as a AdditionalTaintStep.
+ * it would be nice if it is added as a global AdditionalTaintStep
+ */
+predicate isAdditionalTaintStepTextIOWrapper(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
+ exists(API::CallNode textIOWrapper |
+ textIOWrapper = API::moduleImport("io").getMember("TextIOWrapper").getACall()
+ |
+ nodeFrom = textIOWrapper.getParameter(0, "input").asSink() and
+ nodeTo = textIOWrapper
+ )
+}
+
+module BombsConfig implements DataFlow::ConfigSig {
+ predicate isSource(DataFlow::Node source) {
+ source instanceof RemoteFlowSource
+ or
+ source instanceof FastAPI
+ }
+
+ predicate isSink(DataFlow::Node sink) { sink instanceof DecompressionBomb::Sink }
+
+ predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) {
+ (
+ any(DecompressionBomb::AdditionalTaintStep a).isAdditionalTaintStep(pred, succ) or
+ isAdditionalTaintStepTextIOWrapper(pred, succ)
+ )
+ }
+}
+
+module BombsFlow = TaintTracking::Global;
diff --git a/python/ql/src/experimental/semmle/python/security/FileAndFormRemoteFlowSource.qll b/python/ql/src/experimental/semmle/python/security/FileAndFormRemoteFlowSource.qll
new file mode 100644
index 00000000000..ae9691d77e1
--- /dev/null
+++ b/python/ql/src/experimental/semmle/python/security/FileAndFormRemoteFlowSource.qll
@@ -0,0 +1,63 @@
+import python
+import semmle.python.dataflow.new.DataFlow
+import semmle.python.dataflow.new.TaintTracking
+import semmle.python.ApiGraphs
+
+/**
+ * Provides user-controllable Remote sources for file(s) upload and Multipart-Form
+ */
+module FileAndFormRemoteFlowSource {
+ /**
+ * A
+ */
+ class FastAPI extends DataFlow::Node {
+ FastAPI() {
+ exists(API::Node fastApiParam, Expr fastApiUploadFile |
+ fastApiParam =
+ API::moduleImport("fastapi")
+ .getMember("FastAPI")
+ .getReturn()
+ .getMember("post")
+ .getReturn()
+ .getParameter(0)
+ .getKeywordParameter(_) and
+ fastApiUploadFile =
+ API::moduleImport("fastapi")
+ .getMember("UploadFile")
+ .getASubclass*()
+ .getAValueReachableFromSource()
+ .asExpr()
+ |
+ // Multiple uploaded files as list of fastapi.UploadFile
+ // @app.post("/")
+ // def upload(files: List[UploadFile] = File(...)):
+ // for file in files:
+ fastApiUploadFile =
+ fastApiParam.asSource().asExpr().(Parameter).getAnnotation().getASubExpression*() and
+ exists(For f, Attribute attr |
+ fastApiParam.getAValueReachableFromSource().asExpr() = f.getIter().getASubExpression*()
+ |
+ TaintTracking::localExprTaint(f.getIter(), attr.getObject()) and
+ attr.getName() = ["filename", "content_type", "headers", "file", "read"] and
+ this.asExpr() = attr
+ )
+ or
+ // One uploaded file as fastapi.UploadFile
+ // @app.post("/zipbomb2")
+ // async def zipbomb2(file: UploadFile):
+ // print(file.filename)
+ this =
+ [
+ fastApiParam.getMember(["filename", "content_type", "headers"]).asSource(),
+ fastApiParam
+ .getMember("file")
+ .getMember(["readlines", "readline", "read"])
+ .getReturn()
+ .asSource(), fastApiParam.getMember("read").getReturn().asSource()
+ ]
+ )
+ }
+
+ string getSourceType() { result = "fastapi HTTP FORM files" }
+ }
+}
diff --git a/python/ql/test/experimental/query-tests/Security/CWE-409/DataflowQueryTest.ql b/python/ql/test/experimental/query-tests/Security/CWE-409/DataflowQueryTest.ql
new file mode 100644
index 00000000000..24a2c302b98
--- /dev/null
+++ b/python/ql/test/experimental/query-tests/Security/CWE-409/DataflowQueryTest.ql
@@ -0,0 +1,4 @@
+import python
+import experimental.dataflow.TestUtil.DataflowQueryTest
+import experimental.semmle.python.security.DecompressionBomb
+import FromTaintTrackingConfig
diff --git a/python/ql/test/experimental/query-tests/Security/CWE-409/test.py b/python/ql/test/experimental/query-tests/Security/CWE-409/test.py
index 848c2123db8..9a9425fa535 100644
--- a/python/ql/test/experimental/query-tests/Security/CWE-409/test.py
+++ b/python/ql/test/experimental/query-tests/Security/CWE-409/test.py
@@ -7,70 +7,75 @@ app = FastAPI()
@app.post("/bomb")
async def bomb(file_path):
- zipfile.ZipFile(file_path, "r").extract("file1")
- zipfile.ZipFile(file_path, "r").extractall()
+ zipfile.ZipFile(file_path, "r").extract("file1") # $ result=BAD
+ zipfile.ZipFile(file_path, "r").extractall() # $ result=BAD
with zipfile.ZipFile(file_path) as myzip:
- with myzip.open('ZZ') as myfile:
+ with myzip.open('ZZ') as myfile: # $ result=BAD
a = myfile.readline()
with zipfile.ZipFile(file_path) as myzip:
- with myzip.open('ZZ', mode="w") as myfile:
+ with myzip.open('ZZ', mode="w") as myfile: # $result=OK
myfile.write(b"tmpppp")
- zipfile.ZipFile(file_path).read("aFileNameInTheZipFile")
+ zipfile.ZipFile(file_path).read("aFileNameInTheZipFile") # $ result=BAD
- tarfile.open(file_path).extractfile("file1.txt")
- tarfile.TarFile.open(file_path).extract("somefile")
- tarfile.TarFile.xzopen(file_path).extract("somefile")
- tarfile.TarFile.gzopen(file_path).extractall()
- tarfile.TarFile.open(file_path).extractfile("file1.txt")
+ tarfile.open(file_path).extractfile("file1.txt") # $ result=BAD
+ tarfile.TarFile.open(file_path).extract("somefile") # $ result=BAD
+ tarfile.TarFile.xzopen(file_path).extract("somefile") # $ result=BAD
+ tarfile.TarFile.gzopen(file_path).extractall() # $ result=BAD
+ tarfile.TarFile.open(file_path).extractfile("file1.txt") # $ result=BAD
- tarfile.open(file_path, mode="w")
- tarfile.TarFile.gzopen(file_path, mode="w")
- tarfile.TarFile.open(file_path, mode="r:")
+ tarfile.open(file_path, mode="w") # $result=OK
+ tarfile.TarFile.gzopen(file_path, mode="w") # $result=OK
+ tarfile.TarFile.open(file_path, mode="r:") # $ result=BAD
import shutil
- shutil.unpack_archive(file_path)
+ shutil.unpack_archive(file_path) # $ result=BAD
import lzma
- lzma.open(file_path)
- lzma.LZMAFile(file_path).read()
+ lzma.open(file_path) # $ result=BAD
+ lzma.LZMAFile(file_path).read() # $ result=BAD
import bz2
- bz2.open(file_path)
- bz2.BZ2File(file_path).read()
+ bz2.open(file_path) # $ result=BAD
+ bz2.BZ2File(file_path).read() # $ result=BAD
import gzip
- gzip.open(file_path)
- gzip.GzipFile(file_path)
+ gzip.open(file_path) # $ result=BAD
+ gzip.GzipFile(file_path) # $ result=BAD
import pandas
- pandas.read_csv(filepath_or_buffer=file_path)
+ pandas.read_csv(filepath_or_buffer=file_path) # $ result=BAD
- pandas.read_table(file_path, compression='gzip')
- pandas.read_xml(file_path, compression='gzip')
+ pandas.read_table(file_path, compression='gzip') # $ result=BAD
+ pandas.read_xml(file_path, compression='gzip') # $ result=BAD
- pandas.read_csv(filepath_or_buffer=file_path, compression='gzip')
- pandas.read_json(file_path, compression='gzip')
- pandas.read_sas(file_path, compression='gzip')
- pandas.read_stata(filepath_or_buffer=file_path, compression='gzip')
- pandas.read_table(file_path, compression='gzip')
- pandas.read_xml(path_or_buffer=file_path, compression='gzip')
+ pandas.read_csv(filepath_or_buffer=file_path,
+ compression='gzip') # $ result=BAD
+ pandas.read_json(file_path, compression='gzip') # $ result=BAD
+ pandas.read_sas(file_path, compression='gzip') # $ result=BAD
+ pandas.read_stata(filepath_or_buffer=file_path,
+ compression='gzip') # $ result=BAD
+ pandas.read_table(file_path, compression='gzip') # $ result=BAD
+ pandas.read_xml(path_or_buffer=file_path,
+ compression='gzip') # $ result=BAD
# no compression no DOS
- pandas.read_table(file_path, compression='tar')
- pandas.read_xml(file_path, compression='tar')
+ pandas.read_table(file_path, compression='tar') # $result=OK
+ pandas.read_xml(file_path, compression='tar') # $result=OK
- pandas.read_csv(filepath_or_buffer=file_path, compression='tar')
- pandas.read_json(file_path, compression='tar')
- pandas.read_sas(file_path, compression='tar')
- pandas.read_stata(filepath_or_buffer=file_path, compression='tar')
- pandas.read_table(file_path, compression='tar')
- pandas.read_xml(path_or_buffer=file_path, compression='tar')
+ pandas.read_csv(filepath_or_buffer=file_path,
+ compression='tar') # $result=OK
+ pandas.read_json(file_path, compression='tar') # $result=OK
+ pandas.read_sas(file_path, compression='tar') # $result=OK
+ pandas.read_stata(filepath_or_buffer=file_path,
+ compression='tar') # $result=OK
+ pandas.read_table(file_path, compression='tar') # $result=OK
+ pandas.read_xml(path_or_buffer=file_path, compression='tar') # $result=OK
return {"message": "bomb"}
From 96f8a02a7280f105bbf6ea12f7698dd43aecd716 Mon Sep 17 00:00:00 2001
From: Asger F
Date: Mon, 15 Jan 2024 13:00:39 +0100
Subject: [PATCH 015/378] JS: Treat private-field methods as private
---
.../ql/lib/semmle/javascript/Classes.qll | 23 ++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)
diff --git a/javascript/ql/lib/semmle/javascript/Classes.qll b/javascript/ql/lib/semmle/javascript/Classes.qll
index c7ad74561bb..f5877a78371 100644
--- a/javascript/ql/lib/semmle/javascript/Classes.qll
+++ b/javascript/ql/lib/semmle/javascript/Classes.qll
@@ -516,16 +516,37 @@ class MemberDeclaration extends @property, Documentable {
*/
predicate hasPublicKeyword() { has_public_keyword(this) }
+ /**
+ * Holds if this member is considered private.
+ *
+ * This may occur in two cases:
+ * - it is a TypeScript member annotated with the `private` keyword, or
+ * - the member has a private name, such as `#foo`, referring to a private field in the class
+ */
+ predicate isPrivate() { this.hasPrivateKeyword() or this.hasPrivateFieldName() }
+
/**
* Holds if this is a TypeScript member annotated with the `private` keyword.
*/
- predicate isPrivate() { has_private_keyword(this) }
+ predicate hasPrivateKeyword() { has_private_keyword(this) }
/**
* Holds if this is a TypeScript member annotated with the `protected` keyword.
*/
predicate isProtected() { has_protected_keyword(this) }
+ /**
+ * Holds if the member has a private name, such as `#foo`, referring to a private field in the class.
+ *
+ * For example:
+ * ```js
+ * class Foo {
+ * #method() {}
+ * }
+ * ```
+ */
+ predicate hasPrivateFieldName() { this.getNameExpr().(Label).getName().charAt(0) = "#" }
+
/**
* Gets the expression specifying the name of this member,
* or nothing if this is a call signature.
From ddbacc3d4a4d589765668f74778737766637f313 Mon Sep 17 00:00:00 2001
From: Asger F
Date: Fri, 26 Jan 2024 11:11:01 +0100
Subject: [PATCH 016/378] Shared: add test case for stateful outBarrier bug
---
.../library-tests/dataflow/inoutbarriers/A.java | 11 +++++++++++
.../dataflow/inoutbarriers/test.expected | 14 +++++++++-----
.../library-tests/dataflow/inoutbarriers/test.ql | 5 +++++
3 files changed, 25 insertions(+), 5 deletions(-)
diff --git a/java/ql/test/library-tests/dataflow/inoutbarriers/A.java b/java/ql/test/library-tests/dataflow/inoutbarriers/A.java
index 51604991371..69207701837 100644
--- a/java/ql/test/library-tests/dataflow/inoutbarriers/A.java
+++ b/java/ql/test/library-tests/dataflow/inoutbarriers/A.java
@@ -1,10 +1,17 @@
class A {
static String fsrc = "";
+ String fsink = "";
String src(String s) { return s; }
void sink(String s) { }
+ static String flowThroughSink(String s) {
+ A obj = new A();
+ obj.fsink = s;
+ return obj.fsink;
+ }
+
void foo() {
String s = fsrc;
sink(fsrc);
@@ -13,5 +20,9 @@ class A {
sink(s);
sink(s);
+
+ s = fsrc;
+ s = flowThroughSink(s);
+ sink(s);
}
}
diff --git a/java/ql/test/library-tests/dataflow/inoutbarriers/test.expected b/java/ql/test/library-tests/dataflow/inoutbarriers/test.expected
index de785df0a1d..8d5a58661ab 100644
--- a/java/ql/test/library-tests/dataflow/inoutbarriers/test.expected
+++ b/java/ql/test/library-tests/dataflow/inoutbarriers/test.expected
@@ -1,7 +1,11 @@
inconsistentFlow
+| A.java:24:9:24:12 | fsrc | A.java:26:10:26:10 | s | spurious state-flow in configuration both |
+| A.java:24:9:24:12 | fsrc | A.java:26:10:26:10 | s | spurious state-flow in configuration sinkbarrier |
#select
-| A.java:9:16:9:19 | fsrc | A.java:13:10:13:10 | s | nobarrier, sinkbarrier |
-| A.java:9:16:9:19 | fsrc | A.java:15:10:15:10 | s | nobarrier |
-| A.java:10:10:10:13 | fsrc | A.java:10:10:10:13 | fsrc | both, nobarrier, sinkbarrier, srcbarrier |
-| A.java:12:9:12:14 | src(...) | A.java:13:10:13:10 | s | both, nobarrier, sinkbarrier, srcbarrier |
-| A.java:12:9:12:14 | src(...) | A.java:15:10:15:10 | s | nobarrier, srcbarrier |
+| A.java:16:16:16:19 | fsrc | A.java:20:10:20:10 | s | nobarrier, sinkbarrier |
+| A.java:16:16:16:19 | fsrc | A.java:22:10:22:10 | s | nobarrier |
+| A.java:17:10:17:13 | fsrc | A.java:17:10:17:13 | fsrc | both, nobarrier, sinkbarrier, srcbarrier |
+| A.java:19:9:19:14 | src(...) | A.java:20:10:20:10 | s | both, nobarrier, sinkbarrier, srcbarrier |
+| A.java:19:9:19:14 | src(...) | A.java:22:10:22:10 | s | nobarrier, srcbarrier |
+| A.java:24:9:24:12 | fsrc | A.java:11:17:11:17 | s | both, nobarrier, sinkbarrier, srcbarrier |
+| A.java:24:9:24:12 | fsrc | A.java:26:10:26:10 | s | nobarrier, srcbarrier |
diff --git a/java/ql/test/library-tests/dataflow/inoutbarriers/test.ql b/java/ql/test/library-tests/dataflow/inoutbarriers/test.ql
index 82d35f0483c..7972386a2dc 100644
--- a/java/ql/test/library-tests/dataflow/inoutbarriers/test.ql
+++ b/java/ql/test/library-tests/dataflow/inoutbarriers/test.ql
@@ -12,6 +12,11 @@ predicate sink0(Node n) {
sink.getMethod().hasName("sink") and
sink.getAnArgument() = n.asExpr()
)
+ or
+ exists(AssignExpr assign |
+ assign.getDest().(FieldAccess).getField().hasName("fsink") and
+ n.asExpr() = assign.getSource()
+ )
}
module Conf1 implements ConfigSig {
From d1310c74fcb33f1ad789796c2176e383d372338a Mon Sep 17 00:00:00 2001
From: Asger F
Date: Thu, 25 Jan 2024 14:27:54 +0100
Subject: [PATCH 017/378] Shared: remove old stateful outBarrier check
---
.../codeql/dataflow/internal/DataFlowImpl.qll | 13 +++++--------
1 file changed, 5 insertions(+), 8 deletions(-)
diff --git a/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll b/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll
index 27aa1d38e6e..7a5f76715e5 100644
--- a/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll
+++ b/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll
@@ -3773,14 +3773,11 @@ module MakeImpl {
}
override PathNodeImpl getASuccessorImpl() {
- not outBarrier(node, state) and
- (
- // an intermediate step to another intermediate node
- result = this.getSuccMid()
- or
- // a final step to a sink
- result = this.getSuccMid().projectToSink()
- )
+ // an intermediate step to another intermediate node
+ result = this.getSuccMid()
+ or
+ // a final step to a sink
+ result = this.getSuccMid().projectToSink()
}
override predicate isSource() {
From f15ead613023ff9ee42a3cce43f74592a6d5c1e5 Mon Sep 17 00:00:00 2001
From: Asger F
Date: Thu, 25 Jan 2024 14:28:06 +0100
Subject: [PATCH 018/378] Shared: check stateful outBarrier as part of pathStep
SCC
---
.../codeql/dataflow/internal/DataFlowImpl.qll | 20 ++++++++++++-------
1 file changed, 13 insertions(+), 7 deletions(-)
diff --git a/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll b/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll
index 7a5f76715e5..0708bf301af 100644
--- a/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll
+++ b/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll
@@ -2682,6 +2682,7 @@ module MakeImpl {
) {
not isUnreachableInCall1(node2, cc) and
not inBarrier(node2, state) and
+ not outBarrier(node1, state) and
(
localFlowEntry(node1, pragma[only_bind_into](state)) and
(
@@ -3757,6 +3758,9 @@ module MakeImpl {
override NodeEx getNodeEx() { result = node }
+ pragma[inline]
+ final NodeEx getNodeExOutgoing() { result = node and not outBarrier(node, state) }
+
override FlowState getState() { result = state }
CallContext getCallContext() { result = cc }
@@ -3928,14 +3932,14 @@ module MakeImpl {
ap instanceof AccessPathNil
)
or
- jumpStepEx(mid.getNodeEx(), node) and
+ jumpStepEx(mid.getNodeExOutgoing(), node) and
state = mid.getState() and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
t = mid.getType() and
ap = mid.getAp()
or
- additionalJumpStep(mid.getNodeEx(), node) and
+ additionalJumpStep(mid.getNodeExOutgoing(), node) and
state = mid.getState() and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
@@ -3943,7 +3947,7 @@ module MakeImpl {
t = node.getDataFlowType() and
ap = TAccessPathNil()
or
- additionalJumpStateStep(mid.getNodeEx(), mid.getState(), node, state) and
+ additionalJumpStateStep(mid.getNodeExOutgoing(), mid.getState(), node, state) and
cc instanceof CallContextAny and
sc instanceof SummaryCtxNone and
mid.getAp() instanceof AccessPathNil and
@@ -3978,7 +3982,7 @@ module MakeImpl {
) {
ap0 = mid.getAp() and
c = ap0.getHead() and
- Stage5::readStepCand(mid.getNodeEx(), c, node) and
+ Stage5::readStepCand(mid.getNodeExOutgoing(), c, node) and
state = mid.getState() and
cc = mid.getCallContext()
}
@@ -3991,7 +3995,7 @@ module MakeImpl {
exists(DataFlowType contentType |
t0 = mid.getType() and
ap0 = mid.getAp() and
- Stage5::storeStepCand(mid.getNodeEx(), _, c, node, contentType, t) and
+ Stage5::storeStepCand(mid.getNodeExOutgoing(), _, c, node, contentType, t) and
state = mid.getState() and
cc = mid.getCallContext() and
compatibleTypes(t0, contentType)
@@ -4009,7 +4013,8 @@ module MakeImpl {
not outBarrier(retNode, state) and
innercc = mid.getCallContext() and
innercc instanceof CallContextNoCall and
- apa = mid.getAp().getApprox()
+ apa = mid.getAp().getApprox() and
+ not outBarrier(retNode, state)
)
}
@@ -4130,7 +4135,8 @@ module MakeImpl {
pathNode(_, ret, state, cc, sc, t, ap, _) and
kind = ret.getKind() and
apa = ap.getApprox() and
- parameterFlowThroughAllowed(sc.getParamNode(), kind)
+ parameterFlowThroughAllowed(sc.getParamNode(), kind) and
+ not outBarrier(ret, state)
)
}
From ee8e9a4e66b976aa98e28464bfe3a61fb84f9e0d Mon Sep 17 00:00:00 2001
From: Asger F
Date: Fri, 26 Jan 2024 11:13:44 +0100
Subject: [PATCH 019/378] Shared: update test output
---
java/ql/test/library-tests/dataflow/inoutbarriers/test.expected | 2 --
1 file changed, 2 deletions(-)
diff --git a/java/ql/test/library-tests/dataflow/inoutbarriers/test.expected b/java/ql/test/library-tests/dataflow/inoutbarriers/test.expected
index 8d5a58661ab..90f99161585 100644
--- a/java/ql/test/library-tests/dataflow/inoutbarriers/test.expected
+++ b/java/ql/test/library-tests/dataflow/inoutbarriers/test.expected
@@ -1,6 +1,4 @@
inconsistentFlow
-| A.java:24:9:24:12 | fsrc | A.java:26:10:26:10 | s | spurious state-flow in configuration both |
-| A.java:24:9:24:12 | fsrc | A.java:26:10:26:10 | s | spurious state-flow in configuration sinkbarrier |
#select
| A.java:16:16:16:19 | fsrc | A.java:20:10:20:10 | s | nobarrier, sinkbarrier |
| A.java:16:16:16:19 | fsrc | A.java:22:10:22:10 | s | nobarrier |
From 19cb7adb6db17a3131b7db93482abc6a0d93ceff Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Thu, 20 Apr 2023 11:42:11 +0200
Subject: [PATCH 020/378] Migrate path injection sinks to MaD
Deprecate and stop using PathCreation
Path creation sinks are now summaries
---
.../2023-04-20-deprecated-path-creation.md | 4 +
java/ql/lib/ext/java.io.model.yml | 7 +-
java/ql/lib/ext/java.nio.file.model.yml | 12 +-
.../code/java/security/PathCreation.qll | 26 +-
.../code/java/security/TaintedPathQuery.qll | 11 +-
.../src/Security/CWE/CWE-022/TaintedPath.ql | 18 +-
.../Security/CWE/CWE-022/TaintedPathLocal.ql | 18 +-
...04-20-path-injection-precision-improved.md | 4 +
.../Security/CWE/CWE-073/FilePathInjection.ql | 8 +
.../pathcreation/PathCreation.expected | 1 +
.../CWE-022/semmle/tests/TaintedPath.ql | 11 +
.../CWE-022/semmle/tests/TaintedPath.qlref | 1 -
.../security/CWE-022/semmle/tests/Test.java | 254 +++++++++++-------
.../CWE-022/semmle/tests/mad/Test.java | 228 ----------------
14 files changed, 216 insertions(+), 387 deletions(-)
create mode 100644 java/ql/lib/change-notes/2023-04-20-deprecated-path-creation.md
create mode 100644 java/ql/src/change-notes/2023-04-20-path-injection-precision-improved.md
create mode 100644 java/ql/test/query-tests/security/CWE-022/semmle/tests/TaintedPath.ql
delete mode 100644 java/ql/test/query-tests/security/CWE-022/semmle/tests/TaintedPath.qlref
delete mode 100644 java/ql/test/query-tests/security/CWE-022/semmle/tests/mad/Test.java
diff --git a/java/ql/lib/change-notes/2023-04-20-deprecated-path-creation.md b/java/ql/lib/change-notes/2023-04-20-deprecated-path-creation.md
new file mode 100644
index 00000000000..c955a459ca0
--- /dev/null
+++ b/java/ql/lib/change-notes/2023-04-20-deprecated-path-creation.md
@@ -0,0 +1,4 @@
+---
+category: deprecated
+---
+* The `PathCreation` class in `PathCreation.qll` has been deprecated.
diff --git a/java/ql/lib/ext/java.io.model.yml b/java/ql/lib/ext/java.io.model.yml
index 1bd9251c29d..17dbc1464dc 100644
--- a/java/ql/lib/ext/java.io.model.yml
+++ b/java/ql/lib/ext/java.io.model.yml
@@ -3,18 +3,17 @@ extensions:
pack: codeql/java-all
extensible: sinkModel
data:
- - ["java.io", "File", False, "File", "(File,String)", "", "Argument[1]", "path-injection", "manual"] # old PathCreation
- - ["java.io", "File", False, "File", "(String)", "", "Argument[0]", "path-injection", "manual"] # old PathCreation
- - ["java.io", "File", False, "File", "(String,String)", "", "Argument[0..1]", "path-injection", "manual"] # old PathCreation
- - ["java.io", "File", False, "File", "(URI)", "", "Argument[0]", "path-injection", "manual"] # old PathCreation
- ["java.io", "File", True, "createNewFile", "()", "", "Argument[this]", "path-injection", "ai-manual"]
- ["java.io", "File", True, "createTempFile", "(String,String,File)", "", "Argument[2]", "path-injection", "ai-manual"]
- ["java.io", "File", True, "renameTo", "(File)", "", "Argument[0]", "path-injection", "ai-manual"]
- ["java.io", "FileInputStream", True, "FileInputStream", "(File)", "", "Argument[0]", "path-injection", "ai-manual"]
+ - ["java.io", "FileInputStream", True, "FileInputStream", "(FileDescriptor)", "", "Argument[0]", "path-injection", "manual"]
- ["java.io", "FileInputStream", True, "FileInputStream", "(String)", "", "Argument[0]", "path-injection", "ai-manual"]
- ["java.io", "FileOutputStream", False, "FileOutputStream", "", "", "Argument[0]", "path-injection", "manual"]
- ["java.io", "FileOutputStream", False, "write", "", "", "Argument[0]", "file-content-store", "manual"]
- ["java.io", "FileReader", True, "FileReader", "(File)", "", "Argument[0]", "path-injection", "ai-manual"]
+ - ["java.io", "FileReader", True, "FileReader", "(FileDescriptor)", "", "Argument[0]", "path-injection", "manual"]
+ - ["java.io", "FileReader", True, "FileReader", "(File,Charset)", "", "Argument[0]", "path-injection", "manual"]
- ["java.io", "FileReader", True, "FileReader", "(String)", "", "Argument[0]", "path-injection", "ai-manual"]
- ["java.io", "FileReader", True, "FileReader", "(String,Charset)", "", "Argument[0]", "path-injection", "manual"]
- ["java.io", "FileSystem", True, "createDirectory", "(File)", "", "Argument[0]", "path-injection", "ai-manual"]
diff --git a/java/ql/lib/ext/java.nio.file.model.yml b/java/ql/lib/ext/java.nio.file.model.yml
index 3c77c876eee..a35c575e9cb 100644
--- a/java/ql/lib/ext/java.nio.file.model.yml
+++ b/java/ql/lib/ext/java.nio.file.model.yml
@@ -37,15 +37,8 @@ extensions:
- ["java.nio.file", "Files", False, "write", "", "", "Argument[1]", "file-content-store", "manual"]
- ["java.nio.file", "Files", False, "writeString", "", "", "Argument[0]", "path-injection", "manual"]
- ["java.nio.file", "Files", False, "writeString", "", "", "Argument[1]", "file-content-store", "manual"]
- - ["java.nio.file", "FileSystem", False, "getPath", "", "", "Argument[0..1]", "path-injection", "manual"] #Â old PathCreation
- ["java.nio.file", "FileSystems", False, "newFileSystem", "(URI,Map)", "", "Argument[0]", "path-injection", "ai-manual"]
- ["java.nio.file", "FileSystems", False, "newFileSystem", "(URI,Map)", "", "Argument[0]", "request-forgery", "ai-manual"]
- - ["java.nio.file", "Path", False, "of", "(String,String[])", "", "Argument[0..1]", "path-injection", "manual"] #Â old PathCreation
- - ["java.nio.file", "Path", False, "of", "(URI)", "", "Argument[0]", "path-injection", "manual"] #Â old PathCreation
- - ["java.nio.file", "Path", False, "resolve", "(String)", "", "Argument[0]", "path-injection", "manual"] #Â old PathCreation
- - ["java.nio.file", "Path", False, "resolveSibling", "(String)", "", "Argument[0]", "path-injection", "manual"] #Â old PathCreation
- - ["java.nio.file", "Paths", False, "get", "(String,String[])", "", "Argument[0..1]", "path-injection", "manual"] #Â old PathCreation
- - ["java.nio.file", "Paths", False, "get", "(URI)", "", "Argument[0]", "path-injection", "manual"] #Â old PathCreation
- ["java.nio.file", "SecureDirectoryStream", True, "deleteDirectory", "(Path)", "", "Argument[0]", "path-injection", "ai-manual"]
- ["java.nio.file", "SecureDirectoryStream", True, "deleteFile", "(Path)", "", "Argument[0]", "path-injection", "ai-manual"]
- addsTo:
@@ -63,7 +56,7 @@ extensions:
- ["java.nio.file", "Files", True, "newDirectoryStream", "(Path,DirectoryStream$Filter)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
- ["java.nio.file", "Files", True, "newDirectoryStream", "(Path)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
- ["java.nio.file", "Files", True, "walk", "(Path,FileVisitOption[])", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
- - ["java.nio.file", "FileSystem", True, "getPath", "", "", "Argument[0]", "ReturnValue", "taint", "manual"]
+ - ["java.nio.file", "FileSystem", True, "getPath", "(String,String[])", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["java.nio.file", "FileSystem", True, "getPath", "(String,String[])", "", "Argument[1]", "ReturnValue", "taint", "ai-manual"]
- ["java.nio.file", "FileSystem", True, "getPathMatcher", "(String)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
- ["java.nio.file", "FileSystem", True, "getRootDirectories", "", "", "Argument[0]", "ReturnValue", "taint", "manual"]
@@ -76,7 +69,8 @@ extensions:
- ["java.nio.file", "Path", True, "relativize", "(Path)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
- ["java.nio.file", "Path", True, "resolve", "", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["java.nio.file", "Path", True, "resolve", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- - ["java.nio.file", "Path", True, "resolveSibling", "(String)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
+ - ["java.nio.file", "Path", True, "resolveSibling", "", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
+ - ["java.nio.file", "Path", True, "resolveSibling", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- ["java.nio.file", "Path", True, "toAbsolutePath", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- ["java.nio.file", "Path", False, "toFile", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- ["java.nio.file", "Path", True, "toString", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
diff --git a/java/ql/lib/semmle/code/java/security/PathCreation.qll b/java/ql/lib/semmle/code/java/security/PathCreation.qll
index 924d42674fb..3d40a1d4fdb 100644
--- a/java/ql/lib/semmle/code/java/security/PathCreation.qll
+++ b/java/ql/lib/semmle/code/java/security/PathCreation.qll
@@ -1,11 +1,13 @@
/**
+ * DEPRECATED.
+ *
* Models the different ways to create paths. Either by using `java.io.File`-related APIs or `java.nio.file.Path`-related APIs.
*/
import java
-/** Models the creation of a path. */
-abstract class PathCreation extends Expr {
+/** DEPRECATED: Models the creation of a path. */
+abstract deprecated class PathCreation extends Expr {
/**
* Gets an input that is used in the creation of this path.
* This excludes inputs of type `File` and `Path`.
@@ -14,7 +16,7 @@ abstract class PathCreation extends Expr {
}
/** Models the `java.nio.file.Paths.get` method. */
-private class PathsGet extends PathCreation, MethodCall {
+deprecated private class PathsGet extends PathCreation, MethodCall {
PathsGet() {
exists(Method m | m = this.getMethod() |
m.getDeclaringType() instanceof TypePaths and
@@ -26,7 +28,7 @@ private class PathsGet extends PathCreation, MethodCall {
}
/** Models the `java.nio.file.FileSystem.getPath` method. */
-private class FileSystemGetPath extends PathCreation, MethodCall {
+deprecated private class FileSystemGetPath extends PathCreation, MethodCall {
FileSystemGetPath() {
exists(Method m | m = this.getMethod() |
m.getDeclaringType() instanceof TypeFileSystem and
@@ -38,7 +40,7 @@ private class FileSystemGetPath extends PathCreation, MethodCall {
}
/** Models the `new java.io.File(...)` constructor. */
-private class FileCreation extends PathCreation, ClassInstanceExpr {
+deprecated private class FileCreation extends PathCreation, ClassInstanceExpr {
FileCreation() { this.getConstructedType() instanceof TypeFile }
override Expr getAnInput() {
@@ -49,7 +51,7 @@ private class FileCreation extends PathCreation, ClassInstanceExpr {
}
/** Models the `java.nio.file.Path.resolveSibling` method. */
-private class PathResolveSiblingCreation extends PathCreation, MethodCall {
+deprecated private class PathResolveSiblingCreation extends PathCreation, MethodCall {
PathResolveSiblingCreation() {
exists(Method m | m = this.getMethod() |
m.getDeclaringType() instanceof TypePath and
@@ -65,7 +67,7 @@ private class PathResolveSiblingCreation extends PathCreation, MethodCall {
}
/** Models the `java.nio.file.Path.resolve` method. */
-private class PathResolveCreation extends PathCreation, MethodCall {
+deprecated private class PathResolveCreation extends PathCreation, MethodCall {
PathResolveCreation() {
exists(Method m | m = this.getMethod() |
m.getDeclaringType() instanceof TypePath and
@@ -81,7 +83,7 @@ private class PathResolveCreation extends PathCreation, MethodCall {
}
/** Models the `java.nio.file.Path.of` method. */
-private class PathOfCreation extends PathCreation, MethodCall {
+deprecated private class PathOfCreation extends PathCreation, MethodCall {
PathOfCreation() {
exists(Method m | m = this.getMethod() |
m.getDeclaringType() instanceof TypePath and
@@ -93,7 +95,7 @@ private class PathOfCreation extends PathCreation, MethodCall {
}
/** Models the `new java.io.FileWriter(...)` constructor. */
-private class FileWriterCreation extends PathCreation, ClassInstanceExpr {
+deprecated private class FileWriterCreation extends PathCreation, ClassInstanceExpr {
FileWriterCreation() { this.getConstructedType().hasQualifiedName("java.io", "FileWriter") }
override Expr getAnInput() {
@@ -104,7 +106,7 @@ private class FileWriterCreation extends PathCreation, ClassInstanceExpr {
}
/** Models the `new java.io.FileReader(...)` constructor. */
-private class FileReaderCreation extends PathCreation, ClassInstanceExpr {
+deprecated private class FileReaderCreation extends PathCreation, ClassInstanceExpr {
FileReaderCreation() { this.getConstructedType().hasQualifiedName("java.io", "FileReader") }
override Expr getAnInput() {
@@ -115,7 +117,7 @@ private class FileReaderCreation extends PathCreation, ClassInstanceExpr {
}
/** Models the `new java.io.FileInputStream(...)` constructor. */
-private class FileInputStreamCreation extends PathCreation, ClassInstanceExpr {
+deprecated private class FileInputStreamCreation extends PathCreation, ClassInstanceExpr {
FileInputStreamCreation() {
this.getConstructedType().hasQualifiedName("java.io", "FileInputStream")
}
@@ -128,7 +130,7 @@ private class FileInputStreamCreation extends PathCreation, ClassInstanceExpr {
}
/** Models the `new java.io.FileOutputStream(...)` constructor. */
-private class FileOutputStreamCreation extends PathCreation, ClassInstanceExpr {
+deprecated private class FileOutputStreamCreation extends PathCreation, ClassInstanceExpr {
FileOutputStreamCreation() {
this.getConstructedType().hasQualifiedName("java.io", "FileOutputStream")
}
diff --git a/java/ql/lib/semmle/code/java/security/TaintedPathQuery.qll b/java/ql/lib/semmle/code/java/security/TaintedPathQuery.qll
index 85265f6b169..63bd4949699 100644
--- a/java/ql/lib/semmle/code/java/security/TaintedPathQuery.qll
+++ b/java/ql/lib/semmle/code/java/security/TaintedPathQuery.qll
@@ -8,6 +8,13 @@ private import semmle.code.java.dataflow.ExternalFlow
import semmle.code.java.security.PathSanitizer
private import semmle.code.java.security.Sanitizers
+/** A sink for tainted path flow configurations. */
+abstract class TaintedPathSink extends DataFlow::Node { }
+
+private class DefaultTaintedPathSink extends TaintedPathSink {
+ DefaultTaintedPathSink() { sinkNode(this, "path-injection") }
+}
+
/**
* A unit class for adding additional taint steps.
*
@@ -55,7 +62,7 @@ private class TaintPreservingUriCtorParam extends Parameter {
module TaintedPathConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof ThreatModelFlowSource }
- predicate isSink(DataFlow::Node sink) { sinkNode(sink, "path-injection") }
+ predicate isSink(DataFlow::Node sink) { sink instanceof TaintedPathSink }
predicate isBarrier(DataFlow::Node sanitizer) {
sanitizer instanceof SimpleTypeSanitizer or
@@ -76,7 +83,7 @@ module TaintedPathFlow = TaintTracking::Global;
module TaintedPathLocalConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof LocalUserInput }
- predicate isSink(DataFlow::Node sink) { sinkNode(sink, "path-injection") }
+ predicate isSink(DataFlow::Node sink) { sink instanceof TaintedPathSink }
predicate isBarrier(DataFlow::Node sanitizer) {
sanitizer instanceof SimpleTypeSanitizer or
diff --git a/java/ql/src/Security/CWE/CWE-022/TaintedPath.ql b/java/ql/src/Security/CWE/CWE-022/TaintedPath.ql
index 96e8e66c7cd..3963442d648 100644
--- a/java/ql/src/Security/CWE/CWE-022/TaintedPath.ql
+++ b/java/ql/src/Security/CWE/CWE-022/TaintedPath.ql
@@ -18,21 +18,7 @@ import semmle.code.java.security.PathCreation
import semmle.code.java.security.TaintedPathQuery
import TaintedPathFlow::PathGraph
-/**
- * Gets the data-flow node at which to report a path ending at `sink`.
- *
- * Previously this query flagged alerts exclusively at `PathCreation` sites,
- * so to avoid perturbing existing alerts, where a `PathCreation` exists we
- * continue to report there; otherwise we report directly at `sink`.
- */
-DataFlow::Node getReportingNode(DataFlow::Node sink) {
- TaintedPathFlow::flowTo(sink) and
- if exists(PathCreation pc | pc.getAnInput() = sink.asExpr())
- then result.asExpr() = any(PathCreation pc | pc.getAnInput() = sink.asExpr())
- else result = sink
-}
-
from TaintedPathFlow::PathNode source, TaintedPathFlow::PathNode sink
where TaintedPathFlow::flowPath(source, sink)
-select getReportingNode(sink.getNode()), source, sink, "This path depends on a $@.",
- source.getNode(), "user-provided value"
+select sink.getNode(), source, sink, "This path depends on a $@.", source.getNode(),
+ "user-provided value"
diff --git a/java/ql/src/Security/CWE/CWE-022/TaintedPathLocal.ql b/java/ql/src/Security/CWE/CWE-022/TaintedPathLocal.ql
index 8e56121883f..60dc6b54be8 100644
--- a/java/ql/src/Security/CWE/CWE-022/TaintedPathLocal.ql
+++ b/java/ql/src/Security/CWE/CWE-022/TaintedPathLocal.ql
@@ -18,21 +18,7 @@ import semmle.code.java.security.PathCreation
import semmle.code.java.security.TaintedPathQuery
import TaintedPathLocalFlow::PathGraph
-/**
- * Gets the data-flow node at which to report a path ending at `sink`.
- *
- * Previously this query flagged alerts exclusively at `PathCreation` sites,
- * so to avoid perturbing existing alerts, where a `PathCreation` exists we
- * continue to report there; otherwise we report directly at `sink`.
- */
-DataFlow::Node getReportingNode(DataFlow::Node sink) {
- TaintedPathLocalFlow::flowTo(sink) and
- if exists(PathCreation pc | pc.getAnInput() = sink.asExpr())
- then result.asExpr() = any(PathCreation pc | pc.getAnInput() = sink.asExpr())
- else result = sink
-}
-
from TaintedPathLocalFlow::PathNode source, TaintedPathLocalFlow::PathNode sink
where TaintedPathLocalFlow::flowPath(source, sink)
-select getReportingNode(sink.getNode()), source, sink, "This path depends on a $@.",
- source.getNode(), "user-provided value"
+select sink.getNode(), source, sink, "This path depends on a $@.", source.getNode(),
+ "user-provided value"
diff --git a/java/ql/src/change-notes/2023-04-20-path-injection-precision-improved.md b/java/ql/src/change-notes/2023-04-20-path-injection-precision-improved.md
new file mode 100644
index 00000000000..763cedea45d
--- /dev/null
+++ b/java/ql/src/change-notes/2023-04-20-path-injection-precision-improved.md
@@ -0,0 +1,4 @@
+---
+category: majorAnalysis
+---
+* The sinks of the queries `java/path-injection` and `java/path-injection-local` have been reworked. Path creation sinks have been converted to summaries instead, while sinks now are actual file read/write operations only. This has reduced the false positive ratio of both queries.
diff --git a/java/ql/src/experimental/Security/CWE/CWE-073/FilePathInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-073/FilePathInjection.ql
index d0b59bf1136..7f6528a6670 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-073/FilePathInjection.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-073/FilePathInjection.ql
@@ -16,6 +16,10 @@ import java
import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.dataflow.ExternalFlow
import semmle.code.java.dataflow.FlowSources
+<<<<<<< HEAD
+=======
+import semmle.code.java.security.TaintedPathQuery
+>>>>>>> 9e469c9c32 (Migrate path injection sinks to MaD)
import JFinalController
import semmle.code.java.security.PathSanitizer
private import semmle.code.java.security.Sanitizers
@@ -52,7 +56,11 @@ module InjectFilePathConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof ThreatModelFlowSource }
predicate isSink(DataFlow::Node sink) {
+<<<<<<< HEAD
sinkNode(sink, "path-injection") and
+=======
+ sink instanceof TaintedPathSink and
+>>>>>>> 9e469c9c32 (Migrate path injection sinks to MaD)
not sink instanceof NormalizedPathNode
}
diff --git a/java/ql/test/library-tests/pathcreation/PathCreation.expected b/java/ql/test/library-tests/pathcreation/PathCreation.expected
index c0ac69c7da4..41e10fb6aaf 100644
--- a/java/ql/test/library-tests/pathcreation/PathCreation.expected
+++ b/java/ql/test/library-tests/pathcreation/PathCreation.expected
@@ -1,3 +1,4 @@
+WARNING: Type PathCreation has been deprecated and may be removed in future (PathCreation.ql:4,6-18)
| PathCreation.java:13:18:13:32 | new File(...) | PathCreation.java:13:27:13:31 | "dir" |
| PathCreation.java:14:19:14:40 | new File(...) | PathCreation.java:14:28:14:32 | "dir" |
| PathCreation.java:14:19:14:40 | new File(...) | PathCreation.java:14:35:14:39 | "sub" |
diff --git a/java/ql/test/query-tests/security/CWE-022/semmle/tests/TaintedPath.ql b/java/ql/test/query-tests/security/CWE-022/semmle/tests/TaintedPath.ql
new file mode 100644
index 00000000000..e17123ce781
--- /dev/null
+++ b/java/ql/test/query-tests/security/CWE-022/semmle/tests/TaintedPath.ql
@@ -0,0 +1,11 @@
+import java
+import TestUtilities.InlineFlowTest
+import semmle.code.java.security.TaintedPathQuery
+
+class HasFlowTest extends InlineFlowTest {
+ override predicate hasTaintFlow(DataFlow::Node src, DataFlow::Node sink) {
+ TaintedPathFlow::flow(src, sink)
+ }
+
+ override predicate hasValueFlow(DataFlow::Node src, DataFlow::Node sink) { none() }
+}
diff --git a/java/ql/test/query-tests/security/CWE-022/semmle/tests/TaintedPath.qlref b/java/ql/test/query-tests/security/CWE-022/semmle/tests/TaintedPath.qlref
deleted file mode 100644
index 1677939387d..00000000000
--- a/java/ql/test/query-tests/security/CWE-022/semmle/tests/TaintedPath.qlref
+++ /dev/null
@@ -1 +0,0 @@
-Security/CWE/CWE-022/TaintedPath.ql
\ No newline at end of file
diff --git a/java/ql/test/query-tests/security/CWE-022/semmle/tests/Test.java b/java/ql/test/query-tests/security/CWE-022/semmle/tests/Test.java
index 080cc263f08..872f2a01b65 100644
--- a/java/ql/test/query-tests/security/CWE-022/semmle/tests/Test.java
+++ b/java/ql/test/query-tests/security/CWE-022/semmle/tests/Test.java
@@ -1,112 +1,168 @@
-// Semmle test case for CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
-// http://cwe.mitre.org/data/definitions/22.html
-package test.cwe22.semmle.tests;
-
-import javax.servlet.http.*;
-import javax.servlet.ServletException;
-
-import java.io.*;
-import java.net.*;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.URL;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.nio.file.FileSystems;
+import javax.xml.transform.stream.StreamResult;
+import org.apache.commons.io.FileUtils;
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.taskdefs.Copy;
+import org.apache.tools.ant.taskdefs.Expand;
+import org.apache.tools.ant.types.FileSet;
+import org.codehaus.cargo.container.installer.ZipURLInstaller;
+import org.kohsuke.stapler.framework.io.LargeText;
+import org.openjdk.jmh.runner.options.ChainedOptionsBuilder;
+import org.springframework.util.FileCopyUtils;
-import org.apache.commons.io.output.LockableFileWriter;
+public class Test {
-class Test {
- void doGet1(InetAddress address)
- throws IOException {
- String temp = address.getHostName();
- File file;
- Path path;
+ private InetAddress address;
- // BAD: construct a file path with user input
- file = new File(temp);
+ public Object source() {
+ return address.getHostName();
+ }
- // BAD: construct a path with user input
- path = Paths.get(temp);
+ void test() throws IOException {
+ // "java.lang;Module;true;getResourceAsStream;(String);;Argument[0];read-file;ai-generated"
+ getClass().getModule().getResourceAsStream((String) source()); // $ hasTaintFlow
+ // "java.lang;Class;false;getResource;(String);;Argument[0];read-file;ai-generated"
+ getClass().getResource((String) source()); // $ hasTaintFlow
+ // "java.lang;ClassLoader;true;getSystemResourceAsStream;(String);;Argument[0];read-file;ai-generated"
+ ClassLoader.getSystemResourceAsStream((String) source()); // $ hasTaintFlow
+ // "java.io;File;true;createTempFile;(String,String,File);;Argument[2];create-file;ai-generated"
+ File.createTempFile(";", ";", (File) source()); // $ hasTaintFlow
+ // "java.io;File;true;renameTo;(File);;Argument[0];create-file;ai-generated"
+ new File("").renameTo((File) source()); // $ hasTaintFlow
+ // "java.io;FileInputStream;true;FileInputStream;(File);;Argument[0];read-file;ai-generated"
+ new FileInputStream((File) source()); // $ hasTaintFlow
+ // "java.io;FileInputStream;true;FileInputStream;(FileDescriptor);;Argument[0];read-file;manual"
+ new FileInputStream((FileDescriptor) source()); // $ hasTaintFlow
+ // "java.io;FileInputStream;true;FileInputStream;(Strrirng);;Argument[0];read-file;manual"
+ new FileInputStream((String) source()); // $ hasTaintFlow
+ // "java.io;FileReader;true;FileReader;(File);;Argument[0];read-file;ai-generated"
+ new FileReader((File) source()); // $ hasTaintFlow
+ // "java.io;FileReader;true;FileReader;(FileDescriptor);;Argument[0];read-file;manual"
+ new FileReader((FileDescriptor) source()); // $ hasTaintFlow
+ // "java.io;FileReader;true;FileReader;(File,Charset);;Argument[0];read-file;manual"
+ new FileReader((File) source(), null); // $ hasTaintFlow
+ // "java.io;FileReader;true;FileReader;(String);;Argument[0];read-file;ai-generated"
+ new FileReader((String) source()); // $ hasTaintFlow
+ // "java.io;FileReader;true;FileReader;(String,Charset);;Argument[0];read-file;manual"
+ new FileReader((String) source(), null); // $ hasTaintFlow
+ // "java.nio.file;Files;false;copy;;;Argument[0];read-file;manual"
+ Files.copy((Path) source(), (Path) null); // $ hasTaintFlow
+ Files.copy((Path) source(), (OutputStream) null); // $ hasTaintFlow
+ Files.copy((InputStream) source(), null); // $ hasTaintFlow
+ // "java.nio.file;Files;false;copy;;;Argument[1];create-file;manual"
+ Files.copy((Path) null, (Path) source()); // $ hasTaintFlow
+ Files.copy((Path) null, (OutputStream) source()); // $ hasTaintFlow
+ Files.copy((InputStream) null, (Path) source()); // $ hasTaintFlow
+ // "java.nio.file;Files;false;createDirectories;;;Argument[0];create-file;manual"
+ Files.createDirectories((Path) source()); // $ hasTaintFlow
+ // "java.nio.file;Files;false;createDirectory;;;Argument[0];create-file;manual"
+ Files.createDirectory((Path) source()); // $ hasTaintFlow
+ // "java.nio.file;Files;false;createFile;;;Argument[0];create-file;manual"
+ Files.createFile((Path) source()); // $ hasTaintFlow
+ // "java.nio.file;Files;false;createLink;;;Argument[0];create-file;manual"
+ Files.createLink((Path) source(), null); // $ hasTaintFlow
+ // "java.nio.file;Files;false;createSymbolicLink;;;Argument[0];create-file;manual"
+ Files.createSymbolicLink((Path) source(), null); // $ hasTaintFlow
+ // "java.nio.file;Files;false;createTempDirectory;(Path,String,FileAttribute[]);;Argument[0];create-file;manual"
+ Files.createTempDirectory((Path) source(), null); // $ hasTaintFlow
+ // "java.nio.file;Files;false;createTempFile;(Path,String,String,FileAttribute[]);;Argument[0];create-file;manual"
+ Files.createTempFile((Path) source(), null, null); // $ hasTaintFlow
+ // "java.nio.file;Files;false;delete;(Path);;Argument[0];delete-file;ai-generated"
+ Files.delete((Path) source()); // $ hasTaintFlow
+ // "java.nio.file;Files;false;deleteIfExists;(Path);;Argument[0];delete-file;ai-generated"
+ Files.deleteIfExists((Path) source()); // $ hasTaintFlow
+ // "java.nio.file;Files;false;lines;(Path,Charset);;Argument[0];read-file;ai-generated"
+ Files.lines((Path) source(), null); // $ hasTaintFlow
+ // "java.nio.file;Files;false;move;;;Argument[1];create-file;manual"
+ Files.move(null, (Path) source()); // $ hasTaintFlow
+ // "java.nio.file;Files;false;newBufferedReader;(Path,Charset);;Argument[0];read-file;ai-generated"
+ Files.newBufferedReader((Path) source(), null); // $ hasTaintFlow
+ // "java.nio.file;Files;false;newBufferedWriter;;;Argument[0];create-file;manual"
+ Files.newBufferedWriter((Path) source()); // $ hasTaintFlow
+ Files.newBufferedWriter((Path) source(), (Charset) null); // $ hasTaintFlow
+ // "java.nio.file;Files;false;newOutputStream;;;Argument[0];create-file;manual"
+ Files.newOutputStream((Path) source()); // $ hasTaintFlow
+ // "java.nio.file;Files;false;write;;;Argument[0];create-file;manual"
+ Files.write((Path) source(), (byte[]) null); // $ hasTaintFlow
+ Files.write((Path) source(), (Iterable) null); // $ hasTaintFlow
+ Files.write((Path) source(), (Iterable) null, (Charset) null); // $ hasTaintFlow
+ // "java.nio.file;Files;false;writeString;;;Argument[0];create-file;manual"
+ Files.writeString((Path) source(), (CharSequence) null); // $ hasTaintFlow
+ Files.writeString((Path) source(), (CharSequence) null, (Charset) null); // $ hasTaintFlow
+ // "javax.xml.transform.stream;StreamResult";true;"StreamResult;(File);;Argument[0];create-file;ai-generated"
+ new StreamResult((File) source()); // $ hasTaintFlow
+ // "org.apache.commons.io;FileUtils;true;openInputStream;(File);;Argument[0];read-file;ai-generated"
+ FileUtils.openInputStream((File) source()); // $ hasTaintFlow
+ // "org.codehaus.cargo.container.installer;ZipURLInstaller;true;ZipURLInstaller;(URL,String,String);;Argument[1];create-file;ai-generated"
+ new ZipURLInstaller((URL) null, (String) source(), ""); // $ hasTaintFlow
+ // "org.codehaus.cargo.container.installer;ZipURLInstaller;true;ZipURLInstaller;(URL,String,String);;Argument[2];create-file;ai-generated"
+ new ZipURLInstaller((URL) null, "", (String) source()); // $ hasTaintFlow
+ // "org.springframework.util;FileCopyUtils;false;copy;(byte[],File);;Argument[1];create-file;manual"
+ FileCopyUtils.copy((byte[]) null, (File) source()); // $ hasTaintFlow
+ // "org.springframework.util;FileCopyUtils;false;copy;(File,File);;Argument[0];create-file;manual"
+ FileCopyUtils.copy((File) source(), null); // $ hasTaintFlow
+ // "org.springframework.util;FileCopyUtils;false;copy;(File,File);;Argument[1];create-file;manual"
+ FileCopyUtils.copy((File) null, (File) source()); // $ hasTaintFlow
+ }
- // BAD: construct a path with user input
- path = FileSystems.getDefault().getPath(temp);
+ void test(AntClassLoader acl) {
+ // "org.apache.tools.ant;AntClassLoader;true;addPathComponent;(File);;Argument[0];read-file;ai-generated"
+ acl.addPathComponent((File) source()); // $ hasTaintFlow
+ // "org.apache.tools.ant;AntClassLoader;true;AntClassLoader;(ClassLoader,Project,Path,boolean);;Argument[2];read-file;ai-generated"
+ new AntClassLoader(null, null, (org.apache.tools.ant.types.Path) source(), false); // $ hasTaintFlow
+ // "org.apache.tools.ant;AntClassLoader;true;AntClassLoader;(Project,Path,boolean);;Argument[1];read-file;ai-generated"
+ new AntClassLoader(null, (org.apache.tools.ant.types.Path) source(), false); // $ hasTaintFlow
+ // "org.apache.tools.ant;AntClassLoader;true;AntClassLoader;(Project,Path);;Argument[1];read-file;ai-generated"
+ new AntClassLoader(null, (org.apache.tools.ant.types.Path) source()); // $ hasTaintFlow
+ // "org.kohsuke.stapler.framework.io;LargeText;true;LargeText;(File,Charset,boolean,boolean);;Argument[0];read-file;ai-generated"
+ new LargeText((File) source(), null, false, false); // $ hasTaintFlow
+ }
- // BAD: insufficient check
- if (temp.startsWith("/some_safe_dir/")) {
- file = new File(temp);
- }
- }
-
- void doGet2(InetAddress address)
- throws IOException {
- String temp = address.getHostName();
- File file;
-
- // GOOD: check string is safe
- if(isSafe(temp))
- file = new File(temp);
- }
-
- void doGet3(InetAddress address)
- throws IOException {
- String temp = address.getHostName();
- File file;
-
- // FALSE NEGATIVE: inadequate check - fails to account
- // for '.'s
- if(isSortOfSafe(temp))
- file = new File(temp);
- }
-
- boolean isSafe(String pathSpec) {
- // no file separators
- if (pathSpec.contains(File.separator))
- return false;
- // at most one dot
- int indexOfDot = pathSpec.indexOf('.');
- if (indexOfDot != -1 && pathSpec.indexOf('.', indexOfDot + 1) != -1)
- return false;
- return true;
- }
-
- boolean isSortOfSafe(String pathSpec) {
- // no file separators
- if (pathSpec.contains(File.separator))
- return false;
- return true;
- }
-
- public class MyServlet extends HttpServlet {
- public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
- BufferedReader br = new BufferedReader(new InputStreamReader(request.getInputStream()));
- String filename = br.readLine();
- // BAD: construct a file path with user input
- BufferedWriter bw = new BufferedWriter(new FileWriter("dir/"+filename, true));
+ void doGet6(String root, InetAddress address) throws IOException {
+ String temp = address.getHostName();
+ // GOOD: Use `contains` and `startsWith` to check if the path is safe
+ if (!temp.contains("..") && temp.startsWith(root + "/")) {
+ File file = new File(temp);
}
}
- void doGet4(InetAddress address)
- throws IOException {
- String temp = address.getHostName();
- // BAD: open a file based on user input, using a MaD-documented API
- new LockableFileWriter(temp);
- }
+ void test(DirectoryScanner ds) {
+ // "org.apache.tools.ant;DirectoryScanner;true;setBasedir;(File);;Argument[0];read-file;ai-generated"
+ ds.setBasedir((File) source()); // $ hasTaintFlow
+ }
- void doGet5(InetAddress address)
- throws URISyntaxException {
- String t = address.getHostName();
- // BAD: construct a file path with user input
- new File(new URI(null, t, null));
- new File(new URI(t, t, null, t));
- new File(new URI(t, null, t, t));
- new File(new URI(null, null, t, null, null));
- new File(new URI(null, null, null, 0, t, null, null));
- }
+ void test(Copy cp) {
+ // "org.apache.tools.ant.taskdefs;Copy;true;addFileset;(FileSet);;Argument[0];read-file;ai-generated"
+ cp.addFileset((FileSet) source()); // $ hasTaintFlow
+ // "org.apache.tools.ant.taskdefs;Copy;true;setFile;(File);;Argument[0];read-file;ai-generated"
+ cp.setFile((File) source()); // $ hasTaintFlow
+ // "org.apache.tools.ant.taskdefs;Copy;true;setTodir;(File);;Argument[0];create-file;ai-generated"
+ cp.setTodir((File) source()); // $ hasTaintFlow
+ // "org.apache.tools.ant.taskdefs;Copy;true;setTofile;(File);;Argument[0];create-file;ai-generated"
+ cp.setTofile((File) source()); // $ hasTaintFlow
+ }
- void doGet6(String root, InetAddress address)
- throws IOException{
- String temp = address.getHostName();
- // GOOD: Use `contains` and `startsWith` to check if the path is safe
- if (!temp.contains("..") && temp.startsWith(root + "/")) {
- File file = new File(temp);
- }
- }
+ void test(Expand ex) {
+ // "org.apache.tools.ant.taskdefs;Expand;true;setDest;(File);;Argument[0];create-file;ai-generated"
+ ex.setDest((File) source()); // $ hasTaintFlow
+ // "org.apache.tools.ant.taskdefs;Expand;true;setSrc;(File);;Argument[0];read-file;ai-generated"
+ ex.setSrc((File) source()); // $ hasTaintFlow
+ }
+
+ void test(ChainedOptionsBuilder cob) {
+ // "org.openjdk.jmh.runner.options;ChainedOptionsBuilder;true;result;(String);;Argument[0];create-file;ai-generated"
+ cob.result((String) source()); // $ hasTaintFlow
+ }
}
diff --git a/java/ql/test/query-tests/security/CWE-022/semmle/tests/mad/Test.java b/java/ql/test/query-tests/security/CWE-022/semmle/tests/mad/Test.java
deleted file mode 100644
index 169f3535c6b..00000000000
--- a/java/ql/test/query-tests/security/CWE-022/semmle/tests/mad/Test.java
+++ /dev/null
@@ -1,228 +0,0 @@
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.InetAddress;
-import java.net.URL;
-import java.nio.charset.Charset;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import javax.activation.FileDataSource;
-import javax.xml.transform.stream.StreamResult;
-import org.apache.cxf.common.classloader.ClassLoaderUtils;
-import org.apache.cxf.common.jaxb.JAXBUtils;
-import org.apache.cxf.configuration.jsse.SSLUtils;
-import org.apache.cxf.resource.ExtendedURIResolver;
-import org.apache.cxf.resource.URIResolver;
-import org.apache.cxf.staxutils.StaxUtils;
-import org.apache.cxf.tools.corba.utils.FileOutputStreamFactory;
-import org.apache.cxf.tools.corba.utils.OutputStreamFactory;
-import org.apache.cxf.tools.util.FileWriterUtil;
-import org.apache.cxf.tools.util.OutputStreamCreator;
-import org.apache.commons.io.FileUtils;
-import org.apache.tools.ant.AntClassLoader;
-import org.apache.tools.ant.DirectoryScanner;
-import org.apache.tools.ant.taskdefs.Copy;
-import org.apache.tools.ant.taskdefs.Expand;
-import org.apache.tools.ant.types.FileSet;
-import org.codehaus.cargo.container.installer.ZipURLInstaller;
-import org.kohsuke.stapler.framework.io.LargeText;
-import org.openjdk.jmh.runner.options.ChainedOptionsBuilder;
-import org.springframework.util.FileCopyUtils;
-
-public class Test {
-
- private InetAddress address;
-
- public Object source() {
- return address.getHostName();
- }
-
- void test() throws IOException {
- // "java.lang;Module;true;getResourceAsStream;(String);;Argument[0];read-file;ai-generated"
- getClass().getModule().getResourceAsStream((String) source());
- // "java.lang;Class;false;getResource;(String);;Argument[0];read-file;ai-generated"
- getClass().getResource((String) source());
- // "java.lang;ClassLoader;true;getSystemResourceAsStream;(String);;Argument[0];read-file;ai-generated"
- ClassLoader.getSystemResourceAsStream((String) source());
- // "java.io;File;true;createTempFile;(String,String,File);;Argument[2];create-file;ai-generated"
- File.createTempFile(";", ";", (File) source());
- // "java.io;File;true;renameTo;(File);;Argument[0];create-file;ai-generated"
- new File("").renameTo((File) source());
- // "java.io;FileInputStream;true;FileInputStream;(File);;Argument[0];read-file;ai-generated"
- new FileInputStream((File) source());
- // "java.io;FileReader;true;FileReader;(File);;Argument[0];read-file;ai-generated"
- new FileReader((File) source());
- // "java.io;FileReader;true;FileReader;(String);;Argument[0];read-file;ai-generated"
- new FileReader((String) source());
- // "java.nio.file;Files;false;copy;(Path,OutputStream);;Argument[0];read-file;manual"
- Files.copy((Path) source(), (OutputStream) null);
- // "java.nio.file;Files;false;copy;(Path,Path,CopyOption[]);;Argument[0];read-file;manual"
- Files.copy((Path) source(), (Path) null);
- // "java.nio.file;Files;false;copy;(Path,Path,CopyOption[]);;Argument[1];create-file;manual"
- Files.copy((Path) null, (Path) source());
- // "java.nio.file;Files;false;copy;(InputStream,Path,CopyOption[]);;Argument[1];create-file;manual"
- Files.copy((InputStream) null, (Path) source());
- // "java.nio.file;Files;false;createDirectories;;;Argument[0];create-file;manual"
- Files.createDirectories((Path) source());
- // "java.nio.file;Files;false;createDirectory;;;Argument[0];create-file;manual"
- Files.createDirectory((Path) source());
- // "java.nio.file;Files;false;createFile;;;Argument[0];create-file;manual"
- Files.createFile((Path) source());
- // "java.nio.file;Files;false;createLink;;;Argument[0];create-file;manual"
- Files.createLink((Path) source(), null);
- // "java.nio.file;Files;false;createSymbolicLink;;;Argument[0];create-file;manual"
- Files.createSymbolicLink((Path) source(), null);
- // "java.nio.file;Files;false;createTempDirectory;(Path,String,FileAttribute[]);;Argument[0];create-file;manual"
- Files.createTempDirectory((Path) source(), null);
- // "java.nio.file;Files;false;createTempFile;(Path,String,String,FileAttribute[]);;Argument[0];create-file;manual"
- Files.createTempFile((Path) source(), null, null);
- // "java.nio.file;Files;false;delete;(Path);;Argument[0];delete-file;ai-generated"
- Files.delete((Path) source());
- // "java.nio.file;Files;false;deleteIfExists;(Path);;Argument[0];delete-file;ai-generated"
- Files.deleteIfExists((Path) source());
- // "java.nio.file;Files;false;lines;(Path,Charset);;Argument[0];read-file;ai-generated"
- Files.lines((Path) source(), null);
- // "java.nio.file;Files;false;move;;;Argument[1];create-file;manual"
- Files.move(null, (Path) source());
- // "java.nio.file;Files;false;newBufferedReader;(Path,Charset);;Argument[0];read-file;ai-generated"
- Files.newBufferedReader((Path) source(), null);
- // "java.nio.file;Files;false;newBufferedWriter;;;Argument[0];create-file;manual"
- Files.newBufferedWriter((Path) source());
- Files.newBufferedWriter((Path) source(), (Charset) null);
- // "java.nio.file;Files;false;newOutputStream;;;Argument[0];create-file;manual"
- Files.newOutputStream((Path) source());
- // "java.nio.file;Files;false;write;;;Argument[0];create-file;manual"
- Files.write((Path) source(), (byte[]) null);
- Files.write((Path) source(), (Iterable) null);
- Files.write((Path) source(), (Iterable) null, (Charset) null);
- // "java.nio.file;Files;false;writeString;;;Argument[0];create-file;manual"
- Files.writeString((Path) source(), (CharSequence) null);
- Files.writeString((Path) source(), (CharSequence) null, (Charset) null);
- // "javax.xml.transform.stream;StreamResult";true;"StreamResult;(File);;Argument[0];create-file;ai-generated"
- new StreamResult((File) source());
- // "org.apache.commons.io;FileUtils;true;openInputStream;(File);;Argument[0];read-file;ai-generated"
- FileUtils.openInputStream((File) source());
- // "org.codehaus.cargo.container.installer;ZipURLInstaller;true;ZipURLInstaller;(URL,String,String);;Argument[1];create-file;ai-generated"
- new ZipURLInstaller((URL) null, (String) source(), "");
- // "org.codehaus.cargo.container.installer;ZipURLInstaller;true;ZipURLInstaller;(URL,String,String);;Argument[2];create-file;ai-generated"
- new ZipURLInstaller((URL) null, "", (String) source());
- // "org.springframework.util;FileCopyUtils;false;copy;(byte[],File);;Argument[1];create-file;manual"
- FileCopyUtils.copy((byte[]) null, (File) source());
- // "org.springframework.util;FileCopyUtils;false;copy;(File,File);;Argument[0];create-file;manual"
- FileCopyUtils.copy((File) source(), null);
- // "org.springframework.util;FileCopyUtils;false;copy;(File,File);;Argument[1];create-file;manual"
- FileCopyUtils.copy((File) null, (File) source());
- // "javax.activation;FileDataSource;true;FileDataSource;(String);;Argument[0];path-injection;manual"
- new FileDataSource((String) source());
- // "javax.activation;FileDataSource;true;FileDataSource;(File);;Argument[0];path-injection;manual"
- new FileDataSource((File) source());
- // "org.apache.cxf.common.classloader;ClassLoaderUtils;true;getResourceAsStream;(String,Class);;Argument[0];path-injection;manual"
- ClassLoaderUtils.getResourceAsStream((String) source(), null);
- // "org.apache.cxf.common.jaxb;JAXBUtils;true;createFileCodeWriter;(File);;Argument[0];path-injection;manual"
- JAXBUtils.createFileCodeWriter((File) source());
- // "org.apache.cxf.common.jaxb;JAXBUtils;true;createFileCodeWriter;(File,String);;Argument[0];path-injection;manual"
- JAXBUtils.createFileCodeWriter((File) source(), null);
- // "org.apache.cxf.configuration.jsse:SSLUtils;true;loadFile;(String);;Argument[0];path-injection;manual"
- new SSLUtils() {
- public void test() {
- loadFile((String) source());
- }
- };
- // "org.apache.cxf.helpers;FileUtils;true;delete;(File);;Argument[0];path-injection;manual"
- org.apache.cxf.helpers.FileUtils.delete((File) source());
- // "org.apache.cxf.helpers;FileUtils;true;delete;(File,boolean);;Argument[0];path-injection;manual"
- org.apache.cxf.helpers.FileUtils.delete((File) source(), false);
- // "org.apache.cxf.helpers;FileUtils;true;mkdir;(File);;Argument[0];path-injection;manual"
- org.apache.cxf.helpers.FileUtils.mkDir((File) source());
- // "org.apache.cxf.helpers;FileUtils;true;readLines;(File);;Argument[0];path-injection;manual"
- org.apache.cxf.helpers.FileUtils.readLines((File) source());
- // "org.apache.cxf.helpers;FileUtils;true;removeDir;(File);;Argument[0];path-injection;manual"
- org.apache.cxf.helpers.FileUtils.removeDir((File) source());
- // "org.apache.cxf.resource;ExtendedURIResolver;true;resolve;(String,String);;Argument[1];path-injection;manual"
- new ExtendedURIResolver().resolve(null, (String) source()); // $ SSRF
- // "org.apache.cxf.resource;URIResolver;true;URIResolver;(String,String);;Argument[0];path-injection;manual"
- new URIResolver((String) source(), null); // $ SSRF
- // "org.apache.cxf.resource;URIResolver;true;URIResolver;(String,String,Class);;Argument[0];path-injection;manual"
- new URIResolver((String) source(), null, null); // $ SSRF
- // "org.apache.cxf.resource;URIResolver;true;resolve;(String,String,Class);;Argument[0];path-injection;manual"
- new URIResolver().resolve((String) source(), null, null); // $ SSRF
- // "org.apache.cxf.staxutils;StaxUtils;true;read;(File);;Argument[0];path-injection;manual"
- StaxUtils.read((File) source()); // $ SSRF
- // "org.apache.cxf.tools.corba.utils;FileOutputStreamFactory;true;FileOutputStreamFactory;(String);;Argument[0];path-injection;manual"
- new FileOutputStreamFactory((String) source()); // $ SSRF
- // "org.apache.cxf.tools.corba.utils;FileOutputStreamFactory;true;FileOutputStreamFactory;(String,FileOutputStreamFactory);;Argument[0];path-injection;manual"
- new FileOutputStreamFactory((String) source(), null); // $ SSRF
- // "org.apache.cxf.tools.corba.utils;OutputStreamFactory;true;createOutputStream;(String);;Argument[0];path-injection;manual"
- new FileOutputStreamFactory().createOutputStream((String) source()); // $ SSRF
- // "org.apache.cxf.tools.corba.utils;OutputStreamFactory;true;createOutputStream;(String,String);;Argument[0];path-injection;manual"
- new FileOutputStreamFactory().createOutputStream((String) source(), null); // $ SSRF
- // "org.apache.cxf.tools.corba.utils;OutputStreamFactory;true;createOutputStream;(String,String);;Argument[1];path-injection;manual"
- new FileOutputStreamFactory().createOutputStream(null, (String) source()); // $ SSRF
- // @formatter:off
- // "org.apache.cxf.tools.util;FileWriterUtil;true;FileWriterUtil;(String,OutputStreamCreator);;Argument[0];path-injection;manual"
- new FileWriterUtil((String) source(), null); // $ SSRF
- // "org.apache.cxf.tools.util;FileWriterUtil;true;buildDir;(String);;Argument[0];path-injection;manual"
- new FileWriterUtil().buildDir((String) source()); // $ SSRF
- // "org.apache.cxf.tools.util;FileWriterUtil;true;getFileToWrite;(String,String);;Argument[0];path-injection;manual"
- new FileWriterUtil().getFileToWrite((String) source(), (String) null); // $ SSRF
- // "org.apache.cxf.tools.util;FileWriterUtil;true;getFileToWrite;(String,String);;Argument[1];path-injection;manual"
- new FileWriterUtil().getFileToWrite((String) null, (String) source()); // $ SSRF
- // "org.apache.cxf.tools.util;FileWriterUtil;true;getWriter;(File,String);;Argument[0];path-injection;manual"
- new FileWriterUtil().getWriter((File) source(), (String) null); // $ SSRF
- // "org.apache.cxf.tools.util;FileWriterUtil;true;getWriter;(String,String);;Argument[0];path-injection;manual"
- new FileWriterUtil().getWriter((String) source(), null); // $ SSRF
- // "org.apache.cxf.tools.util;FileWriterUtil;true;getWriter;(String,String);;Argument[1];path-injection;manual"
- new FileWriterUtil().getWriter((String) null, (String) source()); // $ SSRF
- // "org.apache.cxf.tools.util;FileWriterUtil;true;getWriter;(String,String,String);;Argument[0];path-injection;manual"
- new FileWriterUtil().getWriter((String) source(), null, null); // $ SSRF
- // "org.apache.cxf.tools.util;FileWriterUtil;true;getWriter;(String,String,String);;Argument[1];path-injection;manual"
- new FileWriterUtil().getWriter((String) null, (String) source(), null); // $ SSRF
- // "org.apache.cxf.tools.util;OutputStreamCreator;true;createOutputStream;(File);;Argument[0];path-injection;manual"
- new OutputStreamCreator().createOutputStream((File) source()); // $ SSRF
- // @formatter:on
- }
-
- void test(AntClassLoader acl) {
- // "org.apache.tools.ant;AntClassLoader;true;addPathComponent;(File);;Argument[0];read-file;ai-generated"
- acl.addPathComponent((File) source());
- // "org.apache.tools.ant;AntClassLoader;true;AntClassLoader;(ClassLoader,Project,Path,boolean);;Argument[2];read-file;ai-generated"
- new AntClassLoader(null, null, (org.apache.tools.ant.types.Path) source(), false);
- // "org.apache.tools.ant;AntClassLoader;true;AntClassLoader;(Project,Path,boolean);;Argument[1];read-file;ai-generated"
- new AntClassLoader(null, (org.apache.tools.ant.types.Path) source(), false);
- // "org.apache.tools.ant;AntClassLoader;true;AntClassLoader;(Project,Path);;Argument[1];read-file;ai-generated"
- new AntClassLoader(null, (org.apache.tools.ant.types.Path) source());
- // "org.kohsuke.stapler.framework.io;LargeText;true;LargeText;(File,Charset,boolean,boolean);;Argument[0];read-file;ai-generated"
- new LargeText((File) source(), null, false, false);
- }
-
- void test(DirectoryScanner ds) {
- // "org.apache.tools.ant;DirectoryScanner;true;setBasedir;(File);;Argument[0];read-file;ai-generated"
- ds.setBasedir((File) source());
- }
-
- void test(Copy cp) {
- // "org.apache.tools.ant.taskdefs;Copy;true;addFileset;(FileSet);;Argument[0];read-file;ai-generated"
- cp.addFileset((FileSet) source());
- // "org.apache.tools.ant.taskdefs;Copy;true;setFile;(File);;Argument[0];read-file;ai-generated"
- cp.setFile((File) source());
- // "org.apache.tools.ant.taskdefs;Copy;true;setTodir;(File);;Argument[0];create-file;ai-generated"
- cp.setTodir((File) source());
- // "org.apache.tools.ant.taskdefs;Copy;true;setTofile;(File);;Argument[0];create-file;ai-generated"
- cp.setTofile((File) source());
- }
-
- void test(Expand ex) {
- // "org.apache.tools.ant.taskdefs;Expand;true;setDest;(File);;Argument[0];create-file;ai-generated"
- ex.setDest((File) source());
- // "org.apache.tools.ant.taskdefs;Expand;true;setSrc;(File);;Argument[0];read-file;ai-generated"
- ex.setSrc((File) source());
- }
-
- void test(ChainedOptionsBuilder cob) {
- // "org.openjdk.jmh.runner.options;ChainedOptionsBuilder;true;result;(String);;Argument[0];create-file;ai-generated"
- cob.result((String) source());
- }
-}
From 1d2a51c522c1357ec94c91585be310dacd3b1bf2 Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Fri, 26 Jan 2024 12:20:47 +0100
Subject: [PATCH 021/378] Rename change note
---
...mproved.md => 2024-01-26-path-injection-precision-improved.md} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename java/ql/src/change-notes/{2023-04-20-path-injection-precision-improved.md => 2024-01-26-path-injection-precision-improved.md} (100%)
diff --git a/java/ql/src/change-notes/2023-04-20-path-injection-precision-improved.md b/java/ql/src/change-notes/2024-01-26-path-injection-precision-improved.md
similarity index 100%
rename from java/ql/src/change-notes/2023-04-20-path-injection-precision-improved.md
rename to java/ql/src/change-notes/2024-01-26-path-injection-precision-improved.md
From 2a146405ac32f2c2372764a4c379104797b1a648 Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Fri, 26 Jan 2024 12:31:48 +0100
Subject: [PATCH 022/378] Adjust tests
---
.../Security/CWE/CWE-073/FilePathInjection.ql | 7 -
.../CWE-073/FilePathInjection.expected | 3 -
.../CWE-022/semmle/tests/TaintedPath.expected | 494 ------------------
.../CWE-022/semmle/tests/TaintedPath.java | 40 +-
.../CWE-022/semmle/tests/TaintedPath.ql | 9 +-
.../security/CWE-022/semmle/tests/Test.java | 2 -
6 files changed, 22 insertions(+), 533 deletions(-)
diff --git a/java/ql/src/experimental/Security/CWE/CWE-073/FilePathInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-073/FilePathInjection.ql
index 7f6528a6670..6fab554ac67 100644
--- a/java/ql/src/experimental/Security/CWE/CWE-073/FilePathInjection.ql
+++ b/java/ql/src/experimental/Security/CWE/CWE-073/FilePathInjection.ql
@@ -16,10 +16,7 @@ import java
import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.dataflow.ExternalFlow
import semmle.code.java.dataflow.FlowSources
-<<<<<<< HEAD
-=======
import semmle.code.java.security.TaintedPathQuery
->>>>>>> 9e469c9c32 (Migrate path injection sinks to MaD)
import JFinalController
import semmle.code.java.security.PathSanitizer
private import semmle.code.java.security.Sanitizers
@@ -56,11 +53,7 @@ module InjectFilePathConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof ThreatModelFlowSource }
predicate isSink(DataFlow::Node sink) {
-<<<<<<< HEAD
- sinkNode(sink, "path-injection") and
-=======
sink instanceof TaintedPathSink and
->>>>>>> 9e469c9c32 (Migrate path injection sinks to MaD)
not sink instanceof NormalizedPathNode
}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-073/FilePathInjection.expected b/java/ql/test/experimental/query-tests/security/CWE-073/FilePathInjection.expected
index cd2b49f28c1..07be573dbf8 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-073/FilePathInjection.expected
+++ b/java/ql/test/experimental/query-tests/security/CWE-073/FilePathInjection.expected
@@ -3,7 +3,6 @@ edges
| FilePathInjection.java:64:21:64:34 | getPara(...) : String | FilePathInjection.java:72:47:72:59 | finalFilePath |
| FilePathInjection.java:87:21:87:34 | getPara(...) : String | FilePathInjection.java:95:47:95:59 | finalFilePath |
| FilePathInjection.java:177:50:177:58 | file : File | FilePathInjection.java:182:30:182:33 | file |
-| FilePathInjection.java:205:17:205:44 | getParameter(...) : String | FilePathInjection.java:209:24:209:31 | filePath |
| FilePathInjection.java:205:17:205:44 | getParameter(...) : String | FilePathInjection.java:209:24:209:31 | filePath : String |
| FilePathInjection.java:209:15:209:32 | new File(...) : File | FilePathInjection.java:217:19:217:22 | file : File |
| FilePathInjection.java:209:24:209:31 | filePath : String | FilePathInjection.java:209:15:209:32 | new File(...) : File |
@@ -19,7 +18,6 @@ nodes
| FilePathInjection.java:182:30:182:33 | file | semmle.label | file |
| FilePathInjection.java:205:17:205:44 | getParameter(...) : String | semmle.label | getParameter(...) : String |
| FilePathInjection.java:209:15:209:32 | new File(...) : File | semmle.label | new File(...) : File |
-| FilePathInjection.java:209:24:209:31 | filePath | semmle.label | filePath |
| FilePathInjection.java:209:24:209:31 | filePath : String | semmle.label | filePath : String |
| FilePathInjection.java:217:19:217:22 | file : File | semmle.label | file : File |
subpaths
@@ -28,4 +26,3 @@ subpaths
| FilePathInjection.java:72:47:72:59 | finalFilePath | FilePathInjection.java:64:21:64:34 | getPara(...) : String | FilePathInjection.java:72:47:72:59 | finalFilePath | External control of file name or path due to $@. | FilePathInjection.java:64:21:64:34 | getPara(...) | user-provided value |
| FilePathInjection.java:95:47:95:59 | finalFilePath | FilePathInjection.java:87:21:87:34 | getPara(...) : String | FilePathInjection.java:95:47:95:59 | finalFilePath | External control of file name or path due to $@. | FilePathInjection.java:87:21:87:34 | getPara(...) | user-provided value |
| FilePathInjection.java:182:30:182:33 | file | FilePathInjection.java:205:17:205:44 | getParameter(...) : String | FilePathInjection.java:182:30:182:33 | file | External control of file name or path due to $@. | FilePathInjection.java:205:17:205:44 | getParameter(...) | user-provided value |
-| FilePathInjection.java:209:24:209:31 | filePath | FilePathInjection.java:205:17:205:44 | getParameter(...) : String | FilePathInjection.java:209:24:209:31 | filePath | External control of file name or path due to $@. | FilePathInjection.java:205:17:205:44 | getParameter(...) | user-provided value |
diff --git a/java/ql/test/query-tests/security/CWE-022/semmle/tests/TaintedPath.expected b/java/ql/test/query-tests/security/CWE-022/semmle/tests/TaintedPath.expected
index 0e2d90c3709..e69de29bb2d 100644
--- a/java/ql/test/query-tests/security/CWE-022/semmle/tests/TaintedPath.expected
+++ b/java/ql/test/query-tests/security/CWE-022/semmle/tests/TaintedPath.expected
@@ -1,494 +0,0 @@
-edges
-| TaintedPath.java:12:38:12:110 | new BufferedReader(...) : BufferedReader | TaintedPath.java:13:24:13:37 | filenameReader : BufferedReader |
-| TaintedPath.java:12:57:12:109 | new InputStreamReader(...) : InputStreamReader | TaintedPath.java:12:38:12:110 | new BufferedReader(...) : BufferedReader |
-| TaintedPath.java:12:79:12:99 | getInputStream(...) : InputStream | TaintedPath.java:12:57:12:109 | new InputStreamReader(...) : InputStreamReader |
-| TaintedPath.java:13:24:13:37 | filenameReader : BufferedReader | TaintedPath.java:13:24:13:48 | readLine(...) : String |
-| TaintedPath.java:13:24:13:48 | readLine(...) : String | TaintedPath.java:15:68:15:75 | filename |
-| TaintedPath.java:38:41:39:70 | new BufferedReader(...) : BufferedReader | TaintedPath.java:40:27:40:40 | filenameReader : BufferedReader |
-| TaintedPath.java:39:17:39:69 | new InputStreamReader(...) : InputStreamReader | TaintedPath.java:38:41:39:70 | new BufferedReader(...) : BufferedReader |
-| TaintedPath.java:39:39:39:59 | getInputStream(...) : InputStream | TaintedPath.java:39:17:39:69 | new InputStreamReader(...) : InputStreamReader |
-| TaintedPath.java:40:27:40:40 | filenameReader : BufferedReader | TaintedPath.java:40:27:40:51 | readLine(...) : String |
-| TaintedPath.java:40:27:40:51 | readLine(...) : String | TaintedPath.java:43:46:43:53 | filename |
-| Test.java:19:18:19:38 | getHostName(...) : String | Test.java:24:20:24:23 | temp |
-| Test.java:19:18:19:38 | getHostName(...) : String | Test.java:27:21:27:24 | temp |
-| Test.java:19:18:19:38 | getHostName(...) : String | Test.java:30:44:30:47 | temp |
-| Test.java:19:18:19:38 | getHostName(...) : String | Test.java:34:21:34:24 | temp |
-| Test.java:79:33:79:99 | new BufferedReader(...) : BufferedReader | Test.java:80:31:80:32 | br : BufferedReader |
-| Test.java:79:52:79:98 | new InputStreamReader(...) : InputStreamReader | Test.java:79:33:79:99 | new BufferedReader(...) : BufferedReader |
-| Test.java:79:74:79:97 | getInputStream(...) : ServletInputStream | Test.java:79:52:79:98 | new InputStreamReader(...) : InputStreamReader |
-| Test.java:80:31:80:32 | br : BufferedReader | Test.java:80:31:80:43 | readLine(...) : String |
-| Test.java:80:31:80:43 | readLine(...) : String | Test.java:82:67:82:81 | ... + ... |
-| Test.java:88:17:88:37 | getHostName(...) : String | Test.java:90:26:90:29 | temp |
-| Test.java:95:14:95:34 | getHostName(...) : String | Test.java:97:26:97:26 | t : String |
-| Test.java:97:26:97:26 | t : String | Test.java:97:12:97:33 | new URI(...) |
-| Test.java:97:26:97:26 | t : String | Test.java:98:23:98:23 | t : String |
-| Test.java:98:23:98:23 | t : String | Test.java:98:12:98:33 | new URI(...) |
-| Test.java:98:23:98:23 | t : String | Test.java:99:29:99:29 | t : String |
-| Test.java:99:29:99:29 | t : String | Test.java:99:12:99:33 | new URI(...) |
-| Test.java:99:29:99:29 | t : String | Test.java:100:32:100:32 | t : String |
-| Test.java:100:32:100:32 | t : String | Test.java:100:12:100:45 | new URI(...) |
-| Test.java:100:32:100:32 | t : String | Test.java:101:41:101:41 | t : String |
-| Test.java:101:41:101:41 | t : String | Test.java:101:12:101:54 | new URI(...) |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:45:61:45:68 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:47:41:47:48 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:49:56:49:63 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:51:46:51:53 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:53:38:53:45 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:55:36:55:43 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:57:31:57:38 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:59:33:59:40 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:61:27:61:34 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:63:27:63:34 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:65:40:65:47 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:67:47:67:54 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:69:40:69:47 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:71:38:71:45 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:73:33:73:40 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:75:33:75:40 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:77:41:77:48 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:79:42:79:49 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:81:37:81:44 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:83:29:83:36 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:85:37:85:44 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:87:28:87:35 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:89:33:89:40 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:91:40:91:47 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:93:40:93:47 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:94:40:94:47 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:96:38:96:45 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:98:28:98:35 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:99:28:99:35 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:100:28:100:35 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:102:34:102:41 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:103:34:103:41 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:105:33:105:40 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:107:42:107:49 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:109:50:109:57 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:111:54:111:61 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:113:50:113:57 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:115:35:115:42 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:117:48:117:55 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:119:37:119:44 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:121:35:121:42 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:123:55:123:62 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:125:47:125:54 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:127:47:127:54 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:131:35:131:42 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:135:56:135:63 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:137:56:137:63 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:141:59:141:66 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:143:59:143:66 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:145:58:145:65 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:147:34:147:41 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:149:34:149:41 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:151:44:151:51 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:153:31:153:38 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:155:46:155:53 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:157:46:157:53 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:159:67:159:74 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:161:67:161:74 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:163:73:163:80 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:166:37:166:44 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:168:48:168:55 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:170:54:170:61 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:172:69:172:76 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:174:47:174:54 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:176:49:176:56 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:178:64:178:71 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:180:49:180:56 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:182:64:182:71 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:184:61:184:68 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:190:37:190:44 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:192:74:192:81 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:194:68:194:75 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:196:68:196:75 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:198:30:198:37 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:203:30:203:37 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:208:33:208:40 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:210:27:210:34 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:212:28:212:35 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:214:29:214:36 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:219:27:219:34 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:221:26:221:33 | source(...) : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:226:29:226:36 | source(...) : String |
-| mad/Test.java:45:61:45:68 | source(...) : String | mad/Test.java:45:52:45:68 | (...)... |
-| mad/Test.java:47:41:47:48 | source(...) : String | mad/Test.java:47:32:47:48 | (...)... |
-| mad/Test.java:49:56:49:63 | source(...) : String | mad/Test.java:49:47:49:63 | (...)... |
-| mad/Test.java:51:46:51:53 | source(...) : String | mad/Test.java:51:39:51:53 | (...)... |
-| mad/Test.java:53:38:53:45 | source(...) : String | mad/Test.java:53:31:53:45 | (...)... |
-| mad/Test.java:55:36:55:43 | source(...) : String | mad/Test.java:55:29:55:43 | (...)... |
-| mad/Test.java:57:31:57:38 | source(...) : String | mad/Test.java:57:24:57:38 | (...)... |
-| mad/Test.java:59:33:59:40 | source(...) : String | mad/Test.java:59:24:59:40 | (...)... |
-| mad/Test.java:61:27:61:34 | source(...) : String | mad/Test.java:61:20:61:34 | (...)... |
-| mad/Test.java:63:27:63:34 | source(...) : String | mad/Test.java:63:20:63:34 | (...)... |
-| mad/Test.java:65:40:65:47 | source(...) : String | mad/Test.java:65:33:65:47 | (...)... |
-| mad/Test.java:67:47:67:54 | source(...) : String | mad/Test.java:67:40:67:54 | (...)... |
-| mad/Test.java:69:40:69:47 | source(...) : String | mad/Test.java:69:33:69:47 | (...)... |
-| mad/Test.java:71:38:71:45 | source(...) : String | mad/Test.java:71:31:71:45 | (...)... |
-| mad/Test.java:73:33:73:40 | source(...) : String | mad/Test.java:73:26:73:40 | (...)... |
-| mad/Test.java:75:33:75:40 | source(...) : String | mad/Test.java:75:26:75:40 | (...)... |
-| mad/Test.java:77:41:77:48 | source(...) : String | mad/Test.java:77:34:77:48 | (...)... |
-| mad/Test.java:79:42:79:49 | source(...) : String | mad/Test.java:79:35:79:49 | (...)... |
-| mad/Test.java:81:37:81:44 | source(...) : String | mad/Test.java:81:30:81:44 | (...)... |
-| mad/Test.java:83:29:83:36 | source(...) : String | mad/Test.java:83:22:83:36 | (...)... |
-| mad/Test.java:85:37:85:44 | source(...) : String | mad/Test.java:85:30:85:44 | (...)... |
-| mad/Test.java:87:28:87:35 | source(...) : String | mad/Test.java:87:21:87:35 | (...)... |
-| mad/Test.java:89:33:89:40 | source(...) : String | mad/Test.java:89:26:89:40 | (...)... |
-| mad/Test.java:91:40:91:47 | source(...) : String | mad/Test.java:91:33:91:47 | (...)... |
-| mad/Test.java:93:40:93:47 | source(...) : String | mad/Test.java:93:33:93:47 | (...)... |
-| mad/Test.java:94:40:94:47 | source(...) : String | mad/Test.java:94:33:94:47 | (...)... |
-| mad/Test.java:96:38:96:45 | source(...) : String | mad/Test.java:96:31:96:45 | (...)... |
-| mad/Test.java:98:28:98:35 | source(...) : String | mad/Test.java:98:21:98:35 | (...)... |
-| mad/Test.java:99:28:99:35 | source(...) : String | mad/Test.java:99:21:99:35 | (...)... |
-| mad/Test.java:100:28:100:35 | source(...) : String | mad/Test.java:100:21:100:35 | (...)... |
-| mad/Test.java:102:34:102:41 | source(...) : String | mad/Test.java:102:27:102:41 | (...)... |
-| mad/Test.java:103:34:103:41 | source(...) : String | mad/Test.java:103:27:103:41 | (...)... |
-| mad/Test.java:105:33:105:40 | source(...) : String | mad/Test.java:105:26:105:40 | (...)... |
-| mad/Test.java:107:42:107:49 | source(...) : String | mad/Test.java:107:35:107:49 | (...)... |
-| mad/Test.java:109:50:109:57 | source(...) : String | mad/Test.java:109:41:109:57 | (...)... |
-| mad/Test.java:111:54:111:61 | source(...) : String | mad/Test.java:111:45:111:61 | (...)... |
-| mad/Test.java:113:50:113:57 | source(...) : String | mad/Test.java:113:43:113:57 | (...)... |
-| mad/Test.java:115:35:115:42 | source(...) : String | mad/Test.java:115:28:115:42 | (...)... |
-| mad/Test.java:117:48:117:55 | source(...) : String | mad/Test.java:117:41:117:55 | (...)... |
-| mad/Test.java:119:37:119:44 | source(...) : String | mad/Test.java:119:28:119:44 | (...)... |
-| mad/Test.java:121:35:121:42 | source(...) : String | mad/Test.java:121:28:121:42 | (...)... |
-| mad/Test.java:123:55:123:62 | source(...) : String | mad/Test.java:123:46:123:62 | (...)... |
-| mad/Test.java:125:47:125:54 | source(...) : String | mad/Test.java:125:40:125:54 | (...)... |
-| mad/Test.java:127:47:127:54 | source(...) : String | mad/Test.java:127:40:127:54 | (...)... |
-| mad/Test.java:131:35:131:42 | source(...) : String | mad/Test.java:131:26:131:42 | (...)... |
-| mad/Test.java:135:56:135:63 | source(...) : String | mad/Test.java:135:49:135:63 | (...)... |
-| mad/Test.java:137:56:137:63 | source(...) : String | mad/Test.java:137:49:137:63 | (...)... |
-| mad/Test.java:141:59:141:66 | source(...) : String | mad/Test.java:141:52:141:66 | (...)... |
-| mad/Test.java:143:59:143:66 | source(...) : String | mad/Test.java:143:52:143:66 | (...)... |
-| mad/Test.java:145:58:145:65 | source(...) : String | mad/Test.java:145:49:145:65 | (...)... |
-| mad/Test.java:147:34:147:41 | source(...) : String | mad/Test.java:147:25:147:41 | (...)... |
-| mad/Test.java:149:34:149:41 | source(...) : String | mad/Test.java:149:25:149:41 | (...)... |
-| mad/Test.java:151:44:151:51 | source(...) : String | mad/Test.java:151:35:151:51 | (...)... |
-| mad/Test.java:153:31:153:38 | source(...) : String | mad/Test.java:153:24:153:38 | (...)... |
-| mad/Test.java:155:46:155:53 | source(...) : String | mad/Test.java:155:37:155:53 | (...)... |
-| mad/Test.java:157:46:157:53 | source(...) : String | mad/Test.java:157:37:157:53 | (...)... |
-| mad/Test.java:159:67:159:74 | source(...) : String | mad/Test.java:159:58:159:74 | (...)... |
-| mad/Test.java:161:67:161:74 | source(...) : String | mad/Test.java:161:58:161:74 | (...)... |
-| mad/Test.java:163:73:163:80 | source(...) : String | mad/Test.java:163:64:163:80 | (...)... |
-| mad/Test.java:166:37:166:44 | source(...) : String | mad/Test.java:166:28:166:44 | (...)... |
-| mad/Test.java:168:48:168:55 | source(...) : String | mad/Test.java:168:39:168:55 | (...)... |
-| mad/Test.java:170:54:170:61 | source(...) : String | mad/Test.java:170:45:170:61 | (...)... |
-| mad/Test.java:172:69:172:76 | source(...) : String | mad/Test.java:172:60:172:76 | (...)... |
-| mad/Test.java:174:47:174:54 | source(...) : String | mad/Test.java:174:40:174:54 | (...)... |
-| mad/Test.java:176:49:176:56 | source(...) : String | mad/Test.java:176:40:176:56 | (...)... |
-| mad/Test.java:178:64:178:71 | source(...) : String | mad/Test.java:178:55:178:71 | (...)... |
-| mad/Test.java:180:49:180:56 | source(...) : String | mad/Test.java:180:40:180:56 | (...)... |
-| mad/Test.java:182:64:182:71 | source(...) : String | mad/Test.java:182:55:182:71 | (...)... |
-| mad/Test.java:184:61:184:68 | source(...) : String | mad/Test.java:184:54:184:68 | (...)... |
-| mad/Test.java:190:37:190:44 | source(...) : String | mad/Test.java:190:30:190:44 | (...)... |
-| mad/Test.java:192:74:192:81 | source(...) : String | mad/Test.java:192:40:192:81 | (...)... |
-| mad/Test.java:194:68:194:75 | source(...) : String | mad/Test.java:194:34:194:75 | (...)... |
-| mad/Test.java:196:68:196:75 | source(...) : String | mad/Test.java:196:34:196:75 | (...)... |
-| mad/Test.java:198:30:198:37 | source(...) : String | mad/Test.java:198:23:198:37 | (...)... |
-| mad/Test.java:203:30:203:37 | source(...) : String | mad/Test.java:203:23:203:37 | (...)... |
-| mad/Test.java:208:33:208:40 | source(...) : String | mad/Test.java:208:23:208:40 | (...)... |
-| mad/Test.java:210:27:210:34 | source(...) : String | mad/Test.java:210:20:210:34 | (...)... |
-| mad/Test.java:212:28:212:35 | source(...) : String | mad/Test.java:212:21:212:35 | (...)... |
-| mad/Test.java:214:29:214:36 | source(...) : String | mad/Test.java:214:22:214:36 | (...)... |
-| mad/Test.java:219:27:219:34 | source(...) : String | mad/Test.java:219:20:219:34 | (...)... |
-| mad/Test.java:221:26:221:33 | source(...) : String | mad/Test.java:221:19:221:33 | (...)... |
-| mad/Test.java:226:29:226:36 | source(...) : String | mad/Test.java:226:20:226:36 | (...)... |
-nodes
-| TaintedPath.java:12:38:12:110 | new BufferedReader(...) : BufferedReader | semmle.label | new BufferedReader(...) : BufferedReader |
-| TaintedPath.java:12:57:12:109 | new InputStreamReader(...) : InputStreamReader | semmle.label | new InputStreamReader(...) : InputStreamReader |
-| TaintedPath.java:12:79:12:99 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| TaintedPath.java:13:24:13:37 | filenameReader : BufferedReader | semmle.label | filenameReader : BufferedReader |
-| TaintedPath.java:13:24:13:48 | readLine(...) : String | semmle.label | readLine(...) : String |
-| TaintedPath.java:15:68:15:75 | filename | semmle.label | filename |
-| TaintedPath.java:38:41:39:70 | new BufferedReader(...) : BufferedReader | semmle.label | new BufferedReader(...) : BufferedReader |
-| TaintedPath.java:39:17:39:69 | new InputStreamReader(...) : InputStreamReader | semmle.label | new InputStreamReader(...) : InputStreamReader |
-| TaintedPath.java:39:39:39:59 | getInputStream(...) : InputStream | semmle.label | getInputStream(...) : InputStream |
-| TaintedPath.java:40:27:40:40 | filenameReader : BufferedReader | semmle.label | filenameReader : BufferedReader |
-| TaintedPath.java:40:27:40:51 | readLine(...) : String | semmle.label | readLine(...) : String |
-| TaintedPath.java:43:46:43:53 | filename | semmle.label | filename |
-| Test.java:19:18:19:38 | getHostName(...) : String | semmle.label | getHostName(...) : String |
-| Test.java:24:20:24:23 | temp | semmle.label | temp |
-| Test.java:27:21:27:24 | temp | semmle.label | temp |
-| Test.java:30:44:30:47 | temp | semmle.label | temp |
-| Test.java:34:21:34:24 | temp | semmle.label | temp |
-| Test.java:79:33:79:99 | new BufferedReader(...) : BufferedReader | semmle.label | new BufferedReader(...) : BufferedReader |
-| Test.java:79:52:79:98 | new InputStreamReader(...) : InputStreamReader | semmle.label | new InputStreamReader(...) : InputStreamReader |
-| Test.java:79:74:79:97 | getInputStream(...) : ServletInputStream | semmle.label | getInputStream(...) : ServletInputStream |
-| Test.java:80:31:80:32 | br : BufferedReader | semmle.label | br : BufferedReader |
-| Test.java:80:31:80:43 | readLine(...) : String | semmle.label | readLine(...) : String |
-| Test.java:82:67:82:81 | ... + ... | semmle.label | ... + ... |
-| Test.java:88:17:88:37 | getHostName(...) : String | semmle.label | getHostName(...) : String |
-| Test.java:90:26:90:29 | temp | semmle.label | temp |
-| Test.java:95:14:95:34 | getHostName(...) : String | semmle.label | getHostName(...) : String |
-| Test.java:97:12:97:33 | new URI(...) | semmle.label | new URI(...) |
-| Test.java:97:26:97:26 | t : String | semmle.label | t : String |
-| Test.java:98:12:98:33 | new URI(...) | semmle.label | new URI(...) |
-| Test.java:98:23:98:23 | t : String | semmle.label | t : String |
-| Test.java:99:12:99:33 | new URI(...) | semmle.label | new URI(...) |
-| Test.java:99:29:99:29 | t : String | semmle.label | t : String |
-| Test.java:100:12:100:45 | new URI(...) | semmle.label | new URI(...) |
-| Test.java:100:32:100:32 | t : String | semmle.label | t : String |
-| Test.java:101:12:101:54 | new URI(...) | semmle.label | new URI(...) |
-| Test.java:101:41:101:41 | t : String | semmle.label | t : String |
-| mad/Test.java:40:16:40:36 | getHostName(...) : String | semmle.label | getHostName(...) : String |
-| mad/Test.java:45:52:45:68 | (...)... | semmle.label | (...)... |
-| mad/Test.java:45:61:45:68 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:47:32:47:48 | (...)... | semmle.label | (...)... |
-| mad/Test.java:47:41:47:48 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:49:47:49:63 | (...)... | semmle.label | (...)... |
-| mad/Test.java:49:56:49:63 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:51:39:51:53 | (...)... | semmle.label | (...)... |
-| mad/Test.java:51:46:51:53 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:53:31:53:45 | (...)... | semmle.label | (...)... |
-| mad/Test.java:53:38:53:45 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:55:29:55:43 | (...)... | semmle.label | (...)... |
-| mad/Test.java:55:36:55:43 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:57:24:57:38 | (...)... | semmle.label | (...)... |
-| mad/Test.java:57:31:57:38 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:59:24:59:40 | (...)... | semmle.label | (...)... |
-| mad/Test.java:59:33:59:40 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:61:20:61:34 | (...)... | semmle.label | (...)... |
-| mad/Test.java:61:27:61:34 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:63:20:63:34 | (...)... | semmle.label | (...)... |
-| mad/Test.java:63:27:63:34 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:65:33:65:47 | (...)... | semmle.label | (...)... |
-| mad/Test.java:65:40:65:47 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:67:40:67:54 | (...)... | semmle.label | (...)... |
-| mad/Test.java:67:47:67:54 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:69:33:69:47 | (...)... | semmle.label | (...)... |
-| mad/Test.java:69:40:69:47 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:71:31:71:45 | (...)... | semmle.label | (...)... |
-| mad/Test.java:71:38:71:45 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:73:26:73:40 | (...)... | semmle.label | (...)... |
-| mad/Test.java:73:33:73:40 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:75:26:75:40 | (...)... | semmle.label | (...)... |
-| mad/Test.java:75:33:75:40 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:77:34:77:48 | (...)... | semmle.label | (...)... |
-| mad/Test.java:77:41:77:48 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:79:35:79:49 | (...)... | semmle.label | (...)... |
-| mad/Test.java:79:42:79:49 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:81:30:81:44 | (...)... | semmle.label | (...)... |
-| mad/Test.java:81:37:81:44 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:83:22:83:36 | (...)... | semmle.label | (...)... |
-| mad/Test.java:83:29:83:36 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:85:30:85:44 | (...)... | semmle.label | (...)... |
-| mad/Test.java:85:37:85:44 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:87:21:87:35 | (...)... | semmle.label | (...)... |
-| mad/Test.java:87:28:87:35 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:89:26:89:40 | (...)... | semmle.label | (...)... |
-| mad/Test.java:89:33:89:40 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:91:33:91:47 | (...)... | semmle.label | (...)... |
-| mad/Test.java:91:40:91:47 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:93:33:93:47 | (...)... | semmle.label | (...)... |
-| mad/Test.java:93:40:93:47 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:94:33:94:47 | (...)... | semmle.label | (...)... |
-| mad/Test.java:94:40:94:47 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:96:31:96:45 | (...)... | semmle.label | (...)... |
-| mad/Test.java:96:38:96:45 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:98:21:98:35 | (...)... | semmle.label | (...)... |
-| mad/Test.java:98:28:98:35 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:99:21:99:35 | (...)... | semmle.label | (...)... |
-| mad/Test.java:99:28:99:35 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:100:21:100:35 | (...)... | semmle.label | (...)... |
-| mad/Test.java:100:28:100:35 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:102:27:102:41 | (...)... | semmle.label | (...)... |
-| mad/Test.java:102:34:102:41 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:103:27:103:41 | (...)... | semmle.label | (...)... |
-| mad/Test.java:103:34:103:41 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:105:26:105:40 | (...)... | semmle.label | (...)... |
-| mad/Test.java:105:33:105:40 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:107:35:107:49 | (...)... | semmle.label | (...)... |
-| mad/Test.java:107:42:107:49 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:109:41:109:57 | (...)... | semmle.label | (...)... |
-| mad/Test.java:109:50:109:57 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:111:45:111:61 | (...)... | semmle.label | (...)... |
-| mad/Test.java:111:54:111:61 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:113:43:113:57 | (...)... | semmle.label | (...)... |
-| mad/Test.java:113:50:113:57 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:115:28:115:42 | (...)... | semmle.label | (...)... |
-| mad/Test.java:115:35:115:42 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:117:41:117:55 | (...)... | semmle.label | (...)... |
-| mad/Test.java:117:48:117:55 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:119:28:119:44 | (...)... | semmle.label | (...)... |
-| mad/Test.java:119:37:119:44 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:121:28:121:42 | (...)... | semmle.label | (...)... |
-| mad/Test.java:121:35:121:42 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:123:46:123:62 | (...)... | semmle.label | (...)... |
-| mad/Test.java:123:55:123:62 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:125:40:125:54 | (...)... | semmle.label | (...)... |
-| mad/Test.java:125:47:125:54 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:127:40:127:54 | (...)... | semmle.label | (...)... |
-| mad/Test.java:127:47:127:54 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:131:26:131:42 | (...)... | semmle.label | (...)... |
-| mad/Test.java:131:35:131:42 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:135:49:135:63 | (...)... | semmle.label | (...)... |
-| mad/Test.java:135:56:135:63 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:137:49:137:63 | (...)... | semmle.label | (...)... |
-| mad/Test.java:137:56:137:63 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:141:52:141:66 | (...)... | semmle.label | (...)... |
-| mad/Test.java:141:59:141:66 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:143:52:143:66 | (...)... | semmle.label | (...)... |
-| mad/Test.java:143:59:143:66 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:145:49:145:65 | (...)... | semmle.label | (...)... |
-| mad/Test.java:145:58:145:65 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:147:25:147:41 | (...)... | semmle.label | (...)... |
-| mad/Test.java:147:34:147:41 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:149:25:149:41 | (...)... | semmle.label | (...)... |
-| mad/Test.java:149:34:149:41 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:151:35:151:51 | (...)... | semmle.label | (...)... |
-| mad/Test.java:151:44:151:51 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:153:24:153:38 | (...)... | semmle.label | (...)... |
-| mad/Test.java:153:31:153:38 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:155:37:155:53 | (...)... | semmle.label | (...)... |
-| mad/Test.java:155:46:155:53 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:157:37:157:53 | (...)... | semmle.label | (...)... |
-| mad/Test.java:157:46:157:53 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:159:58:159:74 | (...)... | semmle.label | (...)... |
-| mad/Test.java:159:67:159:74 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:161:58:161:74 | (...)... | semmle.label | (...)... |
-| mad/Test.java:161:67:161:74 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:163:64:163:80 | (...)... | semmle.label | (...)... |
-| mad/Test.java:163:73:163:80 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:166:28:166:44 | (...)... | semmle.label | (...)... |
-| mad/Test.java:166:37:166:44 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:168:39:168:55 | (...)... | semmle.label | (...)... |
-| mad/Test.java:168:48:168:55 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:170:45:170:61 | (...)... | semmle.label | (...)... |
-| mad/Test.java:170:54:170:61 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:172:60:172:76 | (...)... | semmle.label | (...)... |
-| mad/Test.java:172:69:172:76 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:174:40:174:54 | (...)... | semmle.label | (...)... |
-| mad/Test.java:174:47:174:54 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:176:40:176:56 | (...)... | semmle.label | (...)... |
-| mad/Test.java:176:49:176:56 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:178:55:178:71 | (...)... | semmle.label | (...)... |
-| mad/Test.java:178:64:178:71 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:180:40:180:56 | (...)... | semmle.label | (...)... |
-| mad/Test.java:180:49:180:56 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:182:55:182:71 | (...)... | semmle.label | (...)... |
-| mad/Test.java:182:64:182:71 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:184:54:184:68 | (...)... | semmle.label | (...)... |
-| mad/Test.java:184:61:184:68 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:190:30:190:44 | (...)... | semmle.label | (...)... |
-| mad/Test.java:190:37:190:44 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:192:40:192:81 | (...)... | semmle.label | (...)... |
-| mad/Test.java:192:74:192:81 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:194:34:194:75 | (...)... | semmle.label | (...)... |
-| mad/Test.java:194:68:194:75 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:196:34:196:75 | (...)... | semmle.label | (...)... |
-| mad/Test.java:196:68:196:75 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:198:23:198:37 | (...)... | semmle.label | (...)... |
-| mad/Test.java:198:30:198:37 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:203:23:203:37 | (...)... | semmle.label | (...)... |
-| mad/Test.java:203:30:203:37 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:208:23:208:40 | (...)... | semmle.label | (...)... |
-| mad/Test.java:208:33:208:40 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:210:20:210:34 | (...)... | semmle.label | (...)... |
-| mad/Test.java:210:27:210:34 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:212:21:212:35 | (...)... | semmle.label | (...)... |
-| mad/Test.java:212:28:212:35 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:214:22:214:36 | (...)... | semmle.label | (...)... |
-| mad/Test.java:214:29:214:36 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:219:20:219:34 | (...)... | semmle.label | (...)... |
-| mad/Test.java:219:27:219:34 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:221:19:221:33 | (...)... | semmle.label | (...)... |
-| mad/Test.java:221:26:221:33 | source(...) : String | semmle.label | source(...) : String |
-| mad/Test.java:226:20:226:36 | (...)... | semmle.label | (...)... |
-| mad/Test.java:226:29:226:36 | source(...) : String | semmle.label | source(...) : String |
-subpaths
-#select
-| TaintedPath.java:15:53:15:76 | new FileReader(...) | TaintedPath.java:12:79:12:99 | getInputStream(...) : InputStream | TaintedPath.java:15:68:15:75 | filename | This path depends on a $@. | TaintedPath.java:12:79:12:99 | getInputStream(...) | user-provided value |
-| TaintedPath.java:43:25:43:54 | resolve(...) | TaintedPath.java:39:39:39:59 | getInputStream(...) : InputStream | TaintedPath.java:43:46:43:53 | filename | This path depends on a $@. | TaintedPath.java:39:39:39:59 | getInputStream(...) | user-provided value |
-| Test.java:24:11:24:24 | new File(...) | Test.java:19:18:19:38 | getHostName(...) : String | Test.java:24:20:24:23 | temp | This path depends on a $@. | Test.java:19:18:19:38 | getHostName(...) | user-provided value |
-| Test.java:27:11:27:25 | get(...) | Test.java:19:18:19:38 | getHostName(...) : String | Test.java:27:21:27:24 | temp | This path depends on a $@. | Test.java:19:18:19:38 | getHostName(...) | user-provided value |
-| Test.java:30:11:30:48 | getPath(...) | Test.java:19:18:19:38 | getHostName(...) : String | Test.java:30:44:30:47 | temp | This path depends on a $@. | Test.java:19:18:19:38 | getHostName(...) | user-provided value |
-| Test.java:34:12:34:25 | new File(...) | Test.java:19:18:19:38 | getHostName(...) : String | Test.java:34:21:34:24 | temp | This path depends on a $@. | Test.java:19:18:19:38 | getHostName(...) | user-provided value |
-| Test.java:82:52:82:88 | new FileWriter(...) | Test.java:79:74:79:97 | getInputStream(...) : ServletInputStream | Test.java:82:67:82:81 | ... + ... | This path depends on a $@. | Test.java:79:74:79:97 | getInputStream(...) | user-provided value |
-| Test.java:90:26:90:29 | temp | Test.java:88:17:88:37 | getHostName(...) : String | Test.java:90:26:90:29 | temp | This path depends on a $@. | Test.java:88:17:88:37 | getHostName(...) | user-provided value |
-| Test.java:97:3:97:34 | new File(...) | Test.java:95:14:95:34 | getHostName(...) : String | Test.java:97:12:97:33 | new URI(...) | This path depends on a $@. | Test.java:95:14:95:34 | getHostName(...) | user-provided value |
-| Test.java:98:3:98:34 | new File(...) | Test.java:95:14:95:34 | getHostName(...) : String | Test.java:98:12:98:33 | new URI(...) | This path depends on a $@. | Test.java:95:14:95:34 | getHostName(...) | user-provided value |
-| Test.java:99:3:99:34 | new File(...) | Test.java:95:14:95:34 | getHostName(...) : String | Test.java:99:12:99:33 | new URI(...) | This path depends on a $@. | Test.java:95:14:95:34 | getHostName(...) | user-provided value |
-| Test.java:100:3:100:46 | new File(...) | Test.java:95:14:95:34 | getHostName(...) : String | Test.java:100:12:100:45 | new URI(...) | This path depends on a $@. | Test.java:95:14:95:34 | getHostName(...) | user-provided value |
-| Test.java:101:3:101:55 | new File(...) | Test.java:95:14:95:34 | getHostName(...) : String | Test.java:101:12:101:54 | new URI(...) | This path depends on a $@. | Test.java:95:14:95:34 | getHostName(...) | user-provided value |
-| mad/Test.java:45:52:45:68 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:45:52:45:68 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:47:32:47:48 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:47:32:47:48 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:49:47:49:63 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:49:47:49:63 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:51:39:51:53 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:51:39:51:53 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:53:31:53:45 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:53:31:53:45 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:55:29:55:43 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:55:29:55:43 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:57:24:57:38 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:57:24:57:38 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:59:9:59:41 | new FileReader(...) | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:59:24:59:40 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:61:20:61:34 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:61:20:61:34 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:63:20:63:34 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:63:20:63:34 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:65:33:65:47 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:65:33:65:47 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:67:40:67:54 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:67:40:67:54 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:69:33:69:47 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:69:33:69:47 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:71:31:71:45 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:71:31:71:45 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:73:26:73:40 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:73:26:73:40 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:75:26:75:40 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:75:26:75:40 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:77:34:77:48 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:77:34:77:48 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:79:35:79:49 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:79:35:79:49 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:81:30:81:44 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:81:30:81:44 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:83:22:83:36 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:83:22:83:36 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:85:30:85:44 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:85:30:85:44 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:87:21:87:35 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:87:21:87:35 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:89:26:89:40 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:89:26:89:40 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:91:33:91:47 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:91:33:91:47 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:93:33:93:47 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:93:33:93:47 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:94:33:94:47 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:94:33:94:47 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:96:31:96:45 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:96:31:96:45 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:98:21:98:35 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:98:21:98:35 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:99:21:99:35 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:99:21:99:35 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:100:21:100:35 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:100:21:100:35 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:102:27:102:41 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:102:27:102:41 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:103:27:103:41 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:103:27:103:41 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:105:26:105:40 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:105:26:105:40 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:107:35:107:49 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:107:35:107:49 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:109:41:109:57 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:109:41:109:57 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:111:45:111:61 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:111:45:111:61 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:113:43:113:57 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:113:43:113:57 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:115:28:115:42 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:115:28:115:42 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:117:41:117:55 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:117:41:117:55 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:119:28:119:44 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:119:28:119:44 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:121:28:121:42 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:121:28:121:42 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:123:46:123:62 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:123:46:123:62 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:125:40:125:54 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:125:40:125:54 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:127:40:127:54 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:127:40:127:54 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:131:26:131:42 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:131:26:131:42 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:135:49:135:63 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:135:49:135:63 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:137:49:137:63 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:137:49:137:63 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:141:52:141:66 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:141:52:141:66 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:143:52:143:66 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:143:52:143:66 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:145:49:145:65 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:145:49:145:65 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:147:25:147:41 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:147:25:147:41 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:149:25:149:41 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:149:25:149:41 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:151:35:151:51 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:151:35:151:51 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:153:24:153:38 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:153:24:153:38 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:155:37:155:53 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:155:37:155:53 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:157:37:157:53 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:157:37:157:53 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:159:58:159:74 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:159:58:159:74 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:161:58:161:74 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:161:58:161:74 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:163:64:163:80 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:163:64:163:80 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:166:28:166:44 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:166:28:166:44 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:168:39:168:55 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:168:39:168:55 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:170:45:170:61 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:170:45:170:61 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:172:60:172:76 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:172:60:172:76 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:174:40:174:54 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:174:40:174:54 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:176:40:176:56 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:176:40:176:56 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:178:55:178:71 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:178:55:178:71 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:180:40:180:56 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:180:40:180:56 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:182:55:182:71 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:182:55:182:71 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:184:54:184:68 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:184:54:184:68 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:190:30:190:44 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:190:30:190:44 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:192:40:192:81 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:192:40:192:81 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:194:34:194:75 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:194:34:194:75 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:196:34:196:75 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:196:34:196:75 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:198:23:198:37 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:198:23:198:37 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:203:23:203:37 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:203:23:203:37 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:208:23:208:40 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:208:23:208:40 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:210:20:210:34 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:210:20:210:34 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:212:21:212:35 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:212:21:212:35 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:214:22:214:36 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:214:22:214:36 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:219:20:219:34 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:219:20:219:34 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:221:19:221:33 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:221:19:221:33 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
-| mad/Test.java:226:20:226:36 | (...)... | mad/Test.java:40:16:40:36 | getHostName(...) : String | mad/Test.java:226:20:226:36 | (...)... | This path depends on a $@. | mad/Test.java:40:16:40:36 | getHostName(...) | user-provided value |
diff --git a/java/ql/test/query-tests/security/CWE-022/semmle/tests/TaintedPath.java b/java/ql/test/query-tests/security/CWE-022/semmle/tests/TaintedPath.java
index a2ae561be58..8bfc35c1d96 100644
--- a/java/ql/test/query-tests/security/CWE-022/semmle/tests/TaintedPath.java
+++ b/java/ql/test/query-tests/security/CWE-022/semmle/tests/TaintedPath.java
@@ -9,25 +9,27 @@ import java.nio.file.Paths;
public class TaintedPath {
public void sendUserFile(Socket sock, String user) throws IOException {
- BufferedReader filenameReader = new BufferedReader(new InputStreamReader(sock.getInputStream(), "UTF-8"));
- String filename = filenameReader.readLine();
- // BAD: read from a file without checking its path
- BufferedReader fileReader = new BufferedReader(new FileReader(filename));
+ BufferedReader filenameReader =
+ new BufferedReader(new InputStreamReader(sock.getInputStream(), "UTF-8"));
+ String filename = filenameReader.readLine();
+ // BAD: read from a file without checking its path
+ BufferedReader fileReader = new BufferedReader(new FileReader(filename)); // $ hasTaintFlow
String fileLine = fileReader.readLine();
- while(fileLine != null) {
- sock.getOutputStream().write(fileLine.getBytes());
- fileLine = fileReader.readLine();
+ while (fileLine != null) {
+ sock.getOutputStream().write(fileLine.getBytes());
+ fileLine = fileReader.readLine();
}
}
public void sendUserFileGood(Socket sock, String user) throws IOException {
- BufferedReader filenameReader = new BufferedReader(new InputStreamReader(sock.getInputStream(), "UTF-8"));
+ BufferedReader filenameReader =
+ new BufferedReader(new InputStreamReader(sock.getInputStream(), "UTF-8"));
String filename = filenameReader.readLine();
// GOOD: ensure that the file is in a designated folder in the user's home directory
if (!filename.contains("..") && filename.startsWith("/home/" + user + "/public/")) {
BufferedReader fileReader = new BufferedReader(new FileReader(filename));
String fileLine = fileReader.readLine();
- while(fileLine != null) {
+ while (fileLine != null) {
sock.getOutputStream().write(fileLine.getBytes());
fileLine = fileReader.readLine();
}
@@ -35,28 +37,28 @@ public class TaintedPath {
}
public void sendUserFileGood2(Socket sock, String user) throws Exception {
- BufferedReader filenameReader = new BufferedReader(
- new InputStreamReader(sock.getInputStream(), "UTF-8"));
+ BufferedReader filenameReader =
+ new BufferedReader(new InputStreamReader(sock.getInputStream(), "UTF-8"));
String filename = filenameReader.readLine();
-
+
Path publicFolder = Paths.get("/home/" + user + "/public").normalize().toAbsolutePath();
- Path filePath = publicFolder.resolve(filename).normalize().toAbsolutePath(); // FP until the path-injection sinks are reworked
-
+ Path filePath = publicFolder.resolve(filename).normalize().toAbsolutePath();
+
// GOOD: ensure that the path stays within the public folder
if (!filePath.startsWith(publicFolder + File.separator)) {
throw new IllegalArgumentException("Invalid filename");
}
BufferedReader fileReader = new BufferedReader(new FileReader(filePath.toString()));
String fileLine = fileReader.readLine();
- while(fileLine != null) {
+ while (fileLine != null) {
sock.getOutputStream().write(fileLine.getBytes());
fileLine = fileReader.readLine();
}
}
public void sendUserFileGood3(Socket sock, String user) throws Exception {
- BufferedReader filenameReader = new BufferedReader(
- new InputStreamReader(sock.getInputStream(), "UTF-8"));
+ BufferedReader filenameReader =
+ new BufferedReader(new InputStreamReader(sock.getInputStream(), "UTF-8"));
String filename = filenameReader.readLine();
// GOOD: ensure that the filename has no path separators or parent directory references
if (filename.contains("..") || filename.contains("/") || filename.contains("\\")) {
@@ -64,9 +66,9 @@ public class TaintedPath {
}
BufferedReader fileReader = new BufferedReader(new FileReader(filename));
String fileLine = fileReader.readLine();
- while(fileLine != null) {
+ while (fileLine != null) {
sock.getOutputStream().write(fileLine.getBytes());
fileLine = fileReader.readLine();
- }
+ }
}
}
diff --git a/java/ql/test/query-tests/security/CWE-022/semmle/tests/TaintedPath.ql b/java/ql/test/query-tests/security/CWE-022/semmle/tests/TaintedPath.ql
index e17123ce781..25e5bf1df87 100644
--- a/java/ql/test/query-tests/security/CWE-022/semmle/tests/TaintedPath.ql
+++ b/java/ql/test/query-tests/security/CWE-022/semmle/tests/TaintedPath.ql
@@ -1,11 +1,4 @@
import java
import TestUtilities.InlineFlowTest
import semmle.code.java.security.TaintedPathQuery
-
-class HasFlowTest extends InlineFlowTest {
- override predicate hasTaintFlow(DataFlow::Node src, DataFlow::Node sink) {
- TaintedPathFlow::flow(src, sink)
- }
-
- override predicate hasValueFlow(DataFlow::Node src, DataFlow::Node sink) { none() }
-}
+import TaintFlowTest
diff --git a/java/ql/test/query-tests/security/CWE-022/semmle/tests/Test.java b/java/ql/test/query-tests/security/CWE-022/semmle/tests/Test.java
index 872f2a01b65..a29cf1f620e 100644
--- a/java/ql/test/query-tests/security/CWE-022/semmle/tests/Test.java
+++ b/java/ql/test/query-tests/security/CWE-022/semmle/tests/Test.java
@@ -60,10 +60,8 @@ public class Test {
// "java.nio.file;Files;false;copy;;;Argument[0];read-file;manual"
Files.copy((Path) source(), (Path) null); // $ hasTaintFlow
Files.copy((Path) source(), (OutputStream) null); // $ hasTaintFlow
- Files.copy((InputStream) source(), null); // $ hasTaintFlow
// "java.nio.file;Files;false;copy;;;Argument[1];create-file;manual"
Files.copy((Path) null, (Path) source()); // $ hasTaintFlow
- Files.copy((Path) null, (OutputStream) source()); // $ hasTaintFlow
Files.copy((InputStream) null, (Path) source()); // $ hasTaintFlow
// "java.nio.file;Files;false;createDirectories;;;Argument[0];create-file;manual"
Files.createDirectories((Path) source()); // $ hasTaintFlow
From 19a6b7858b12c112056420f0af40cc3dca0a488f Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Fri, 26 Jan 2024 12:45:00 +0100
Subject: [PATCH 023/378] Remove reference to PathCreation
ZipSlip no longer needs to make this exclusion, since PathCreation arguments are no longer path-injection sinks
---
.../code/java/security/ZipSlipQuery.qll | 25 +------------------
1 file changed, 1 insertion(+), 24 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/security/ZipSlipQuery.qll b/java/ql/lib/semmle/code/java/security/ZipSlipQuery.qll
index 10db2997bef..7ba99a31e26 100644
--- a/java/ql/lib/semmle/code/java/security/ZipSlipQuery.qll
+++ b/java/ql/lib/semmle/code/java/security/ZipSlipQuery.qll
@@ -41,28 +41,5 @@ module ZipSlipFlow = TaintTracking::Global;
* A sink that represents a file creation, such as a file write, copy or move operation.
*/
private class FileCreationSink extends DataFlow::Node {
- FileCreationSink() {
- sinkNode(this, "path-injection") and
- not isPathCreation(this)
- }
-}
-
-/**
- * Holds if `sink` is a path creation node that doesn't imply a read/write filesystem operation.
- * This is to avoid creating new spurious alerts, since `PathCreation` sinks weren't
- * previously part of this query.
- */
-private predicate isPathCreation(DataFlow::Node sink) {
- exists(PathCreation pc |
- pc.getAnInput() = sink.asExpr()
- or
- pc.getAnInput().(Argument).isVararg() and sink.(DataFlow::ImplicitVarargsArray).getCall() = pc
- |
- // exclude actual read/write operations included in `PathCreation`
- not pc.(Call)
- .getCallee()
- .getDeclaringType()
- .hasQualifiedName("java.io",
- ["FileInputStream", "FileOutputStream", "FileReader", "FileWriter"])
- )
+ FileCreationSink() { sinkNode(this, "path-injection") }
}
From b8cb514dc45b29b471ec8b321507a10cc97c464d Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Fri, 26 Jan 2024 12:46:51 +0100
Subject: [PATCH 024/378] Rename the other change note
---
...ed-path-creation.md => 2024-01-26-deprecated-path-creation.md} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename java/ql/lib/change-notes/{2023-04-20-deprecated-path-creation.md => 2024-01-26-deprecated-path-creation.md} (100%)
diff --git a/java/ql/lib/change-notes/2023-04-20-deprecated-path-creation.md b/java/ql/lib/change-notes/2024-01-26-deprecated-path-creation.md
similarity index 100%
rename from java/ql/lib/change-notes/2023-04-20-deprecated-path-creation.md
rename to java/ql/lib/change-notes/2024-01-26-deprecated-path-creation.md
From 6e550d28af7e425d0727d7077316fb676f56a1e6 Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Fri, 26 Jan 2024 13:33:25 +0100
Subject: [PATCH 025/378] Update more test expectations
---
.../SupportedExternalSinks/SupportedExternalSinks.expected | 1 -
.../ql/test/utils/modeleditor/ApplicationModeEndpoints.expected | 2 --
2 files changed, 3 deletions(-)
diff --git a/java/ql/test/query-tests/Telemetry/SupportedExternalSinks/SupportedExternalSinks.expected b/java/ql/test/query-tests/Telemetry/SupportedExternalSinks/SupportedExternalSinks.expected
index 5f0ed7d05df..6cb849601d5 100644
--- a/java/ql/test/query-tests/Telemetry/SupportedExternalSinks/SupportedExternalSinks.expected
+++ b/java/ql/test/query-tests/Telemetry/SupportedExternalSinks/SupportedExternalSinks.expected
@@ -1,3 +1,2 @@
-| java.io.File#File(String) | 1 |
| java.io.FileWriter#FileWriter(File) | 1 |
| java.net.URL#openStream() | 1 |
diff --git a/java/ql/test/utils/modeleditor/ApplicationModeEndpoints.expected b/java/ql/test/utils/modeleditor/ApplicationModeEndpoints.expected
index 919fc09b261..4d32cb7e922 100644
--- a/java/ql/test/utils/modeleditor/ApplicationModeEndpoints.expected
+++ b/java/ql/test/utils/modeleditor/ApplicationModeEndpoints.expected
@@ -2,11 +2,9 @@
| com/github/codeql/test/PublicClass.java:8:5:8:27 | println(...) | java.io | PrintStream | println | (String) | true | rt.jar | | sink | source |
| com/github/codeql/test/PublicClass.java:12:5:12:27 | println(...) | java.io | PrintStream | println | (String) | true | rt.jar | | sink | source |
| com/github/codeql/test/PublicClass.java:16:5:16:45 | println(...) | java.io | PrintStream | println | (Object) | true | rt.jar | | sink | source |
-| com/github/codeql/test/PublicClass.java:16:24:16:44 | get(...) | java.nio.file | Paths | get | (String,String[]) | true | rt.jar | | sink | source |
| com/github/codeql/test/PublicClass.java:16:24:16:44 | get(...) | java.nio.file | Paths | get | (String,String[]) | true | rt.jar | | summary | source |
| com/github/codeql/test/PublicClass.java:20:5:20:68 | println(...) | java.io | PrintStream | println | (Object) | true | rt.jar | | sink | source |
| com/github/codeql/test/PublicClass.java:20:24:20:47 | getDefault(...) | java.nio.file | FileSystems | getDefault | () | false | rt.jar | | | source |
-| com/github/codeql/test/PublicClass.java:20:24:20:67 | getPath(...) | java.nio.file | FileSystem | getPath | (String,String[]) | true | rt.jar | | sink | source |
| com/github/codeql/test/PublicClass.java:20:24:20:67 | getPath(...) | java.nio.file | FileSystem | getPath | (String,String[]) | true | rt.jar | | summary | source |
| com/github/codeql/test/PublicClass.java:24:5:24:27 | println(...) | java.io | PrintStream | println | (String) | true | rt.jar | | sink | source |
| com/github/codeql/test/PublicGenericClass.java:7:5:7:27 | println(...) | java.io | PrintStream | println | (Object) | true | rt.jar | | sink | source |
From d8fe0f5bb83ac713dca767043aff9fc7c2223584 Mon Sep 17 00:00:00 2001
From: Marcono1234
Date: Sun, 28 Jan 2024 18:46:18 +0100
Subject: [PATCH 026/378] Java: Document which assignment type is covered by
which class
---
java/ql/lib/semmle/code/java/Expr.qll | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)
diff --git a/java/ql/lib/semmle/code/java/Expr.qll b/java/ql/lib/semmle/code/java/Expr.qll
index be3976b8458..74f37a4a451 100644
--- a/java/ql/lib/semmle/code/java/Expr.qll
+++ b/java/ql/lib/semmle/code/java/Expr.qll
@@ -378,7 +378,17 @@ class ArrayInit extends Expr, @arrayinit {
override string getAPrimaryQlClass() { result = "ArrayInit" }
}
-/** A common super-class that represents all varieties of assignments. */
+/**
+ * A common super-class that represents many varieties of assignments.
+ *
+ * This does not cover unary assignments such as `i++`, and initialization of
+ * local variables at their declaration such as `int i = 0;`.
+ *
+ * To cover more cases of variable updates, see the classes `VariableAssign`,
+ * `VariableUpdate` and `VarWrite`. But consider that they don't cover array
+ * element assignments since there the assignment destination is not directly
+ * the array variable but instead an `ArrayAccess`.
+ */
class Assignment extends Expr, @assignment {
/** Gets the destination (left-hand side) of the assignment. */
Expr getDest() { result.isNthChildOf(this, 0) }
@@ -1781,6 +1791,9 @@ class VariableUpdate extends Expr {
/**
* An assignment to a variable or an initialization of the variable.
+ *
+ * This does not cover compound assignments such as `i += 1`, or unary
+ * assignments such as `i++`; use the class `VariableUpdate` for that.
*/
class VariableAssign extends VariableUpdate {
VariableAssign() {
@@ -1979,6 +1992,9 @@ class ExtensionReceiverAccess extends VarAccess {
/**
* A write access to a variable, which occurs as the destination of an assignment.
+ *
+ * This does not cover the initialization of local variables at their declaration,
+ * use the class `VariableUpdate` if you want to cover that as well.
*/
class VarWrite extends VarAccess {
VarWrite() { this.isVarWrite() }
From 3f0dc2b0228782093efe2efebe1e3dda3e5d4897 Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Mon, 29 Jan 2024 12:10:46 +0100
Subject: [PATCH 027/378] Python: Model the `psycopg` package
---
python/ql/lib/semmle/python/Frameworks.qll | 1 +
.../lib/semmle/python/frameworks/Psycopg.qll | 32 +++++++++++++++++++
.../frameworks/psycopg/ConceptsTest.expected | 2 ++
.../frameworks/psycopg/ConceptsTest.ql | 2 ++
.../frameworks/psycopg/pep249.py | 14 ++++++++
5 files changed, 51 insertions(+)
create mode 100644 python/ql/lib/semmle/python/frameworks/Psycopg.qll
create mode 100644 python/ql/test/library-tests/frameworks/psycopg/ConceptsTest.expected
create mode 100644 python/ql/test/library-tests/frameworks/psycopg/ConceptsTest.ql
create mode 100644 python/ql/test/library-tests/frameworks/psycopg/pep249.py
diff --git a/python/ql/lib/semmle/python/Frameworks.qll b/python/ql/lib/semmle/python/Frameworks.qll
index 801a51008ec..a6288dadc11 100644
--- a/python/ql/lib/semmle/python/Frameworks.qll
+++ b/python/ql/lib/semmle/python/Frameworks.qll
@@ -48,6 +48,7 @@ private import semmle.python.frameworks.Oracledb
private import semmle.python.frameworks.Pandas
private import semmle.python.frameworks.Peewee
private import semmle.python.frameworks.Phoenixdb
+private import semmle.python.frameworks.Psycopg
private import semmle.python.frameworks.Psycopg2
private import semmle.python.frameworks.Pycurl
private import semmle.python.frameworks.Pydantic
diff --git a/python/ql/lib/semmle/python/frameworks/Psycopg.qll b/python/ql/lib/semmle/python/frameworks/Psycopg.qll
new file mode 100644
index 00000000000..10d4609aaaf
--- /dev/null
+++ b/python/ql/lib/semmle/python/frameworks/Psycopg.qll
@@ -0,0 +1,32 @@
+/**
+ * Provides classes modeling security-relevant aspects of the `psycopg` PyPI package.
+ * See
+ * - https://www.psycopg.org/psycopg3/docs/
+ * - https://pypi.org/project/psycopg/
+ */
+
+private import python
+private import semmle.python.dataflow.new.DataFlow
+private import semmle.python.dataflow.new.RemoteFlowSources
+private import semmle.python.Concepts
+private import semmle.python.ApiGraphs
+private import semmle.python.frameworks.PEP249
+
+/**
+ * Provides models for the `psycopg` PyPI package.
+ * See
+ * - https://www.psycopg.org/psycopg3/docs/
+ * - https://pypi.org/project/psycopg/
+ */
+private module Psycopg {
+ // ---------------------------------------------------------------------------
+ // Psycopg
+ // ---------------------------------------------------------------------------
+ /**
+ * A model of `psycopg` as a module that implements PEP 249, providing ways to execute SQL statements
+ * against a database.
+ */
+ class Psycopg extends PEP249::PEP249ModuleApiNode {
+ Psycopg() { this = API::moduleImport("psycopg") }
+ }
+}
diff --git a/python/ql/test/library-tests/frameworks/psycopg/ConceptsTest.expected b/python/ql/test/library-tests/frameworks/psycopg/ConceptsTest.expected
new file mode 100644
index 00000000000..8ec8033d086
--- /dev/null
+++ b/python/ql/test/library-tests/frameworks/psycopg/ConceptsTest.expected
@@ -0,0 +1,2 @@
+testFailures
+failures
diff --git a/python/ql/test/library-tests/frameworks/psycopg/ConceptsTest.ql b/python/ql/test/library-tests/frameworks/psycopg/ConceptsTest.ql
new file mode 100644
index 00000000000..b557a0bccb6
--- /dev/null
+++ b/python/ql/test/library-tests/frameworks/psycopg/ConceptsTest.ql
@@ -0,0 +1,2 @@
+import python
+import experimental.meta.ConceptsTest
diff --git a/python/ql/test/library-tests/frameworks/psycopg/pep249.py b/python/ql/test/library-tests/frameworks/psycopg/pep249.py
new file mode 100644
index 00000000000..0336facb079
--- /dev/null
+++ b/python/ql/test/library-tests/frameworks/psycopg/pep249.py
@@ -0,0 +1,14 @@
+import psycopg
+
+conn = psycopg.connect(...)
+conn.execute("some sql", (42,)) # $ getSql="some sql"
+cursor = conn.cursor()
+cursor.execute("some sql", (42,)) # $ getSql="some sql"
+cursor.executemany("some sql", [(42,)]) # $ getSql="some sql"
+
+# as in their examples:
+with psycopg.connect(...) as conn:
+ conn.execute("some sql", (42,)) # $ getSql="some sql"
+ with conn.cursor() as cursor:
+ cursor.execute("some sql", (42,)) # $ getSql="some sql"
+ cursor.executemany("some sql", [(42,)]) # $ getSql="some sql"
From 5867fb3d2925aa25ebe633d7921a1e716a8b217e Mon Sep 17 00:00:00 2001
From: Rasmus Wriedt Larsen
Date: Mon, 29 Jan 2024 12:12:07 +0100
Subject: [PATCH 028/378] Python: Add change-note
---
python/ql/lib/change-notes/2024-01-29-psycopg-modeling.md | 4 ++++
1 file changed, 4 insertions(+)
create mode 100644 python/ql/lib/change-notes/2024-01-29-psycopg-modeling.md
diff --git a/python/ql/lib/change-notes/2024-01-29-psycopg-modeling.md b/python/ql/lib/change-notes/2024-01-29-psycopg-modeling.md
new file mode 100644
index 00000000000..007cde7fb34
--- /dev/null
+++ b/python/ql/lib/change-notes/2024-01-29-psycopg-modeling.md
@@ -0,0 +1,4 @@
+---
+category: minorAnalysis
+---
+* Added modeling of the `psycopg` PyPI package as a SQL database library.
From e441dd472befacec1b47f9df5b42403783408e7f Mon Sep 17 00:00:00 2001
From: Asger F
Date: Tue, 16 Jan 2024 12:34:48 +0100
Subject: [PATCH 029/378] JS: Expose hasBothNamedAndDefaultExports()
---
.../lib/semmle/javascript/ES2015Modules.qll | 27 ++++++++++---------
1 file changed, 15 insertions(+), 12 deletions(-)
diff --git a/javascript/ql/lib/semmle/javascript/ES2015Modules.qll b/javascript/ql/lib/semmle/javascript/ES2015Modules.qll
index d32278371c6..1bdfec7ffe9 100644
--- a/javascript/ql/lib/semmle/javascript/ES2015Modules.qll
+++ b/javascript/ql/lib/semmle/javascript/ES2015Modules.qll
@@ -39,6 +39,20 @@ class ES2015Module extends Module {
// modules are implicitly strict
any()
}
+
+ /**
+ * Holds if this module contains both named and `default` exports.
+ *
+ * This is used to determine whether a default-import of the module should be reinterpreted
+ * as a namespace-import, to accommodate the non-standard behavior implemented by some compilers.
+ *
+ * When a module has both named and `default` exports, the non-standard interpretation can lead to
+ * ambiguities, so we only allow the standard interpretation in that case.
+ */
+ predicate hasBothNamedAndDefaultExports() {
+ hasNamedExports(this) and
+ hasDefaultExport(this)
+ }
}
/**
@@ -64,17 +78,6 @@ private predicate hasDefaultExport(ES2015Module mod) {
mod.getAnExport().(ExportNamedDeclaration).getASpecifier().getExportedName() = "default"
}
-/**
- * Holds if `mod` contains both named and `default` exports.
- *
- * This is used to determine whether a default-import of the module should be reinterpreted
- * as a namespace-import, to accommodate the non-standard behavior implemented by some compilers.
- */
-private predicate hasBothNamedAndDefaultExports(ES2015Module mod) {
- hasNamedExports(mod) and
- hasDefaultExport(mod)
-}
-
/**
* An import declaration.
*
@@ -131,7 +134,7 @@ class ImportDeclaration extends Stmt, Import, @import_declaration {
// For compatibility with the non-standard implementation of default imports,
// treat default imports as namespace imports in cases where it can't cause ambiguity
// between named exports and the properties of a default-exported object.
- not hasBothNamedAndDefaultExports(this.getImportedModule()) and
+ not this.getImportedModule().(ES2015Module).hasBothNamedAndDefaultExports() and
is.getImportedName() = "default"
)
or
From 0e0fb0e52dcc7d6dde1f4dae54df825c70052e5c Mon Sep 17 00:00:00 2001
From: Asger F
Date: Tue, 16 Jan 2024 14:29:09 +0100
Subject: [PATCH 030/378] JS: Remove API graph edge causing ambiguity
---
javascript/ql/lib/semmle/javascript/ApiGraphs.qll | 1 +
1 file changed, 1 insertion(+)
diff --git a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll
index dc9844bf8bd..2e4e96aa46a 100644
--- a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll
+++ b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll
@@ -1621,6 +1621,7 @@ private predicate exports(string m, DataFlow::Node rhs) {
exists(Module mod | mod = importableModule(m) |
rhs = mod.(AmdModule).getDefine().getModuleExpr().flow()
or
+ not mod.(ES2015Module).hasBothNamedAndDefaultExports() and
exports(m, "default", rhs)
or
exists(ExportAssignDeclaration assgn | assgn.getTopLevel() = mod |
From 2d8d11fa7840a62dfd3b4e4f6b3380ea22bf1afb Mon Sep 17 00:00:00 2001
From: Asger F
Date: Wed, 17 Jan 2024 15:35:33 +0100
Subject: [PATCH 031/378] JS: Restrict type-only exports in API graphs
---
javascript/ql/lib/semmle/javascript/ApiGraphs.qll | 1 +
1 file changed, 1 insertion(+)
diff --git a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll
index 2e4e96aa46a..c7d911eac63 100644
--- a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll
+++ b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll
@@ -1635,6 +1635,7 @@ private predicate exports(string m, DataFlow::Node rhs) {
/** Holds if module `m` exports `rhs` under the name `prop`. */
private predicate exports(string m, string prop, DataFlow::Node rhs) {
exists(ExportDeclaration exp | exp.getEnclosingModule() = importableModule(m) |
+ not exp.isTypeOnly() and
rhs = exp.getSourceNode(prop)
or
exists(Variable v |
From 8930ce74afa573cc3c3e3b056c57b9c3aaa754b3 Mon Sep 17 00:00:00 2001
From: Asger F
Date: Tue, 16 Jan 2024 12:13:14 +0100
Subject: [PATCH 032/378] JS: Do not view packages as nested in a private
package
---
javascript/ql/lib/semmle/javascript/NPM.qll | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/javascript/ql/lib/semmle/javascript/NPM.qll b/javascript/ql/lib/semmle/javascript/NPM.qll
index 0bf92c5d29a..137641e119a 100644
--- a/javascript/ql/lib/semmle/javascript/NPM.qll
+++ b/javascript/ql/lib/semmle/javascript/NPM.qll
@@ -29,7 +29,8 @@ class PackageJson extends JsonObject {
parentDir.getAChildContainer+() = currentDir and
pkgNameDiff = currentDir.getAbsolutePath().suffix(parentDir.getAbsolutePath().length()) and
not exists(pkgNameDiff.indexOf("/node_modules/")) and
- result = parentPkgName + pkgNameDiff
+ result = parentPkgName + pkgNameDiff and
+ not parentPkg.isPrivate()
)
}
From 6cfdd7aec463ac4d0a9ef566e53cbcf0a1aa4df5 Mon Sep 17 00:00:00 2001
From: Asger F
Date: Mon, 15 Jan 2024 13:53:16 +0100
Subject: [PATCH 033/378] JS: Add InlineExpectationsTest
---
javascript/ql/test/qlpack.yml | 1 +
.../ql/test/testUtilities/InlineExpectationsTest.qll | 8 ++++++++
.../internal/InlineExpectationsTestImpl.qll | 12 ++++++++++++
3 files changed, 21 insertions(+)
create mode 100644 javascript/ql/test/testUtilities/InlineExpectationsTest.qll
create mode 100644 javascript/ql/test/testUtilities/internal/InlineExpectationsTestImpl.qll
diff --git a/javascript/ql/test/qlpack.yml b/javascript/ql/test/qlpack.yml
index 8976782483a..df7e46f5df7 100644
--- a/javascript/ql/test/qlpack.yml
+++ b/javascript/ql/test/qlpack.yml
@@ -3,6 +3,7 @@ groups: [javascript, test]
dependencies:
codeql/javascript-all: ${workspace}
codeql/javascript-queries: ${workspace}
+ codeql/util: ${workspace}
extractor: javascript
tests: .
warnOnImplicitThis: true
diff --git a/javascript/ql/test/testUtilities/InlineExpectationsTest.qll b/javascript/ql/test/testUtilities/InlineExpectationsTest.qll
new file mode 100644
index 00000000000..b1953314078
--- /dev/null
+++ b/javascript/ql/test/testUtilities/InlineExpectationsTest.qll
@@ -0,0 +1,8 @@
+/**
+ * Inline expectation tests for JS.
+ * See `shared/util/codeql/util/test/InlineExpectationsTest.qll`
+ */
+
+private import codeql.util.test.InlineExpectationsTest
+private import internal.InlineExpectationsTestImpl
+import Make
diff --git a/javascript/ql/test/testUtilities/internal/InlineExpectationsTestImpl.qll b/javascript/ql/test/testUtilities/internal/InlineExpectationsTestImpl.qll
new file mode 100644
index 00000000000..d1de2866b10
--- /dev/null
+++ b/javascript/ql/test/testUtilities/internal/InlineExpectationsTestImpl.qll
@@ -0,0 +1,12 @@
+private import javascript as JS
+private import codeql.util.test.InlineExpectationsTest
+
+module Impl implements InlineExpectationsTestSig {
+ private import javascript
+
+ class ExpectationComment extends LineComment {
+ string getContents() { result = this.getText() }
+ }
+
+ class Location = JS::Location;
+}
From e2bf9ea2eba40753dd99bd298f97ddf1afce1cb1 Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Tue, 30 Jan 2024 10:43:56 +0100
Subject: [PATCH 034/378] Consider File.exists() et al a path-injection sink
---
java/ql/lib/ext/java.io.model.yml | 2 +-
java/ql/lib/ext/java.nio.file.model.yml | 4 ++--
.../query-tests/security/CWE-073/FilePathInjection.expected | 3 +++
java/ql/test/library-tests/neutrals/neutralsinks/Test.java | 3 ---
4 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/java/ql/lib/ext/java.io.model.yml b/java/ql/lib/ext/java.io.model.yml
index 17dbc1464dc..1ba027dbbb0 100644
--- a/java/ql/lib/ext/java.io.model.yml
+++ b/java/ql/lib/ext/java.io.model.yml
@@ -5,6 +5,7 @@ extensions:
data:
- ["java.io", "File", True, "createNewFile", "()", "", "Argument[this]", "path-injection", "ai-manual"]
- ["java.io", "File", True, "createTempFile", "(String,String,File)", "", "Argument[2]", "path-injection", "ai-manual"]
+ - ["java.io", "File", True, "exists", "()", "", "Argument[this]", "path-injection", "manual"]
- ["java.io", "File", True, "renameTo", "(File)", "", "Argument[0]", "path-injection", "ai-manual"]
- ["java.io", "FileInputStream", True, "FileInputStream", "(File)", "", "Argument[0]", "path-injection", "ai-manual"]
- ["java.io", "FileInputStream", True, "FileInputStream", "(FileDescriptor)", "", "Argument[0]", "path-injection", "manual"]
@@ -126,7 +127,6 @@ extensions:
- ["java.io", "DataOutput", "writeLong", "(long)", "summary", "manual"] # taint-numeric
# sink neutrals
- ["java.io", "File", "compareTo", "", "sink", "hq-manual"]
- - ["java.io", "File", "exists", "()", "sink", "hq-manual"]
- addsTo:
pack: codeql/java-all
extensible: sourceModel
diff --git a/java/ql/lib/ext/java.nio.file.model.yml b/java/ql/lib/ext/java.nio.file.model.yml
index a35c575e9cb..946f5653db6 100644
--- a/java/ql/lib/ext/java.nio.file.model.yml
+++ b/java/ql/lib/ext/java.nio.file.model.yml
@@ -18,6 +18,7 @@ extensions:
- ["java.nio.file", "Files", False, "delete", "(Path)", "", "Argument[0]", "path-injection", "ai-manual"]
- ["java.nio.file", "Files", False, "deleteIfExists", "(Path)", "", "Argument[0]", "path-injection", "ai-manual"]
- ["java.nio.file", "Files", False, "getFileStore", "(Path)", "", "Argument[0]", "path-injection", "ai-manual"] # the FileStore class is unlikely to be used for later sanitization
+ - ["java.nio.file", "Files", False, "exists", "(Path,LinkOption[])", "", "Argument[0]", "path-injection", "manual"]
- ["java.nio.file", "Files", False, "lines", "(Path,Charset)", "", "Argument[0]", "path-injection", "ai-manual"]
- ["java.nio.file", "Files", False, "lines", "(Path)", "", "Argument[0]", "path-injection", "ai-manual"]
- ["java.nio.file", "Files", False, "move", "", "", "Argument[1]", "path-injection", "manual"]
@@ -27,6 +28,7 @@ extensions:
- ["java.nio.file", "Files", False, "newBufferedWriter", "", "", "Argument[0]", "path-injection", "manual"]
- ["java.nio.file", "Files", False, "newInputStream", "(Path,OpenOption[])", "", "Argument[0]", "path-injection", "ai-manual"]
- ["java.nio.file", "Files", False, "newOutputStream", "", "", "Argument[0]", "path-injection", "manual"]
+ - ["java.nio.file", "Files", False, "notExists", "(Path,LinkOption[])", "", "Argument[0]", "path-injection", "manual"]
- ["java.nio.file", "Files", False, "probeContentType", "(Path)", "", "Argument[0]", "path-injection", "ai-manual"] # accesses the file based on user input, but only reads its content type from it
- ["java.nio.file", "Files", False, "readAllBytes", "(Path)", "", "Argument[0]", "path-injection", "ai-manual"]
- ["java.nio.file", "Files", False, "readAllLines", "(Path,Charset)", "", "Argument[0]", "path-injection", "ai-manual"]
@@ -89,7 +91,6 @@ extensions:
# summary neutrals
- ["java.nio.file", "Files", "exists", "(Path,LinkOption[])", "summary", "manual"]
# sink neutrals
- - ["java.nio.file", "Files", "exists", "", "sink", "hq-manual"]
- ["java.nio.file", "Files", "getLastModifiedTime", "", "sink", "hq-manual"]
- ["java.nio.file", "Files", "getOwner", "", "sink", "hq-manual"]
- ["java.nio.file", "Files", "getPosixFilePermissions", "", "sink", "hq-manual"]
@@ -101,6 +102,5 @@ extensions:
- ["java.nio.file", "Files", "isSameFile", "", "sink", "hq-manual"]
- ["java.nio.file", "Files", "isSymbolicLink", "", "sink", "hq-manual"]
- ["java.nio.file", "Files", "isWritable", "", "sink", "hq-manual"]
- - ["java.nio.file", "Files", "notExists", "", "sink", "hq-manual"]
- ["java.nio.file", "Files", "setLastModifiedTime", "", "sink", "hq-manual"]
- ["java.nio.file", "Files", "size", "", "sink", "hq-manual"]
diff --git a/java/ql/test/experimental/query-tests/security/CWE-073/FilePathInjection.expected b/java/ql/test/experimental/query-tests/security/CWE-073/FilePathInjection.expected
index 07be573dbf8..c7a79c6328d 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-073/FilePathInjection.expected
+++ b/java/ql/test/experimental/query-tests/security/CWE-073/FilePathInjection.expected
@@ -4,6 +4,7 @@ edges
| FilePathInjection.java:87:21:87:34 | getPara(...) : String | FilePathInjection.java:95:47:95:59 | finalFilePath |
| FilePathInjection.java:177:50:177:58 | file : File | FilePathInjection.java:182:30:182:33 | file |
| FilePathInjection.java:205:17:205:44 | getParameter(...) : String | FilePathInjection.java:209:24:209:31 | filePath : String |
+| FilePathInjection.java:209:15:209:32 | new File(...) : File | FilePathInjection.java:210:23:210:26 | file |
| FilePathInjection.java:209:15:209:32 | new File(...) : File | FilePathInjection.java:217:19:217:22 | file : File |
| FilePathInjection.java:209:24:209:31 | filePath : String | FilePathInjection.java:209:15:209:32 | new File(...) : File |
| FilePathInjection.java:217:19:217:22 | file : File | FilePathInjection.java:177:50:177:58 | file : File |
@@ -19,6 +20,7 @@ nodes
| FilePathInjection.java:205:17:205:44 | getParameter(...) : String | semmle.label | getParameter(...) : String |
| FilePathInjection.java:209:15:209:32 | new File(...) : File | semmle.label | new File(...) : File |
| FilePathInjection.java:209:24:209:31 | filePath : String | semmle.label | filePath : String |
+| FilePathInjection.java:210:23:210:26 | file | semmle.label | file |
| FilePathInjection.java:217:19:217:22 | file : File | semmle.label | file : File |
subpaths
#select
@@ -26,3 +28,4 @@ subpaths
| FilePathInjection.java:72:47:72:59 | finalFilePath | FilePathInjection.java:64:21:64:34 | getPara(...) : String | FilePathInjection.java:72:47:72:59 | finalFilePath | External control of file name or path due to $@. | FilePathInjection.java:64:21:64:34 | getPara(...) | user-provided value |
| FilePathInjection.java:95:47:95:59 | finalFilePath | FilePathInjection.java:87:21:87:34 | getPara(...) : String | FilePathInjection.java:95:47:95:59 | finalFilePath | External control of file name or path due to $@. | FilePathInjection.java:87:21:87:34 | getPara(...) | user-provided value |
| FilePathInjection.java:182:30:182:33 | file | FilePathInjection.java:205:17:205:44 | getParameter(...) : String | FilePathInjection.java:182:30:182:33 | file | External control of file name or path due to $@. | FilePathInjection.java:205:17:205:44 | getParameter(...) | user-provided value |
+| FilePathInjection.java:210:23:210:26 | file | FilePathInjection.java:205:17:205:44 | getParameter(...) : String | FilePathInjection.java:210:23:210:26 | file | External control of file name or path due to $@. | FilePathInjection.java:205:17:205:44 | getParameter(...) | user-provided value |
diff --git a/java/ql/test/library-tests/neutrals/neutralsinks/Test.java b/java/ql/test/library-tests/neutrals/neutralsinks/Test.java
index a234132226f..a563cb78de7 100644
--- a/java/ql/test/library-tests/neutrals/neutralsinks/Test.java
+++ b/java/ql/test/library-tests/neutrals/neutralsinks/Test.java
@@ -14,11 +14,9 @@ public class Test {
// java.io
File file = null;
- file.exists(); // $ isNeutralSink
file.compareTo(null); // $ isNeutralSink
// java.nio.file
- Files.exists(null, (LinkOption[])null); // $ isNeutralSink
Files.getLastModifiedTime(null, (LinkOption[])null); // $ isNeutralSink
Files.getOwner(null, (LinkOption[])null); // $ isNeutralSink
Files.getPosixFilePermissions(null, (LinkOption[])null); // $ isNeutralSink
@@ -30,7 +28,6 @@ public class Test {
Files.isSameFile(null, null); // $ isNeutralSink
Files.isSymbolicLink(null); // $ isNeutralSink
Files.isWritable(null); // $ isNeutralSink
- Files.notExists(null, (LinkOption[])null); // $ isNeutralSink
Files.setLastModifiedTime(null, null); // $ isNeutralSink
Files.size(null); // $ isNeutralSink
From 1737ba1a6bfe1e7dac921c47ca916a062beb351a Mon Sep 17 00:00:00 2001
From: Asger F
Date: Mon, 15 Jan 2024 13:54:41 +0100
Subject: [PATCH 035/378] JS: Add library for naming endpoints
---
.../ql/lib/semmle/javascript/ApiGraphs.qll | 3 +
.../javascript/endpoints/EndpointNaming.qll | 459 ++++++++++++++++++
.../EndpointNaming/EndpointNaming.expected | 7 +
.../EndpointNaming/EndpointNaming.ql | 41 ++
.../EndpointNaming/pack1/main.js | 13 +
.../EndpointNaming/pack1/package.json | 4 +
.../EndpointNaming/pack10/foo.js | 1 +
.../EndpointNaming/pack10/index.js | 3 +
.../EndpointNaming/pack10/package.json | 4 +
.../library-tests/EndpointNaming/pack2/lib.js | 6 +
.../EndpointNaming/pack2/main.js | 9 +
.../EndpointNaming/pack2/package.json | 4 +
.../library-tests/EndpointNaming/pack3/lib.js | 1 +
.../EndpointNaming/pack3/main.js | 7 +
.../EndpointNaming/pack3/package.json | 4 +
.../EndpointNaming/pack4/index.js | 1 +
.../EndpointNaming/pack4/package.json | 4 +
.../EndpointNaming/pack5/package.json | 4 +
.../EndpointNaming/pack5/src/index.js | 1 +
.../EndpointNaming/pack6/index.js | 6 +
.../EndpointNaming/pack6/package.json | 4 +
.../EndpointNaming/pack7/index.js | 6 +
.../EndpointNaming/pack7/package.json | 4 +
.../library-tests/EndpointNaming/pack8/foo.js | 5 +
.../EndpointNaming/pack8/index.js | 5 +
.../EndpointNaming/pack8/package.json | 4 +
.../library-tests/EndpointNaming/pack9/foo.js | 1 +
.../EndpointNaming/pack9/index.ts | 9 +
.../EndpointNaming/pack9/package.json | 4 +
29 files changed, 624 insertions(+)
create mode 100644 javascript/ql/lib/semmle/javascript/endpoints/EndpointNaming.qll
create mode 100644 javascript/ql/test/library-tests/EndpointNaming/EndpointNaming.expected
create mode 100644 javascript/ql/test/library-tests/EndpointNaming/EndpointNaming.ql
create mode 100644 javascript/ql/test/library-tests/EndpointNaming/pack1/main.js
create mode 100644 javascript/ql/test/library-tests/EndpointNaming/pack1/package.json
create mode 100644 javascript/ql/test/library-tests/EndpointNaming/pack10/foo.js
create mode 100644 javascript/ql/test/library-tests/EndpointNaming/pack10/index.js
create mode 100644 javascript/ql/test/library-tests/EndpointNaming/pack10/package.json
create mode 100644 javascript/ql/test/library-tests/EndpointNaming/pack2/lib.js
create mode 100644 javascript/ql/test/library-tests/EndpointNaming/pack2/main.js
create mode 100644 javascript/ql/test/library-tests/EndpointNaming/pack2/package.json
create mode 100644 javascript/ql/test/library-tests/EndpointNaming/pack3/lib.js
create mode 100644 javascript/ql/test/library-tests/EndpointNaming/pack3/main.js
create mode 100644 javascript/ql/test/library-tests/EndpointNaming/pack3/package.json
create mode 100644 javascript/ql/test/library-tests/EndpointNaming/pack4/index.js
create mode 100644 javascript/ql/test/library-tests/EndpointNaming/pack4/package.json
create mode 100644 javascript/ql/test/library-tests/EndpointNaming/pack5/package.json
create mode 100644 javascript/ql/test/library-tests/EndpointNaming/pack5/src/index.js
create mode 100644 javascript/ql/test/library-tests/EndpointNaming/pack6/index.js
create mode 100644 javascript/ql/test/library-tests/EndpointNaming/pack6/package.json
create mode 100644 javascript/ql/test/library-tests/EndpointNaming/pack7/index.js
create mode 100644 javascript/ql/test/library-tests/EndpointNaming/pack7/package.json
create mode 100644 javascript/ql/test/library-tests/EndpointNaming/pack8/foo.js
create mode 100644 javascript/ql/test/library-tests/EndpointNaming/pack8/index.js
create mode 100644 javascript/ql/test/library-tests/EndpointNaming/pack8/package.json
create mode 100644 javascript/ql/test/library-tests/EndpointNaming/pack9/foo.js
create mode 100644 javascript/ql/test/library-tests/EndpointNaming/pack9/index.ts
create mode 100644 javascript/ql/test/library-tests/EndpointNaming/pack9/package.json
diff --git a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll
index c7d911eac63..0bd79dd2029 100644
--- a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll
+++ b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll
@@ -594,6 +594,9 @@ module API {
exportedName = "" and
result = getAModuleImportRaw(moduleName)
}
+
+ /** Gets a sink node that represents instances of `cls`. */
+ Node getClassInstance(DataFlow::ClassNode cls) { result = Impl::MkClassInstance(cls) }
}
/**
diff --git a/javascript/ql/lib/semmle/javascript/endpoints/EndpointNaming.qll b/javascript/ql/lib/semmle/javascript/endpoints/EndpointNaming.qll
new file mode 100644
index 00000000000..5d4a067874c
--- /dev/null
+++ b/javascript/ql/lib/semmle/javascript/endpoints/EndpointNaming.qll
@@ -0,0 +1,459 @@
+/**
+ * Provides predicates for generating names for classes and functions that are part
+ * of the public API of a library.
+ *
+ * When possible, we try to use the qualified name by which a class/function can be accessed
+ * from client code.
+ *
+ * However, there are cases where classes and functions can be exposed to client
+ * code without being accessible as a qualified name. For example;
+ * ```js
+ * // 'Foo' is internal, but clients can reach its methods via `getFoo().m()`
+ * class Foo {
+ * m() {}
+ * }
+ * export function getFoo() {
+ * return new Foo();
+ * }
+ *
+ * // Clients can reach m() via getObj().m()
+ * export function getObj() {
+ * return {
+ * m() {}
+ * }
+ * }
+ * ```
+ *
+ * In these cases, we try to make up human-readable names for the endpoints.
+ * We make an effort to make these unambiguous in practice, though this is not always guaranteed.
+ */
+
+private import javascript
+
+/** Concatenates two access paths. */
+bindingset[x, y]
+private string join(string x, string y) {
+ if x = "" or y = "" then result = x + y else result = x + "." + y
+}
+
+private predicate isPackageExport(API::Node node) { node = API::moduleExport(_) }
+
+private predicate memberEdge(API::Node pred, API::Node succ) { succ = pred.getAMember() }
+
+/** Gets the shortest distance from a packaeg export to `nd` in the API graph. */
+private int distanceFromPackageExport(API::Node nd) =
+ shortestDistances(isPackageExport/1, memberEdge/2)(_, nd, result)
+
+private predicate isExported(API::Node node) {
+ isPackageExport(node)
+ or
+ exists(API::Node pred |
+ isExported(pred) and
+ memberEdge(pred, node)
+ )
+}
+
+/**
+ * Holds if `node` is a default export that can be reinterpreted as a namespace export,
+ * because the enclosing module has no named exports.
+ */
+private predicate defaultExportCanBeInterpretedAsNamespaceExport(API::Node node) {
+ exists(ES2015Module mod |
+ node.asSink() = mod.getAnExportedValue("default") and
+ not mod.hasBothNamedAndDefaultExports()
+ )
+}
+
+private predicate isPrivateAssignment(DataFlow::Node node) {
+ exists(MemberDeclaration decl |
+ node = decl.getInit().flow() and
+ decl.isPrivate()
+ )
+ or
+ exists(DataFlow::PropWrite write |
+ write.isPrivateField() and
+ node = write.getRhs()
+ )
+}
+
+private predicate isPrivateLike(API::Node node) { isPrivateAssignment(node.asSink()) }
+
+private API::Node getASuccessor(API::Node node, string name, int badness) {
+ isExported(node) and
+ exists(string member |
+ result = node.getMember(member) and
+ not isPrivateLike(node) and
+ if member = "default"
+ then
+ if defaultExportCanBeInterpretedAsNamespaceExport(node)
+ then (
+ badness = 5 and name = ""
+ ) else (
+ badness = 10 and name = "default"
+ )
+ else (
+ name = member and badness = 0
+ )
+ )
+}
+
+private API::Node getAPredecessor(API::Node node, string name, int badness) {
+ node = getASuccessor(result, name, badness)
+}
+
+/**
+ * Gets the predecessor of `node` to use when constructing a qualified name for it,
+ * and binds `name` and `badness` corresponding to the label on that edge.
+ */
+private API::Node getPreferredPredecessor(API::Node node, string name, int badness) {
+ // For root nodes, we prefer not having a predecessor, as we use the package name.
+ not isPackageExport(node) and
+ // Rank predecessors by name-badness, export-distance, and name.
+ // Since min() can only return a single value, we need a separate min() call per column.
+ badness = min(int b | exists(getAPredecessor(node, _, b)) | b) and
+ result =
+ min(API::Node pred, string name1 |
+ pred = getAPredecessor(node, name1, badness)
+ |
+ pred order by distanceFromPackageExport(pred), name1
+ ) and
+ name = min(string n | result = getAPredecessor(node, n, badness) | n)
+}
+
+/**
+ * Holds if values escpin
+ */
+private predicate sinkHasNameCandidate(API::Node sink, string package, string name, int badness) {
+ sink = API::moduleExport(package) and
+ name = "" and
+ badness = 0
+ or
+ exists(API::Node baseNode, string baseName, int baseBadness, string step, int stepBadness |
+ sinkHasNameCandidate(baseNode, package, baseName, baseBadness) and
+ baseNode = getPreferredPredecessor(sink, step, stepBadness) and
+ badness = (baseBadness + stepBadness).minimum(20) and
+ name = join(baseName, step)
+ )
+}
+
+/**
+ * Holds if `(package, name)` is the primary name to associate with `node`.
+ *
+ * `badness` is bound to the associated badness of the name.
+ */
+private predicate sinkHasPrimaryName(API::Node sink, string package, string name, int badness) {
+ badness = min(int b | sinkHasNameCandidate(sink, _, _, b) | b) and
+ package = min(string p | sinkHasNameCandidate(sink, p, _, badness) | p) and
+ name = min(string n | sinkHasNameCandidate(sink, package, n, badness) | n order by n.length(), n)
+}
+
+/**
+ * Holds if `(package, name)` is the primary name to associate with `node`.
+ */
+private predicate sinkHasPrimaryName(API::Node sink, string package, string name) {
+ sinkHasPrimaryName(sink, package, name, _)
+}
+
+/**
+ * Holds if `(package, name)` is an alias for `node`.
+ *
+ * This means it is a valid name for it, but was not chosen as the primary name.
+ */
+predicate sinkHasAlias(API::Node sink, string package, string name) {
+ not sinkHasPrimaryName(sink, package, name) and
+ (
+ exists(string baseName, string step |
+ sinkHasPrimaryName(getAPredecessor(sink, step, _), package, baseName) and
+ name = join(baseName, step)
+ )
+ or
+ sink = API::moduleExport(package) and
+ name = ""
+ )
+}
+
+/** Gets a sink node reachable from `node`. */
+bindingset[node]
+private API::Node getASinkNode(DataFlow::SourceNode node) { result.getAValueReachingSink() = node }
+
+bindingset[qualifiedName]
+private int getBadnessOfClassName(string qualifiedName) {
+ if qualifiedName.matches("%.constructor")
+ then result = 10
+ else
+ if qualifiedName = ""
+ then result = 5
+ else result = 0
+}
+
+/** Holds if `(package, name)` is a potential name for `cls`, with the given `badness`. */
+private predicate classObjectHasNameCandidate(
+ DataFlow::ClassNode cls, string package, string name, int badness
+) {
+ // There can be multiple API nodes associated with `cls`.
+ // For example:
+ ///
+ // class C {}
+ // module.exports.A = C; // first sink
+ // module.exports.B = C; // second sink
+ //
+ exists(int baseBadness |
+ sinkHasPrimaryName(getASinkNode(cls), package, name, baseBadness) and
+ badness = baseBadness + getBadnessOfClassName(name)
+ )
+}
+
+private predicate classObjectHasPrimaryName(
+ DataFlow::ClassNode cls, string package, string name, int badness
+) {
+ badness = min(int b | classObjectHasNameCandidate(cls, _, _, b) | b) and
+ package = min(string p | classObjectHasNameCandidate(cls, p, _, badness) | p) and
+ name = min(string n | classObjectHasNameCandidate(cls, package, n, badness) | n)
+}
+
+/** Holds if `(package, name)` is the primary name for the class object of `cls`. */
+predicate classObjectHasPrimaryName(DataFlow::ClassNode cls, string package, string name) {
+ classObjectHasPrimaryName(cls, package, name, _)
+}
+
+/** Holds if `(package, name)` is an alias for the class object of `cls`. */
+predicate classObjectHasAlias(DataFlow::ClassNode cls, string package, string name) {
+ not classObjectHasPrimaryName(cls, package, name) and
+ exists(int badness |
+ classObjectHasNameCandidate(cls, package, name, badness) and
+ badness < 100
+ )
+}
+
+/** Holds if an instance of `cls` can be exposed to client code. */
+private predicate hasEscapingInstance(DataFlow::ClassNode cls) {
+ cls.getAnInstanceReference().flowsTo(any(API::Node n).asSink())
+}
+
+/**
+ * Holds if `(package, name)` is a potential name to use for instances of `cls`, with the given `badness`.
+ */
+private predicate classInstanceHasNameCandidate(
+ DataFlow::ClassNode cls, string package, string name, int badness
+) {
+ exists(string baseName |
+ classObjectHasPrimaryName(cls, package, baseName, badness) and
+ name = join(baseName, "prototype")
+ )
+ or
+ // In case the class itself is unaccessible, but an instance is exposed via an access path,
+ // consider using that access path. For example:
+ //
+ // class InternalClass {}
+ // module.exports.foo = new InternalClass();
+ //
+ exists(int baseBadness |
+ sinkHasPrimaryName(getASinkNode(cls.getAnInstanceReference()), package, name, baseBadness) and
+ badness = baseBadness + 30 // add penalty, as we prefer to base this on the class name
+ )
+ or
+ // If neither the class nor its instances are accessible via an access path, but instances of the
+ // class can still escape via more complex access patterns, resort to a synthesized name.
+ // For example:
+ //
+ // class InternalClass {}
+ // function foo() {
+ // return new InternalClass();
+ // }
+ //
+ hasEscapingInstance(cls) and
+ exists(string baseName |
+ InternalModuleNaming::fallbackModuleName(cls.getTopLevel(), package, baseName, badness - 100) and
+ name = join(baseName, cls.getName()) + ".prototype"
+ )
+}
+
+private predicate classInstanceHasPrimaryName(
+ DataFlow::ClassNode cls, string package, string name, int badness
+) {
+ badness = min(int b | classInstanceHasNameCandidate(cls, _, _, b) | b) and
+ package = min(string p | classInstanceHasNameCandidate(cls, p, _, badness) | p) and
+ name =
+ min(string n |
+ classInstanceHasNameCandidate(cls, package, n, badness)
+ |
+ n order by n.length(), n
+ )
+}
+
+/** Holds if `(package, name)` is the primary name to use for instances of `cls`. */
+predicate classInstanceHasPrimaryName(DataFlow::ClassNode cls, string package, string name) {
+ classInstanceHasPrimaryName(cls, package, name, _)
+}
+
+/** Holds if `(package, name)` is an alias referring to some instance of `cls`. */
+predicate classInstanceHasAlias(DataFlow::ClassNode cls, string package, string name) {
+ not classInstanceHasPrimaryName(cls, package, name) and
+ exists(int badness |
+ classInstanceHasNameCandidate(cls, package, name, badness) and
+ badness < 100 // Badness 100 is when we start to synthesize names. Do not suggest these as aliases.
+ )
+}
+
+private predicate functionHasNameCandidate(
+ DataFlow::FunctionNode function, string package, string name, int badness
+) {
+ sinkHasPrimaryName(getASinkNode(function), package, name, badness)
+ or
+ exists(DataFlow::ClassNode cls |
+ function = cls.getConstructor() and
+ classObjectHasPrimaryName(cls, package, name, badness)
+ or
+ exists(string baseName, string memberName |
+ function = cls.getInstanceMethod(memberName) and
+ classInstanceHasPrimaryName(cls, package, baseName, badness) and
+ name = join(baseName, memberName)
+ or
+ function = cls.getStaticMethod(memberName) and
+ classObjectHasPrimaryName(cls, package, baseName, badness) and
+ name = join(baseName, memberName)
+ )
+ )
+}
+
+private predicate functionHasPrimaryName(
+ DataFlow::FunctionNode function, string package, string name, int badness
+) {
+ badness = min(int b | functionHasNameCandidate(function, _, _, b) | b) and
+ package = min(string p | functionHasNameCandidate(function, p, _, badness) | p) and
+ name =
+ min(string n |
+ functionHasNameCandidate(function, package, n, badness)
+ |
+ n order by n.length(), n
+ )
+}
+
+/**
+ * Holds if `(package, name)` is the primary name for the given `function`.
+ */
+predicate functionHasPrimaryName(DataFlow::FunctionNode function, string package, string name) {
+ functionHasPrimaryName(function, package, name, _)
+}
+
+/**
+ * Holds if `(package, name)` is an alias for the given `function`.
+ */
+predicate functionHasAlias(DataFlow::FunctionNode function, string package, string name) {
+ not functionHasPrimaryName(function, package, name) and
+ exists(int badness |
+ functionHasNameCandidate(function, package, name, badness) and
+ badness < 100
+ )
+}
+
+/**
+ * Converts a `(package, name)` pair to a string of form `(package).name`.
+ */
+bindingset[package, name]
+string renderName(string package, string name) { result = join("(" + package + ")", name) }
+
+/**
+ * Contains predicates for naming individual modules (i.e. files) inside of a package.
+ *
+ * These names are not necessarily part of a package's public API, and so we only used them
+ * as a fallback when a publicly-accessible access path cannot be found.
+ */
+private module InternalModuleNaming {
+ /** Gets the path to `folder` relative to its enclosing non-private `package.json` file. */
+ private string getPackageRelativePathFromFolder(Folder folder) {
+ exists(PackageJson json |
+ json.getFile() = folder.getFile("package.json") and
+ not json.isPrivate() and
+ result = json.getPackageName()
+ )
+ or
+ not exists(folder.getFile("package.json")) and
+ result =
+ getPackageRelativePathFromFolder(folder.getParentContainer()) + "/" + folder.getBaseName()
+ }
+
+ private string getPackageRelativePath(Module mod) {
+ exists(PackageJson json, string relativePath |
+ not json.isPrivate() and
+ json.getExportedModule(relativePath) = mod and
+ if relativePath = "."
+ then result = json.getPackageName()
+ else result = json.getPackageName() + "/" + relativePath.regexpReplaceAll("^\\./", "")
+ )
+ or
+ not mod = any(PackageJson json | not json.isPrivate()).getExportedModule(_) and
+ not mod.isAmbient() and
+ exists(string folderPath |
+ folderPath = getPackageRelativePathFromFolder(mod.getFile().getParentContainer()) and
+ if mod.getName() = "index"
+ then result = folderPath
+ else result = folderPath + "/" + mod.getName()
+ )
+ }
+
+ /** Holds if `(package, name)` should be used to refer to code inside `mod`. */
+ predicate fallbackModuleName(Module mod, string package, string name, int badness) {
+ sinkHasPrimaryName(getASinkNode(mod.getDefaultOrBulkExport()), package, name, badness)
+ or
+ badness = 50 and
+ package = getPackageRelativePath(mod) and
+ name = ""
+ }
+}
+
+/**
+ * Contains query predicates for emitting debugging information about endpoint naming.
+ */
+module Debug {
+ /** Holds if `node` has multiple preferred predecessors. */
+ query predicate ambiguousPreferredPredecessor(API::Node node) {
+ strictcount(API::Node pred, string name, int badness |
+ pred = getPreferredPredecessor(node, name, badness)
+ ) > 1
+ }
+
+ /** Holds if the given `node` has multiple primary names. */
+ query string ambiguousSinkName(API::Node node) {
+ strictcount(string package, string name | sinkHasPrimaryName(node, package, name)) > 1 and
+ result =
+ concat(string package, string name |
+ sinkHasPrimaryName(node, package, name)
+ |
+ renderName(package, name), ", "
+ )
+ }
+
+ /** Holds if the given `node` has multiple primary names. */
+ query string ambiguousClassObjectName(DataFlow::ClassNode node) {
+ strictcount(string package, string name | classObjectHasPrimaryName(node, package, name)) > 1 and
+ result =
+ concat(string package, string name |
+ classObjectHasPrimaryName(node, package, name)
+ |
+ renderName(package, name), ", "
+ )
+ }
+
+ /** Holds if the given `node` has multiple primary names. */
+ query string ambiguousClassInstanceName(DataFlow::ClassNode node) {
+ strictcount(string package, string name | classInstanceHasPrimaryName(node, package, name)) > 1 and
+ result =
+ concat(string package, string name |
+ classInstanceHasPrimaryName(node, package, name)
+ |
+ renderName(package, name), ", "
+ )
+ }
+
+ /** Holds if the given `node` has multiple primary names. */
+ query string ambiguousFunctionName(DataFlow::FunctionNode node) {
+ strictcount(string package, string name | functionHasPrimaryName(node, package, name)) > 1 and
+ result =
+ concat(string package, string name |
+ functionHasPrimaryName(node, package, name)
+ |
+ renderName(package, name), ", "
+ )
+ }
+}
diff --git a/javascript/ql/test/library-tests/EndpointNaming/EndpointNaming.expected b/javascript/ql/test/library-tests/EndpointNaming/EndpointNaming.expected
new file mode 100644
index 00000000000..e0cc251f903
--- /dev/null
+++ b/javascript/ql/test/library-tests/EndpointNaming/EndpointNaming.expected
@@ -0,0 +1,7 @@
+testFailures
+ambiguousPreferredPredecessor
+ambiguousSinkName
+ambiguousClassObjectName
+ambiguousClassInstanceName
+ambiguousFunctionName
+failures
diff --git a/javascript/ql/test/library-tests/EndpointNaming/EndpointNaming.ql b/javascript/ql/test/library-tests/EndpointNaming/EndpointNaming.ql
new file mode 100644
index 00000000000..102ebb721a8
--- /dev/null
+++ b/javascript/ql/test/library-tests/EndpointNaming/EndpointNaming.ql
@@ -0,0 +1,41 @@
+import javascript
+import semmle.javascript.RestrictedLocations
+import semmle.javascript.Lines
+import semmle.javascript.endpoints.EndpointNaming as EndpointNaming
+import testUtilities.InlineExpectationsTest
+import EndpointNaming::Debug
+
+module TestConfig implements TestSig {
+ string getARelevantTag() {
+ result = "instance"
+ or
+ result = "class"
+ or
+ result = "method"
+ }
+
+ predicate hasActualResult(Location location, string element, string tag, string value) {
+ exists(string package, string name |
+ element = "" and
+ value = EndpointNaming::renderName(package, name)
+ |
+ exists(DataFlow::ClassNode cls | location = cls.getAstNode().getLocation() |
+ tag = "class" and
+ EndpointNaming::classObjectHasPrimaryName(cls, package, name)
+ or
+ tag = "instance" and
+ EndpointNaming::classInstanceHasPrimaryName(cls, package, name)
+ )
+ or
+ element = "" and
+ exists(DataFlow::FunctionNode function |
+ not function.getFunction() = any(ConstructorDeclaration decl | decl.isSynthetic()).getBody() and
+ location = function.getFunction().getLocation() and
+ tag = "method" and
+ EndpointNaming::functionHasPrimaryName(function, package, name)
+ )
+ )
+ }
+}
+
+import MakeTest
diff --git a/javascript/ql/test/library-tests/EndpointNaming/pack1/main.js b/javascript/ql/test/library-tests/EndpointNaming/pack1/main.js
new file mode 100644
index 00000000000..ead8000ff14
--- /dev/null
+++ b/javascript/ql/test/library-tests/EndpointNaming/pack1/main.js
@@ -0,0 +1,13 @@
+export class PublicClass {} // $ class=(pack1).PublicClass instance=(pack1).PublicClass.prototype
+
+class PrivateClass {}
+
+export const ExportedConst = class ExportedConstClass {} // $ class=(pack1).ExportedConst instance=(pack1).ExportedConst.prototype
+
+class ClassWithEscapingInstance {} // $ instance=(pack1).ClassWithEscapingInstance.prototype
+
+export function getEscapingInstance() {
+ return new ClassWithEscapingInstance();
+} // $ method=(pack1).getEscapingInstance
+
+export function publicFunction() {} // $ method=(pack1).publicFunction
diff --git a/javascript/ql/test/library-tests/EndpointNaming/pack1/package.json b/javascript/ql/test/library-tests/EndpointNaming/pack1/package.json
new file mode 100644
index 00000000000..da2dbb94dd1
--- /dev/null
+++ b/javascript/ql/test/library-tests/EndpointNaming/pack1/package.json
@@ -0,0 +1,4 @@
+{
+ "name": "pack1",
+ "main": "./main.js"
+}
diff --git a/javascript/ql/test/library-tests/EndpointNaming/pack10/foo.js b/javascript/ql/test/library-tests/EndpointNaming/pack10/foo.js
new file mode 100644
index 00000000000..3495843defe
--- /dev/null
+++ b/javascript/ql/test/library-tests/EndpointNaming/pack10/foo.js
@@ -0,0 +1 @@
+export default class FooClass {} // $ class=(pack10).Foo instance=(pack10).Foo.prototype
diff --git a/javascript/ql/test/library-tests/EndpointNaming/pack10/index.js b/javascript/ql/test/library-tests/EndpointNaming/pack10/index.js
new file mode 100644
index 00000000000..59bad5c3ecb
--- /dev/null
+++ b/javascript/ql/test/library-tests/EndpointNaming/pack10/index.js
@@ -0,0 +1,3 @@
+import { default as Foo } from "./foo";
+
+export { Foo }
diff --git a/javascript/ql/test/library-tests/EndpointNaming/pack10/package.json b/javascript/ql/test/library-tests/EndpointNaming/pack10/package.json
new file mode 100644
index 00000000000..977ffaf282b
--- /dev/null
+++ b/javascript/ql/test/library-tests/EndpointNaming/pack10/package.json
@@ -0,0 +1,4 @@
+{
+ "name": "pack10",
+ "main": "./index.js"
+}
diff --git a/javascript/ql/test/library-tests/EndpointNaming/pack2/lib.js b/javascript/ql/test/library-tests/EndpointNaming/pack2/lib.js
new file mode 100644
index 00000000000..ed996a3350e
--- /dev/null
+++ b/javascript/ql/test/library-tests/EndpointNaming/pack2/lib.js
@@ -0,0 +1,6 @@
+class AmbiguousClass {
+ instanceMethod(foo) {} // $ method=(pack2).lib.LibClass.prototype.instanceMethod
+} // $ class=(pack2).lib.LibClass instance=(pack2).lib.LibClass.prototype
+
+export default AmbiguousClass;
+export { AmbiguousClass as LibClass }
diff --git a/javascript/ql/test/library-tests/EndpointNaming/pack2/main.js b/javascript/ql/test/library-tests/EndpointNaming/pack2/main.js
new file mode 100644
index 00000000000..e40015de73f
--- /dev/null
+++ b/javascript/ql/test/library-tests/EndpointNaming/pack2/main.js
@@ -0,0 +1,9 @@
+class AmbiguousClass {
+ instanceMethod() {} // $ method=(pack2).MainClass.prototype.instanceMethod
+} // $ class=(pack2).MainClass instance=(pack2).MainClass.prototype
+
+export default AmbiguousClass;
+export { AmbiguousClass as MainClass }
+
+import * as lib from "./lib";
+export { lib }
diff --git a/javascript/ql/test/library-tests/EndpointNaming/pack2/package.json b/javascript/ql/test/library-tests/EndpointNaming/pack2/package.json
new file mode 100644
index 00000000000..b359913f639
--- /dev/null
+++ b/javascript/ql/test/library-tests/EndpointNaming/pack2/package.json
@@ -0,0 +1,4 @@
+{
+ "name": "pack2",
+ "main": "./main.js"
+}
diff --git a/javascript/ql/test/library-tests/EndpointNaming/pack3/lib.js b/javascript/ql/test/library-tests/EndpointNaming/pack3/lib.js
new file mode 100644
index 00000000000..9ef8c57437d
--- /dev/null
+++ b/javascript/ql/test/library-tests/EndpointNaming/pack3/lib.js
@@ -0,0 +1 @@
+export default function(x,y,z) {} // $ method=(pack3).libFunction
diff --git a/javascript/ql/test/library-tests/EndpointNaming/pack3/main.js b/javascript/ql/test/library-tests/EndpointNaming/pack3/main.js
new file mode 100644
index 00000000000..3f49675b492
--- /dev/null
+++ b/javascript/ql/test/library-tests/EndpointNaming/pack3/main.js
@@ -0,0 +1,7 @@
+function ambiguousFunction(x, y, z) {} // $ method=(pack3).namedFunction
+
+export default ambiguousFunction;
+export { ambiguousFunction as namedFunction };
+
+import libFunction from "./lib";
+export { libFunction };
diff --git a/javascript/ql/test/library-tests/EndpointNaming/pack3/package.json b/javascript/ql/test/library-tests/EndpointNaming/pack3/package.json
new file mode 100644
index 00000000000..0ca9a608332
--- /dev/null
+++ b/javascript/ql/test/library-tests/EndpointNaming/pack3/package.json
@@ -0,0 +1,4 @@
+{
+ "name": "pack3",
+ "main": "./main.js"
+}
diff --git a/javascript/ql/test/library-tests/EndpointNaming/pack4/index.js b/javascript/ql/test/library-tests/EndpointNaming/pack4/index.js
new file mode 100644
index 00000000000..15143e30bf6
--- /dev/null
+++ b/javascript/ql/test/library-tests/EndpointNaming/pack4/index.js
@@ -0,0 +1 @@
+export default class C {} // $ class=(pack4) instance=(pack4).prototype
diff --git a/javascript/ql/test/library-tests/EndpointNaming/pack4/package.json b/javascript/ql/test/library-tests/EndpointNaming/pack4/package.json
new file mode 100644
index 00000000000..fb63d98f8ab
--- /dev/null
+++ b/javascript/ql/test/library-tests/EndpointNaming/pack4/package.json
@@ -0,0 +1,4 @@
+{
+ "name": "pack4",
+ "main": "./index.js"
+}
diff --git a/javascript/ql/test/library-tests/EndpointNaming/pack5/package.json b/javascript/ql/test/library-tests/EndpointNaming/pack5/package.json
new file mode 100644
index 00000000000..d6f924e72ca
--- /dev/null
+++ b/javascript/ql/test/library-tests/EndpointNaming/pack5/package.json
@@ -0,0 +1,4 @@
+{
+ "name": "pack5",
+ "main": "./dist/index.js"
+}
diff --git a/javascript/ql/test/library-tests/EndpointNaming/pack5/src/index.js b/javascript/ql/test/library-tests/EndpointNaming/pack5/src/index.js
new file mode 100644
index 00000000000..d9653840786
--- /dev/null
+++ b/javascript/ql/test/library-tests/EndpointNaming/pack5/src/index.js
@@ -0,0 +1 @@
+export default class C {} // $ class=(pack5) instance=(pack5).prototype
diff --git a/javascript/ql/test/library-tests/EndpointNaming/pack6/index.js b/javascript/ql/test/library-tests/EndpointNaming/pack6/index.js
new file mode 100644
index 00000000000..e15b5319858
--- /dev/null
+++ b/javascript/ql/test/library-tests/EndpointNaming/pack6/index.js
@@ -0,0 +1,6 @@
+class C {
+ instanceMethod() {} // $ method=(pack6).instanceMethod
+ static staticMethod() {} // not accessible
+} // $ instance=(pack6)
+
+export default new C();
diff --git a/javascript/ql/test/library-tests/EndpointNaming/pack6/package.json b/javascript/ql/test/library-tests/EndpointNaming/pack6/package.json
new file mode 100644
index 00000000000..c4daea408e3
--- /dev/null
+++ b/javascript/ql/test/library-tests/EndpointNaming/pack6/package.json
@@ -0,0 +1,4 @@
+{
+ "name": "pack6",
+ "main": "./index.js"
+}
diff --git a/javascript/ql/test/library-tests/EndpointNaming/pack7/index.js b/javascript/ql/test/library-tests/EndpointNaming/pack7/index.js
new file mode 100644
index 00000000000..b56b32095d4
--- /dev/null
+++ b/javascript/ql/test/library-tests/EndpointNaming/pack7/index.js
@@ -0,0 +1,6 @@
+export class D {} // $ class=(pack7).D instance=(pack7).D.prototype
+
+// In this case we are forced to include ".default" to avoid ambiguity with class D above.
+export default {
+ D: class {} // $ class=(pack7).default.D instance=(pack7).default.D.prototype
+};
diff --git a/javascript/ql/test/library-tests/EndpointNaming/pack7/package.json b/javascript/ql/test/library-tests/EndpointNaming/pack7/package.json
new file mode 100644
index 00000000000..a43ae4f5903
--- /dev/null
+++ b/javascript/ql/test/library-tests/EndpointNaming/pack7/package.json
@@ -0,0 +1,4 @@
+{
+ "name": "pack7",
+ "main": "./index.js"
+}
diff --git a/javascript/ql/test/library-tests/EndpointNaming/pack8/foo.js b/javascript/ql/test/library-tests/EndpointNaming/pack8/foo.js
new file mode 100644
index 00000000000..b141c3c81e7
--- /dev/null
+++ b/javascript/ql/test/library-tests/EndpointNaming/pack8/foo.js
@@ -0,0 +1,5 @@
+class Foo {} // $ class=(pack8).Foo instance=(pack8).Foo.prototype
+
+module.exports = Foo;
+module.exports.default = Foo;
+module.exports.Foo = Foo;
diff --git a/javascript/ql/test/library-tests/EndpointNaming/pack8/index.js b/javascript/ql/test/library-tests/EndpointNaming/pack8/index.js
new file mode 100644
index 00000000000..3cf0920f4bf
--- /dev/null
+++ b/javascript/ql/test/library-tests/EndpointNaming/pack8/index.js
@@ -0,0 +1,5 @@
+class Main {} // $ class=(pack8) instance=(pack8).prototype
+
+Main.Foo = require('./foo');
+
+module.exports = Main;
diff --git a/javascript/ql/test/library-tests/EndpointNaming/pack8/package.json b/javascript/ql/test/library-tests/EndpointNaming/pack8/package.json
new file mode 100644
index 00000000000..09d57a3348e
--- /dev/null
+++ b/javascript/ql/test/library-tests/EndpointNaming/pack8/package.json
@@ -0,0 +1,4 @@
+{
+ "name": "pack8",
+ "main": "./index.js"
+}
diff --git a/javascript/ql/test/library-tests/EndpointNaming/pack9/foo.js b/javascript/ql/test/library-tests/EndpointNaming/pack9/foo.js
new file mode 100644
index 00000000000..55d14ad5b52
--- /dev/null
+++ b/javascript/ql/test/library-tests/EndpointNaming/pack9/foo.js
@@ -0,0 +1 @@
+export class Foo {} // $ instance=(pack9/foo).Foo.prototype
diff --git a/javascript/ql/test/library-tests/EndpointNaming/pack9/index.ts b/javascript/ql/test/library-tests/EndpointNaming/pack9/index.ts
new file mode 100644
index 00000000000..65c783aa499
--- /dev/null
+++ b/javascript/ql/test/library-tests/EndpointNaming/pack9/index.ts
@@ -0,0 +1,9 @@
+// Only the type is exposed. For the time being we do not consider type-only declarations or .d.ts files
+// when naming classes.
+export type { Foo } from "./foo";
+
+import * as foo from "./foo";
+
+export function expose() {
+ return new foo.Foo(); // expose an instance of Foo but not the class
+} // $ method=(pack9).expose
diff --git a/javascript/ql/test/library-tests/EndpointNaming/pack9/package.json b/javascript/ql/test/library-tests/EndpointNaming/pack9/package.json
new file mode 100644
index 00000000000..4e69ff9e365
--- /dev/null
+++ b/javascript/ql/test/library-tests/EndpointNaming/pack9/package.json
@@ -0,0 +1,4 @@
+{
+ "name": "pack9",
+ "main": "./index.js"
+}
From 19ba9fed9970ef015e151ac48430b5a549d4e780 Mon Sep 17 00:00:00 2001
From: Asger F
Date: Tue, 30 Jan 2024 17:13:02 +0100
Subject: [PATCH 036/378] Handle externs
---
.../javascript/endpoints/EndpointNaming.qll | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/javascript/ql/lib/semmle/javascript/endpoints/EndpointNaming.qll b/javascript/ql/lib/semmle/javascript/endpoints/EndpointNaming.qll
index 5d4a067874c..95d3d191a70 100644
--- a/javascript/ql/lib/semmle/javascript/endpoints/EndpointNaming.qll
+++ b/javascript/ql/lib/semmle/javascript/endpoints/EndpointNaming.qll
@@ -176,6 +176,18 @@ predicate sinkHasAlias(API::Node sink, string package, string name) {
bindingset[node]
private API::Node getASinkNode(DataFlow::SourceNode node) { result.getAValueReachingSink() = node }
+/**
+ * Holds if `node` is a declaration in an externs file.
+ *
+ * This is to ensure that functions/classes in externs are not named after a re-export in a package.
+ */
+private predicate nameFromExterns(DataFlow::Node node, string package, string name, int badness) {
+ node.getTopLevel().isExterns() and
+ package = "global" and
+ node = AccessPath::getAnAssignmentTo(name) and
+ badness = -10
+}
+
bindingset[qualifiedName]
private int getBadnessOfClassName(string qualifiedName) {
if qualifiedName.matches("%.constructor")
@@ -201,6 +213,8 @@ private predicate classObjectHasNameCandidate(
sinkHasPrimaryName(getASinkNode(cls), package, name, baseBadness) and
badness = baseBadness + getBadnessOfClassName(name)
)
+ or
+ nameFromExterns(cls, package, name, badness)
}
private predicate classObjectHasPrimaryName(
@@ -314,6 +328,8 @@ private predicate functionHasNameCandidate(
name = join(baseName, memberName)
)
)
+ or
+ nameFromExterns(function, package, name, badness)
}
private predicate functionHasPrimaryName(
From 8bd79908a61cefeb9e129a85b1ba1d7d9f51ec53 Mon Sep 17 00:00:00 2001
From: Joe Farebrother
Date: Tue, 30 Jan 2024 16:49:55 +0000
Subject: [PATCH 037/378] Implement local auth query
---
.../java/security/AndroidLocalAuthQuery.qll | 40 +++++++++++++++++++
.../AndroidInsecureLocalAuthentication.ql | 18 +++++++++
2 files changed, 58 insertions(+)
create mode 100644 java/ql/lib/semmle/code/java/security/AndroidLocalAuthQuery.qll
create mode 100644 java/ql/src/Security/CWE/CWE-287/AndroidInsecureLocalAuthentication.ql
diff --git a/java/ql/lib/semmle/code/java/security/AndroidLocalAuthQuery.qll b/java/ql/lib/semmle/code/java/security/AndroidLocalAuthQuery.qll
new file mode 100644
index 00000000000..8c052fc58ee
--- /dev/null
+++ b/java/ql/lib/semmle/code/java/security/AndroidLocalAuthQuery.qll
@@ -0,0 +1,40 @@
+/** Definitions for the insecure local authentication query. */
+
+import java
+
+/** A base class that is used as a callback for biometric authentication. */
+private class AuthenticationCallbackClass extends Class {
+ AuthenticationCallbackClass() {
+ this.hasQualifiedName("android.hardware.fingerprint",
+ "FingerprintManager$AuthenticationCallback")
+ or
+ this.hasQualifiedName("android.hardware.biometrics", "BiometricPrompt$AuthenticationCallback")
+ }
+}
+
+/** An implementation of the `onAuthenticationSucceeded` method for an authentication callback. */
+class AuthenticationSuccessCallback extends Method {
+ AuthenticationSuccessCallback() {
+ this.getDeclaringType().getASupertype+() instanceof AuthenticationCallbackClass and
+ this.hasName("onAuthenticationSucceeded")
+ }
+
+ /** Gets the parameter containing the `authenticationResult` */
+ Parameter getResultParameter() { result = this.getParameter(0) }
+
+ /** Gets a use of the result parameter that's used in a `super` call to the base `AuthenticationCallback` class. */
+ private VarAccess getASuperResultUse() {
+ exists(SuperMethodCall sup |
+ sup.getEnclosingCallable() = this and
+ result = sup.getArgument(0) and
+ result = this.getResultParameter().getAnAccess() and
+ this.getDeclaringType().getASupertype() instanceof AuthenticationCallbackClass
+ )
+ }
+
+ /** Gets a use of the result parameter, other than one used in a `super` call. */
+ VarAccess getAResultUse() {
+ result = this.getResultParameter().getAnAccess() and
+ not result = this.getASuperResultUse()
+ }
+}
diff --git a/java/ql/src/Security/CWE/CWE-287/AndroidInsecureLocalAuthentication.ql b/java/ql/src/Security/CWE/CWE-287/AndroidInsecureLocalAuthentication.ql
new file mode 100644
index 00000000000..22f4582fec3
--- /dev/null
+++ b/java/ql/src/Security/CWE/CWE-287/AndroidInsecureLocalAuthentication.ql
@@ -0,0 +1,18 @@
+/**
+ * @name Insecure local authentication
+ * @description Local authentication that does not make use of a `CryptoObject` can be bypassed.
+ * @kind problem
+ * @problem.severity warning
+ * @security-severity ...TODO
+ * @precision high
+ * @id java/android/insecure-local-authentication
+ * @tags security
+ * external/cwe/cwe-287
+ */
+
+import java
+import semmle.code.java.security.AndroidLocalAuthQuery
+
+from AuthenticationSuccessCallback c
+where not exists(c.getAResultUse())
+select c, "This authentication callback does not use its result for a cryptographic operation."
From ad8038bade1c6bd78c44edb9b8af7bb86c5b9d42 Mon Sep 17 00:00:00 2001
From: Max Schaefer
Date: Wed, 31 Jan 2024 11:16:49 +0000
Subject: [PATCH 038/378] Update MaD Declarations after Triage
---
.../lib/change-notes/2024-01-31-new-models.md | 21 +++++++++++++++++++
java/ql/lib/ext/android.app.model.yml | 1 +
java/ql/lib/ext/java.io.model.yml | 3 ++-
java/ql/lib/ext/java.lang.model.yml | 5 +++++
java/ql/lib/ext/java.net.http.model.yml | 1 +
java/ql/lib/ext/java.net.model.yml | 3 +++
java/ql/lib/ext/java.nio.file.model.yml | 2 ++
java/ql/lib/ext/java.util.zip.model.yml | 1 +
java/ql/lib/ext/javax.servlet.model.yml | 6 +++++-
java/ql/lib/ext/javax.xml.parsers.model.yml | 6 ++++++
java/ql/lib/ext/kotlin.io.model.yml | 1 +
.../lib/ext/org.apache.commons.io.model.yml | 1 +
.../ql/lib/ext/org.apache.hadoop.fs.model.yml | 7 +++++++
.../ext/org.apache.hadoop.fs.s3a.model.yml | 6 ++++++
.../ext/org.apache.http.impl.client.model.yml | 1 +
.../ext/org.eclipse.jetty.client.model.yml | 1 +
java/ql/lib/ext/org.gradle.api.file.model.yml | 1 +
17 files changed, 65 insertions(+), 2 deletions(-)
create mode 100644 java/ql/lib/change-notes/2024-01-31-new-models.md
create mode 100644 java/ql/lib/ext/javax.xml.parsers.model.yml
create mode 100644 java/ql/lib/ext/org.apache.hadoop.fs.s3a.model.yml
diff --git a/java/ql/lib/change-notes/2024-01-31-new-models.md b/java/ql/lib/change-notes/2024-01-31-new-models.md
new file mode 100644
index 00000000000..195c1dd9954
--- /dev/null
+++ b/java/ql/lib/change-notes/2024-01-31-new-models.md
@@ -0,0 +1,21 @@
+---
+category: minorAnalysis
+---
+* Added models for the following packages:
+
+ * android.app
+ * java.io
+ * java.lang
+ * java.net
+ * java.net.http
+ * java.nio.file
+ * java.util.zip
+ * javax.servlet
+ * javax.xml.parsers
+ * kotlin.io
+ * org.apache.commons.io
+ * org.apache.hadoop.fs
+ * org.apache.hadoop.fs.s3a
+ * org.apache.http.impl.client
+ * org.eclipse.jetty.client
+ * org.gradle.api.file
diff --git a/java/ql/lib/ext/android.app.model.yml b/java/ql/lib/ext/android.app.model.yml
index d7a236871a7..bf82aa4cec5 100644
--- a/java/ql/lib/ext/android.app.model.yml
+++ b/java/ql/lib/ext/android.app.model.yml
@@ -6,6 +6,7 @@ extensions:
- ["android.app", "Activity", True, "bindService", "", "", "Argument[0]", "intent-redirection", "manual"]
- ["android.app", "Activity", True, "bindServiceAsUser", "", "", "Argument[0]", "intent-redirection", "manual"]
- ["android.app", "Activity", True, "setResult", "(int,Intent)", "", "Argument[1]", "pending-intents", "manual"]
+ - ["android.app", "Activity", True, "startActivity", "(Intent)", "", "Argument[0]", "intent-redirection", "ai-manual"]
- ["android.app", "Activity", True, "startActivityAsCaller", "", "", "Argument[0]", "intent-redirection", "manual"]
- ["android.app", "Activity", True, "startActivityForResult", "(Intent,int)", "", "Argument[0]", "intent-redirection", "manual"]
- ["android.app", "Activity", True, "startActivityForResult", "(Intent,int,Bundle)", "", "Argument[0]", "intent-redirection", "manual"]
diff --git a/java/ql/lib/ext/java.io.model.yml b/java/ql/lib/ext/java.io.model.yml
index 1bd9251c29d..0fe99bba86c 100644
--- a/java/ql/lib/ext/java.io.model.yml
+++ b/java/ql/lib/ext/java.io.model.yml
@@ -10,6 +10,7 @@ extensions:
- ["java.io", "File", True, "createNewFile", "()", "", "Argument[this]", "path-injection", "ai-manual"]
- ["java.io", "File", True, "createTempFile", "(String,String,File)", "", "Argument[2]", "path-injection", "ai-manual"]
- ["java.io", "File", True, "renameTo", "(File)", "", "Argument[0]", "path-injection", "ai-manual"]
+ - ["java.io", "File", True, "renameTo", "(File)", "", "Argument[this]", "path-injection", "ai-manual"]
- ["java.io", "FileInputStream", True, "FileInputStream", "(File)", "", "Argument[0]", "path-injection", "ai-manual"]
- ["java.io", "FileInputStream", True, "FileInputStream", "(String)", "", "Argument[0]", "path-injection", "ai-manual"]
- ["java.io", "FileOutputStream", False, "FileOutputStream", "", "", "Argument[0]", "path-injection", "manual"]
@@ -132,4 +133,4 @@ extensions:
pack: codeql/java-all
extensible: sourceModel
data:
- - ["java.io", "FileInputStream", True, "FileInputStream", "", "", "Argument[this]", "file", "manual"]
\ No newline at end of file
+ - ["java.io", "FileInputStream", True, "FileInputStream", "", "", "Argument[this]", "file", "manual"]
diff --git a/java/ql/lib/ext/java.lang.model.yml b/java/ql/lib/ext/java.lang.model.yml
index e5ee383c522..8a257e51048 100644
--- a/java/ql/lib/ext/java.lang.model.yml
+++ b/java/ql/lib/ext/java.lang.model.yml
@@ -5,6 +5,10 @@ extensions:
data:
- ["java.lang", "Class", False, "getResource", "(String)", "", "Argument[0]", "path-injection", "ai-manual"]
- ["java.lang", "Class", False, "getResourceAsStream", "(String)", "", "Argument[0]", "path-injection", "ai-manual"]
+ - ["java.lang", "ClassLoader", False, "getSystemResources", "(String)", "", "Argument[0]", "path-injection", "ai-manual"]
+ - ["java.lang", "ClassLoader", True, "getResource", "(String)", "", "Argument[0]", "path-injection", "ai-manual"]
+ - ["java.lang", "ClassLoader", True, "getResourceAsStream", "(String)", "", "Argument[0]", "path-injection", "ai-manual"]
+ - ["java.lang", "ClassLoader", True, "getResources", "(String)", "", "Argument[0]", "path-injection", "ai-manual"]
- ["java.lang", "ClassLoader", True, "getSystemResource", "(String)", "", "Argument[0]", "path-injection", "ai-manual"]
- ["java.lang", "ClassLoader", True, "getSystemResourceAsStream", "(String)", "", "Argument[0]", "path-injection", "ai-manual"]
- ["java.lang", "Module", True, "getResourceAsStream", "(String)", "", "Argument[0]", "path-injection", "ai-manual"]
@@ -14,6 +18,7 @@ extensions:
- ["java.lang", "ProcessBuilder", False, "ProcessBuilder", "(List)", "", "Argument[0]", "command-injection", "ai-manual"]
- ["java.lang", "ProcessBuilder", False, "ProcessBuilder", "(String[])", "", "Argument[0]", "command-injection", "ai-manual"]
- ["java.lang", "ProcessBuilder", False, "redirectError", "(File)", "", "Argument[0]", "path-injection", "ai-manual"]
+ - ["java.lang", "ProcessBuilder", False, "redirectOutput", "(File)", "", "Argument[0]", "path-injection", "ai-manual"]
- ["java.lang", "Runtime", True, "exec", "(String)", "", "Argument[0]", "command-injection", "ai-manual"]
- ["java.lang", "Runtime", True, "exec", "(String[])", "", "Argument[0]", "command-injection", "ai-manual"]
- ["java.lang", "Runtime", True, "exec", "(String[],String[])", "", "Argument[0]", "command-injection", "ai-manual"]
diff --git a/java/ql/lib/ext/java.net.http.model.yml b/java/ql/lib/ext/java.net.http.model.yml
index 9fc18d2eaab..b920eb3da08 100644
--- a/java/ql/lib/ext/java.net.http.model.yml
+++ b/java/ql/lib/ext/java.net.http.model.yml
@@ -8,5 +8,6 @@ extensions:
pack: codeql/java-all
extensible: sinkModel
data:
+ - ["java.net.http", "HttpClient", True, "send", "(HttpRequest,HttpResponse$BodyHandler)", "", "Argument[0]", "request-forgery", "ai-manual"]
- ["java.net.http", "HttpRequest", False, "newBuilder", "", "", "Argument[0]", "request-forgery", "manual"]
- ["java.net.http", "HttpRequest$Builder", False, "uri", "", "", "Argument[0]", "request-forgery", "manual"]
diff --git a/java/ql/lib/ext/java.net.model.yml b/java/ql/lib/ext/java.net.model.yml
index bdc40590fde..c33cd0e83a5 100644
--- a/java/ql/lib/ext/java.net.model.yml
+++ b/java/ql/lib/ext/java.net.model.yml
@@ -10,6 +10,7 @@ extensions:
extensible: sinkModel
data:
- ["java.net", "DatagramPacket", False, "DatagramPacket", "(byte[],int,InetAddress,int)", "", "Argument[2]", "request-forgery", "ai-manual"]
+ - ["java.net", "DatagramPacket", False, "DatagramPacket", "(byte[],int,int,InetAddress,int)", "", "Argument[3]", "request-forgery", "ai-manual"]
- ["java.net", "DatagramSocket", True, "connect", "(SocketAddress)", "", "Argument[0]", "request-forgery", "ai-manual"]
- ["java.net", "PasswordAuthentication", False, "PasswordAuthentication", "(String,char[])", "", "Argument[1]", "credentials-password", "hq-generated"]
- ["java.net", "Socket", True, "Socket", "(String,int)", "", "Argument[0]", "request-forgery", "ai-manual"]
@@ -39,6 +40,8 @@ extensions:
- ["java.net", "InetSocketAddress", True, "InetSocketAddress", "(String,int)", "", "Argument[0]", "Argument[this]", "taint", "ai-manual"]
- ["java.net", "URI", False, "resolve", "(URI)", "", "Argument[this]", "ReturnValue", "taint", "ai-manual"]
- ["java.net", "URI", False, "URI", "(String,String,String,int,String,String,String)", "", "Argument[5]", "Argument[this].SyntheticField[java.net.URI.query]", "taint", "ai-manual"]
+ - ["java.net", "URI", False, "URI", "(String,String,String,int,String,String,String)", "", Argument[4], "ReturnValue", "taint", "ai-manual"]
+ - ["java.net", "URI", False, "URI", "(String,String,String)", "", Argument[1], "ReturnValue", "taint", "ai-manual"]
- ["java.net", "URI", False, "URI", "(String)", "", "Argument[0]", "Argument[this]", "taint", "manual"]
- ["java.net", "URI", False, "create", "", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["java.net", "URI", False, "resolve", "(String)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
diff --git a/java/ql/lib/ext/java.nio.file.model.yml b/java/ql/lib/ext/java.nio.file.model.yml
index 3c77c876eee..567859f47ae 100644
--- a/java/ql/lib/ext/java.nio.file.model.yml
+++ b/java/ql/lib/ext/java.nio.file.model.yml
@@ -67,6 +67,7 @@ extensions:
- ["java.nio.file", "FileSystem", True, "getPath", "(String,String[])", "", "Argument[1]", "ReturnValue", "taint", "ai-manual"]
- ["java.nio.file", "FileSystem", True, "getPathMatcher", "(String)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
- ["java.nio.file", "FileSystem", True, "getRootDirectories", "", "", "Argument[0]", "ReturnValue", "taint", "manual"]
+ - ["java.nio.file", "FileSystems", False, "getFileSystem", "(URI)", "", Argument[0], "ReturnValue", "taint", "ai-manual"]
- ["java.nio.file", "Path", True, "getFileName", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- ["java.nio.file", "Path", True, "getParent", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- ["java.nio.file", "Path", True, "normalize", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
@@ -81,6 +82,7 @@ extensions:
- ["java.nio.file", "Path", False, "toFile", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- ["java.nio.file", "Path", True, "toString", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- ["java.nio.file", "Path", True, "toUri", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
+ - ["java.nio.file", "Paths", False, "get", "(String,String[])", "", Argument[1], "ReturnValue", "taint", "ai-manual"]
- ["java.nio.file", "Paths", True, "get", "", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["java.nio.file", "Paths", True, "get", "", "", "Argument[1].ArrayElement", "ReturnValue", "taint", "manual"]
# Not supported by current lambda flow
diff --git a/java/ql/lib/ext/java.util.zip.model.yml b/java/ql/lib/ext/java.util.zip.model.yml
index 577e6b35723..29c51d5def7 100644
--- a/java/ql/lib/ext/java.util.zip.model.yml
+++ b/java/ql/lib/ext/java.util.zip.model.yml
@@ -5,6 +5,7 @@ extensions:
data:
- ["java.util.zip", "GZIPInputStream", False, "GZIPInputStream", "", "", "Argument[0]", "Argument[this]", "taint", "manual"]
- ["java.util.zip", "ZipEntry", True, "ZipEntry", "(String)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
+ - ["java.util.zip", "ZipFile", True, "getInputStream", "(ZipEntry)", "", Argument[0], "ReturnValue", "taint", "ai-manual"]
- ["java.util.zip", "ZipInputStream", False, "ZipInputStream", "", "", "Argument[0]", "Argument[this]", "taint", "manual"]
- addsTo:
pack: codeql/java-all
diff --git a/java/ql/lib/ext/javax.servlet.model.yml b/java/ql/lib/ext/javax.servlet.model.yml
index 7d7f432d2bd..acd9bb6a6d4 100644
--- a/java/ql/lib/ext/javax.servlet.model.yml
+++ b/java/ql/lib/ext/javax.servlet.model.yml
@@ -9,9 +9,13 @@ extensions:
- ["javax.servlet", "ServletRequest", False, "getParameterNames", "()", "", "ReturnValue", "remote", "manual"]
- ["javax.servlet", "ServletRequest", False, "getParameterValues", "(String)", "", "ReturnValue", "remote", "manual"]
- ["javax.servlet", "ServletRequest", False, "getReader", "()", "", "ReturnValue", "remote", "manual"]
-
- addsTo:
pack: codeql/java-all
extensible: sinkModel
data:
- ["javax.servlet", "ServletContext", True, "getResourceAsStream", "(String)", "", "Argument[0]", "path-injection", "ai-manual"]
+ - addsTo:
+ pack: codeql/java-all
+ extensible: summaryModel
+ data:
+ - ["javax.servlet", "ServletRequest", True, "getParameter", "(String)", "", Argument[0], "ReturnValue", "taint", "ai-manual"]
diff --git a/java/ql/lib/ext/javax.xml.parsers.model.yml b/java/ql/lib/ext/javax.xml.parsers.model.yml
new file mode 100644
index 00000000000..d39a28f5942
--- /dev/null
+++ b/java/ql/lib/ext/javax.xml.parsers.model.yml
@@ -0,0 +1,6 @@
+extensions:
+ - addsTo:
+ pack: codeql/java-all
+ extensible: sinkModel
+ data:
+ - ["javax.xml.parsers", "DocumentBuilder", True, "parse", "(InputSource)", "", "Argument[0]", "xxe", "ai-manual"]
diff --git a/java/ql/lib/ext/kotlin.io.model.yml b/java/ql/lib/ext/kotlin.io.model.yml
index b748e04a292..c65862f6eac 100644
--- a/java/ql/lib/ext/kotlin.io.model.yml
+++ b/java/ql/lib/ext/kotlin.io.model.yml
@@ -3,6 +3,7 @@ extensions:
pack: codeql/java-all
extensible: sinkModel
data:
+ - ["kotlin.io", "FilesKt", False, "appendText$default", "(File,String,Charset,int,Object)", "", "Argument[0]", "path-injection", "ai-manual"]
- ["kotlin.io", "FilesKt", False, "deleteRecursively", "(File)", "", "Argument[0]", "path-injection", "ai-manual"]
- ["kotlin.io", "FilesKt", False, "inputStream", "(File)", "", "Argument[0]", "path-injection", "ai-manual"]
- ["kotlin.io", "FilesKt", False, "readBytes", "(File)", "", "Argument[0]", "path-injection", "ai-manual"]
diff --git a/java/ql/lib/ext/org.apache.commons.io.model.yml b/java/ql/lib/ext/org.apache.commons.io.model.yml
index 20de13c5366..fccecd72912 100644
--- a/java/ql/lib/ext/org.apache.commons.io.model.yml
+++ b/java/ql/lib/ext/org.apache.commons.io.model.yml
@@ -21,6 +21,7 @@ extensions:
- ["org.apache.commons.io", "FileUtils", False, "forceMkdir", "(File)", "", "Argument[0]", "path-injection", "ai-manual"]
- ["org.apache.commons.io", "FileUtils", False, "moveDirectory", "(File,File)", "", "Argument[0]", "path-injection", "ai-manual"]
- ["org.apache.commons.io", "FileUtils", False, "readFileToByteArray", "(File)", "", "Argument[0]", "path-injection", "ai-manual"]
+ - ["org.apache.commons.io", "FileUtils", False, "readFileToString", "(File,Charset)", "", "Argument[0]", "path-injection", "ai-manual"]
- ["org.apache.commons.io", "FileUtils", False, "writeLines", "(File,String,Collection,String)", "", "Argument[3]", "file-content-store", "ai-manual"]
- ["org.apache.commons.io", "FileUtils", False, "writeStringToFile", "(File,String,Charset,boolean)", "", "Argument[1]", "file-content-store", "ai-manual"]
- ["org.apache.commons.io", "FileUtils", True, "copyInputStreamToFile", "(InputStream,File)", "", "Argument[0]", "file-content-store", "ai-manual"]
diff --git a/java/ql/lib/ext/org.apache.hadoop.fs.model.yml b/java/ql/lib/ext/org.apache.hadoop.fs.model.yml
index ba819b73776..79dab1de37b 100644
--- a/java/ql/lib/ext/org.apache.hadoop.fs.model.yml
+++ b/java/ql/lib/ext/org.apache.hadoop.fs.model.yml
@@ -13,3 +13,10 @@ extensions:
- ["org.apache.hadoop.fs", "Path", True, "Path", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "ai-manual"]
- ["org.apache.hadoop.fs", "Path", True, "Path", "(String)", "", "Argument[0]", "Argument[this]", "taint", "ai-manual"]
- ["org.apache.hadoop.fs", "Path", True, "Path", "(URI)", "", "Argument[0]", "Argument[this]", "taint", "ai-manual"]
+ - addsTo:
+ pack: codeql/java-all
+ extensible: sinkModel
+ data:
+ - ["org.apache.hadoop.fs", "FileSystem", True, "makeQualified", "(Path)", "", "Argument[0]", "path-injection", "ai-manual"]
+ - ["org.apache.hadoop.fs", "FileSystem", True, "rename", "(Path,Path)", "", "Argument[0]", "path-injection", "ai-manual"]
+ - ["org.apache.hadoop.fs", "FileSystem", True, "rename", "(Path,Path)", "", "Argument[1]", "path-injection", "ai-manual"]
diff --git a/java/ql/lib/ext/org.apache.hadoop.fs.s3a.model.yml b/java/ql/lib/ext/org.apache.hadoop.fs.s3a.model.yml
new file mode 100644
index 00000000000..4d5d9484335
--- /dev/null
+++ b/java/ql/lib/ext/org.apache.hadoop.fs.s3a.model.yml
@@ -0,0 +1,6 @@
+extensions:
+ - addsTo:
+ pack: codeql/java-all
+ extensible: sinkModel
+ data:
+ - ["org.apache.hadoop.fs.s3a", "WriteOperationHelper", True, "createPutObjectRequest", "(String,File)", "", "Argument[1]", "path-injection", "ai-manual"]
diff --git a/java/ql/lib/ext/org.apache.http.impl.client.model.yml b/java/ql/lib/ext/org.apache.http.impl.client.model.yml
index be517e5344f..6f407ac3682 100644
--- a/java/ql/lib/ext/org.apache.http.impl.client.model.yml
+++ b/java/ql/lib/ext/org.apache.http.impl.client.model.yml
@@ -3,4 +3,5 @@ extensions:
pack: codeql/java-all
extensible: sinkModel
data:
+ - ["org.apache.http.impl.client", "CloseableHttpClient", True, "execute", "(HttpUriRequest)", "", "Argument[0]", "request-forgery", "ai-manual"]
- ["org.apache.http.impl.client", "RequestWrapper", True, "setURI", "(URI)", "", "Argument[0]", "request-forgery", "hq-manual"]
diff --git a/java/ql/lib/ext/org.eclipse.jetty.client.model.yml b/java/ql/lib/ext/org.eclipse.jetty.client.model.yml
index 28c3430e818..bd3b4f58e72 100644
--- a/java/ql/lib/ext/org.eclipse.jetty.client.model.yml
+++ b/java/ql/lib/ext/org.eclipse.jetty.client.model.yml
@@ -3,4 +3,5 @@ extensions:
pack: codeql/java-all
extensible: sinkModel
data:
+ - ["org.eclipse.jetty.client", "HttpClient", True, "GET", "(String)", "", "Argument[0]", "request-forgery", "ai-manual"]
- ["org.eclipse.jetty.client", "HttpClient", True, "newRequest", "(String)", "", "Argument[0]", "request-forgery", "ai-manual"]
diff --git a/java/ql/lib/ext/org.gradle.api.file.model.yml b/java/ql/lib/ext/org.gradle.api.file.model.yml
index 4f492cdbcbc..7123cd0d0ca 100644
--- a/java/ql/lib/ext/org.gradle.api.file.model.yml
+++ b/java/ql/lib/ext/org.gradle.api.file.model.yml
@@ -4,4 +4,5 @@ extensions:
extensible: summaryModel
data:
- ["org.gradle.api.file", "Directory", True, "getAsFile", "()", "", "Argument[this]", "ReturnValue", "taint", "ai-manual"]
+ - ["org.gradle.api.file", "DirectoryProperty", True, "dir", "(String)", "", Argument[0], "ReturnValue", "taint", "ai-manual"]
- ["org.gradle.api.file", "DirectoryProperty", True, "file", "(String)", "", "Argument[this]", "ReturnValue", "taint", "ai-manual"]
From ab6cea14c878c2c4b0b2fae5e6ffe58310e4873e Mon Sep 17 00:00:00 2001
From: Max Schaefer
Date: Wed, 31 Jan 2024 11:49:25 +0000
Subject: [PATCH 039/378] Fix missing quotes.
---
java/ql/lib/ext/java.net.model.yml | 4 ++--
java/ql/lib/ext/java.nio.file.model.yml | 4 ++--
java/ql/lib/ext/java.util.zip.model.yml | 2 +-
java/ql/lib/ext/org.gradle.api.file.model.yml | 2 +-
4 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/java/ql/lib/ext/java.net.model.yml b/java/ql/lib/ext/java.net.model.yml
index 92f1ad7eb05..a6dd7fc5ce8 100644
--- a/java/ql/lib/ext/java.net.model.yml
+++ b/java/ql/lib/ext/java.net.model.yml
@@ -44,8 +44,8 @@ extensions:
- ["java.net", "InetSocketAddress", True, "InetSocketAddress", "(String,int)", "", "Argument[0]", "Argument[this]", "taint", "ai-manual"]
- ["java.net", "URI", False, "resolve", "(URI)", "", "Argument[this]", "ReturnValue", "taint", "ai-manual"]
- ["java.net", "URI", False, "URI", "(String,String,String,int,String,String,String)", "", "Argument[5]", "Argument[this].SyntheticField[java.net.URI.query]", "taint", "ai-manual"]
- - ["java.net", "URI", False, "URI", "(String,String,String,int,String,String,String)", "", Argument[4], "ReturnValue", "taint", "ai-manual"]
- - ["java.net", "URI", False, "URI", "(String,String,String)", "", Argument[1], "ReturnValue", "taint", "ai-manual"]
+ - ["java.net", "URI", False, "URI", "(String,String,String,int,String,String,String)", "", "Argument[4]", "ReturnValue", "taint", "ai-manual"]
+ - ["java.net", "URI", False, "URI", "(String,String,String)", "", "Argument[1]", "ReturnValue", "taint", "ai-manual"]
- ["java.net", "URI", False, "URI", "(String)", "", "Argument[0]", "Argument[this]", "taint", "manual"]
- ["java.net", "URI", False, "create", "", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["java.net", "URI", False, "resolve", "(String)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
diff --git a/java/ql/lib/ext/java.nio.file.model.yml b/java/ql/lib/ext/java.nio.file.model.yml
index 567859f47ae..ea32fa75fe3 100644
--- a/java/ql/lib/ext/java.nio.file.model.yml
+++ b/java/ql/lib/ext/java.nio.file.model.yml
@@ -67,7 +67,7 @@ extensions:
- ["java.nio.file", "FileSystem", True, "getPath", "(String,String[])", "", "Argument[1]", "ReturnValue", "taint", "ai-manual"]
- ["java.nio.file", "FileSystem", True, "getPathMatcher", "(String)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
- ["java.nio.file", "FileSystem", True, "getRootDirectories", "", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- - ["java.nio.file", "FileSystems", False, "getFileSystem", "(URI)", "", Argument[0], "ReturnValue", "taint", "ai-manual"]
+ - ["java.nio.file", "FileSystems", False, "getFileSystem", "(URI)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
- ["java.nio.file", "Path", True, "getFileName", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- ["java.nio.file", "Path", True, "getParent", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- ["java.nio.file", "Path", True, "normalize", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
@@ -82,7 +82,7 @@ extensions:
- ["java.nio.file", "Path", False, "toFile", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- ["java.nio.file", "Path", True, "toString", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- ["java.nio.file", "Path", True, "toUri", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- - ["java.nio.file", "Paths", False, "get", "(String,String[])", "", Argument[1], "ReturnValue", "taint", "ai-manual"]
+ - ["java.nio.file", "Paths", False, "get", "(String,String[])", "", "Argument[1]", "ReturnValue", "taint", "ai-manual"]
- ["java.nio.file", "Paths", True, "get", "", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["java.nio.file", "Paths", True, "get", "", "", "Argument[1].ArrayElement", "ReturnValue", "taint", "manual"]
# Not supported by current lambda flow
diff --git a/java/ql/lib/ext/java.util.zip.model.yml b/java/ql/lib/ext/java.util.zip.model.yml
index 29c51d5def7..611fc7804ef 100644
--- a/java/ql/lib/ext/java.util.zip.model.yml
+++ b/java/ql/lib/ext/java.util.zip.model.yml
@@ -5,7 +5,7 @@ extensions:
data:
- ["java.util.zip", "GZIPInputStream", False, "GZIPInputStream", "", "", "Argument[0]", "Argument[this]", "taint", "manual"]
- ["java.util.zip", "ZipEntry", True, "ZipEntry", "(String)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
- - ["java.util.zip", "ZipFile", True, "getInputStream", "(ZipEntry)", "", Argument[0], "ReturnValue", "taint", "ai-manual"]
+ - ["java.util.zip", "ZipFile", True, "getInputStream", "(ZipEntry)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
- ["java.util.zip", "ZipInputStream", False, "ZipInputStream", "", "", "Argument[0]", "Argument[this]", "taint", "manual"]
- addsTo:
pack: codeql/java-all
diff --git a/java/ql/lib/ext/org.gradle.api.file.model.yml b/java/ql/lib/ext/org.gradle.api.file.model.yml
index 7123cd0d0ca..c3d202aab39 100644
--- a/java/ql/lib/ext/org.gradle.api.file.model.yml
+++ b/java/ql/lib/ext/org.gradle.api.file.model.yml
@@ -4,5 +4,5 @@ extensions:
extensible: summaryModel
data:
- ["org.gradle.api.file", "Directory", True, "getAsFile", "()", "", "Argument[this]", "ReturnValue", "taint", "ai-manual"]
- - ["org.gradle.api.file", "DirectoryProperty", True, "dir", "(String)", "", Argument[0], "ReturnValue", "taint", "ai-manual"]
+ - ["org.gradle.api.file", "DirectoryProperty", True, "dir", "(String)", "", "Argument[0]", "ReturnValue", "taint", "ai-manual"]
- ["org.gradle.api.file", "DirectoryProperty", True, "file", "(String)", "", "Argument[this]", "ReturnValue", "taint", "ai-manual"]
From aa5cccdddd4eaf0989f98af12bd6e1f5d9496e91 Mon Sep 17 00:00:00 2001
From: Asger F
Date: Wed, 31 Jan 2024 20:39:25 +0100
Subject: [PATCH 040/378] JS: Make sinkHasPrimaryName public
---
.../ql/lib/semmle/javascript/endpoints/EndpointNaming.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/javascript/ql/lib/semmle/javascript/endpoints/EndpointNaming.qll b/javascript/ql/lib/semmle/javascript/endpoints/EndpointNaming.qll
index 95d3d191a70..791545fe3ce 100644
--- a/javascript/ql/lib/semmle/javascript/endpoints/EndpointNaming.qll
+++ b/javascript/ql/lib/semmle/javascript/endpoints/EndpointNaming.qll
@@ -150,7 +150,7 @@ private predicate sinkHasPrimaryName(API::Node sink, string package, string name
/**
* Holds if `(package, name)` is the primary name to associate with `node`.
*/
-private predicate sinkHasPrimaryName(API::Node sink, string package, string name) {
+predicate sinkHasPrimaryName(API::Node sink, string package, string name) {
sinkHasPrimaryName(sink, package, name, _)
}
From 817d04c0874ac0fe06ac7fdd8ca7d29aa27c1b5d Mon Sep 17 00:00:00 2001
From: Tom Hvitved
Date: Tue, 30 Jan 2024 14:27:13 +0100
Subject: [PATCH 041/378] C#: Add more delegate flow tests
---
.../dataflow/delegates/DelegateFlow.cs | 26 +++++++++++++++++--
.../dataflow/delegates/DelegateFlow.expected | 8 +++---
2 files changed, 28 insertions(+), 6 deletions(-)
diff --git a/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.cs b/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.cs
index 15d628aea9a..1f9c48445dd 100644
--- a/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.cs
+++ b/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.cs
@@ -128,9 +128,31 @@ class DelegateFlow
void M19(Action a, bool b)
{
if (b)
- a = () => {};
+ a = () => { };
a();
}
- void M20(bool b) => M19(() => {}, b);
+ void M20(bool b) => M19(() => { }, b);
+
+ Action Field;
+ Action Prop2 { get; set; }
+
+ DelegateFlow(Action a, Action b)
+ {
+ Field = a;
+ Prop2 = b;
+ }
+
+ void M20()
+ {
+ new DelegateFlow(
+ _ => { },
+ _ => { }
+ );
+
+ this.Field(0);
+ this.Prop2(0);
+ Field(0);
+ Prop2(0);
+ }
}
diff --git a/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.expected b/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.expected
index 610ff1f06d9..3197db2cfe7 100644
--- a/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.expected
+++ b/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.expected
@@ -22,8 +22,8 @@ delegateCall
| DelegateFlow.cs:89:35:89:37 | delegate call | DelegateFlow.cs:93:13:93:21 | (...) => ... |
| DelegateFlow.cs:114:9:114:16 | function pointer call | DelegateFlow.cs:7:17:7:18 | M2 |
| DelegateFlow.cs:125:9:125:25 | function pointer call | DelegateFlow.cs:7:17:7:18 | M2 |
-| DelegateFlow.cs:132:9:132:11 | delegate call | DelegateFlow.cs:131:17:131:24 | (...) => ... |
-| DelegateFlow.cs:132:9:132:11 | delegate call | DelegateFlow.cs:135:29:135:36 | (...) => ... |
+| DelegateFlow.cs:132:9:132:11 | delegate call | DelegateFlow.cs:131:17:131:25 | (...) => ... |
+| DelegateFlow.cs:132:9:132:11 | delegate call | DelegateFlow.cs:135:29:135:37 | (...) => ... |
viableLambda
| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:16:9:16:20 | call to method M2 | DelegateFlow.cs:16:12:16:19 | (...) => ... |
| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:17:9:17:14 | call to method M2 | DelegateFlow.cs:5:10:5:11 | M1 |
@@ -49,7 +49,7 @@ viableLambda
| DelegateFlow.cs:89:35:89:37 | delegate call | DelegateFlow.cs:93:9:93:22 | call to local function M14 | DelegateFlow.cs:93:13:93:21 | (...) => ... |
| DelegateFlow.cs:114:9:114:16 | function pointer call | DelegateFlow.cs:119:9:119:28 | call to method M16 | DelegateFlow.cs:7:17:7:18 | M2 |
| DelegateFlow.cs:125:9:125:25 | function pointer call | file://:0:0:0:0 | (none) | DelegateFlow.cs:7:17:7:18 | M2 |
-| DelegateFlow.cs:132:9:132:11 | delegate call | DelegateFlow.cs:135:25:135:40 | call to method M19 | DelegateFlow.cs:135:29:135:36 | (...) => ... |
-| DelegateFlow.cs:132:9:132:11 | delegate call | file://:0:0:0:0 | (none) | DelegateFlow.cs:131:17:131:24 | (...) => ... |
+| DelegateFlow.cs:132:9:132:11 | delegate call | DelegateFlow.cs:135:25:135:41 | call to method M19 | DelegateFlow.cs:135:29:135:37 | (...) => ... |
+| DelegateFlow.cs:132:9:132:11 | delegate call | file://:0:0:0:0 | (none) | DelegateFlow.cs:131:17:131:25 | (...) => ... |
| file://:0:0:0:0 | [summary] call to [summary param] position 0 in Lazy in Lazy | DelegateFlow.cs:105:9:105:24 | object creation of type Lazy | DelegateFlow.cs:104:23:104:30 | (...) => ... |
| file://:0:0:0:0 | [summary] call to [summary param] position 0 in Lazy in Lazy | DelegateFlow.cs:107:9:107:24 | object creation of type Lazy | DelegateFlow.cs:106:13:106:20 | (...) => ... |
From bfe4a4bf0b4017e094505130bda0183741e4e0d0 Mon Sep 17 00:00:00 2001
From: Tom Hvitved
Date: Wed, 31 Jan 2024 11:29:54 +0100
Subject: [PATCH 042/378] C#: Additional tracking of lambdas through fields and
properties
---
.../DataFlowConsistency.ql | 11 +--
.../dataflow/internal/DataFlowDispatch.qll | 24 ++++--
.../dataflow/internal/DataFlowPrivate.qll | 85 +++++++++++++++++--
.../dataflow/delegates/DelegateFlow.expected | 8 ++
4 files changed, 105 insertions(+), 23 deletions(-)
diff --git a/csharp/ql/consistency-queries/DataFlowConsistency.ql b/csharp/ql/consistency-queries/DataFlowConsistency.ql
index e1eb8b15a56..0f9dead6b77 100644
--- a/csharp/ql/consistency-queries/DataFlowConsistency.ql
+++ b/csharp/ql/consistency-queries/DataFlowConsistency.ql
@@ -7,15 +7,6 @@ private import codeql.dataflow.internal.DataFlowImplConsistency
private module Input implements InputSig {
private import CsharpDataFlow
- predicate uniqueEnclosingCallableExclude(Node n) {
- // TODO: Remove once static initializers are folded into the
- // static constructors
- exists(ControlFlow::Node cfn |
- cfn.getAstNode() = any(FieldOrProperty f | f.isStatic()).getAChild+() and
- cfn = n.getControlFlowNode()
- )
- }
-
predicate uniqueCallEnclosingCallableExclude(DataFlowCall call) {
// TODO: Remove once static initializers are folded into the
// static constructors
@@ -30,6 +21,8 @@ private module Input implements InputSig {
n instanceof ParameterNode
or
missingLocationExclude(n)
+ or
+ n instanceof FlowInsensitiveFieldNode
}
predicate missingLocationExclude(Node n) {
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll
index e9782315711..55ad89e03d2 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll
@@ -98,7 +98,8 @@ private module Cached {
cached
newtype TDataFlowCallable =
TDotNetCallable(DotNet::Callable c) { c.isUnboundDeclaration() } or
- TSummarizedCallable(DataFlowSummarizedCallable sc)
+ TSummarizedCallable(DataFlowSummarizedCallable sc) or
+ TFieldOrProperty(FieldOrProperty f)
cached
newtype TDataFlowCall =
@@ -247,22 +248,33 @@ class ImplicitCapturedReturnKind extends ReturnKind, TImplicitCapturedReturnKind
/** A callable used for data flow. */
class DataFlowCallable extends TDataFlowCallable {
- /** Get the underlying source code callable, if any. */
+ /** Gets the underlying source code callable, if any. */
DotNet::Callable asCallable() { this = TDotNetCallable(result) }
- /** Get the underlying summarized callable, if any. */
+ /** Gets the underlying summarized callable, if any. */
FlowSummary::SummarizedCallable asSummarizedCallable() { this = TSummarizedCallable(result) }
- /** Get the underlying callable. */
+ /** Gets the underlying field or property, if any. */
+ FieldOrProperty asFieldOrProperty() { this = TFieldOrProperty(result) }
+
+ /** Gets the underlying callable. */
DotNet::Callable getUnderlyingCallable() {
result = this.asCallable() or result = this.asSummarizedCallable()
}
/** Gets a textual representation of this dataflow callable. */
- string toString() { result = this.getUnderlyingCallable().toString() }
+ string toString() {
+ result = this.getUnderlyingCallable().toString()
+ or
+ result = this.asFieldOrProperty().toString()
+ }
/** Get the location of this dataflow callable. */
- Location getLocation() { result = this.getUnderlyingCallable().getLocation() }
+ Location getLocation() {
+ result = this.getUnderlyingCallable().getLocation()
+ or
+ result = this.asFieldOrProperty().getLocation()
+ }
}
/** A call relevant for data flow. */
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll
index 6a1c3c589d3..282465a0a9d 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll
@@ -69,6 +69,17 @@ abstract class NodeImpl extends Node {
abstract string toStringImpl();
}
+// TODO: Remove once static initializers are folded into the
+// static constructors
+private DataFlowCallable getEnclosingStaticFieldOrProperty(Expr e) {
+ result.asFieldOrProperty() =
+ any(FieldOrProperty f |
+ f.isStatic() and
+ e = f.getAChild+() and
+ not exists(e.getEnclosingCallable())
+ )
+}
+
private class ExprNodeImpl extends ExprNode, NodeImpl {
override DataFlowCallable getEnclosingCallableImpl() {
result.asCallable() =
@@ -76,6 +87,8 @@ private class ExprNodeImpl extends ExprNode, NodeImpl {
this.getExpr().(CIL::Expr).getEnclosingCallable().(DotNet::Callable),
this.getControlFlowNodeImpl().getEnclosingCallable()
]
+ or
+ result = getEnclosingStaticFieldOrProperty(this.asExpr())
}
override DotNet::Type getTypeImpl() {
@@ -909,7 +922,8 @@ private module Cached {
TFlowSummaryNode(FlowSummaryImpl::Private::SummaryNode sn) or
TParamsArgumentNode(ControlFlow::Node callCfn) {
callCfn = any(Call c | isParamsArg(c, _, _)).getAControlFlowNode()
- }
+ } or
+ TFlowInsensitiveFieldNode(FieldOrProperty f) { f.isFieldLike() }
/**
* Holds if data flows from `nodeFrom` to `nodeTo` in exactly one local
@@ -1019,6 +1033,8 @@ predicate nodeIsHidden(Node n) {
n instanceof ParamsArgumentNode
or
n.asExpr() = any(WithExpr we).getInitializer()
+ or
+ n instanceof FlowInsensitiveFieldNode
}
/** A CIL SSA definition, viewed as a node in a data flow graph. */
@@ -1344,6 +1360,8 @@ private module ArgumentNodes {
override DataFlowCallable getEnclosingCallableImpl() {
result.asCallable() = cfn.getEnclosingCallable()
+ or
+ result = getEnclosingStaticFieldOrProperty(cfn.getAstNode())
}
override Type getTypeImpl() { result = cfn.getAstNode().(Expr).getType() }
@@ -1383,6 +1401,8 @@ private module ArgumentNodes {
override DataFlowCallable getEnclosingCallableImpl() {
result.asCallable() = callCfn.getEnclosingCallable()
+ or
+ result = getEnclosingStaticFieldOrProperty(callCfn.getAstNode())
}
override Type getTypeImpl() { result = this.getParameter().getType() }
@@ -1782,6 +1802,30 @@ private class FieldOrPropertyRead extends FieldOrPropertyAccess, AssignableRead
}
}
+/**
+ * A data flow node used for control-flow insensitive flow through fields
+ * and properties.
+ *
+ * In global data flow this is used to model flow through static fields and
+ * properties, while for lambda flow we additionally use it to track assignments
+ * in constructors to uses within the same class.
+ */
+class FlowInsensitiveFieldNode extends NodeImpl, TFlowInsensitiveFieldNode {
+ private FieldOrProperty f;
+
+ FlowInsensitiveFieldNode() { this = TFlowInsensitiveFieldNode(f) }
+
+ override DataFlowCallable getEnclosingCallableImpl() { result.asFieldOrProperty() = f }
+
+ override Type getTypeImpl() { result = f.getType() }
+
+ override ControlFlow::Node getControlFlowNodeImpl() { none() }
+
+ override Location getLocationImpl() { result = f.getLocation() }
+
+ override string toStringImpl() { result = "[flow-insensitive] " + f }
+}
+
/**
* Holds if `pred` can flow to `succ`, by jumping from one callable to
* another. Additional steps specified by the configuration are *not*
@@ -1790,13 +1834,16 @@ private class FieldOrPropertyRead extends FieldOrPropertyAccess, AssignableRead
predicate jumpStep(Node pred, Node succ) {
pred.(NonLocalJumpNode).getAJumpSuccessor(true) = succ
or
- exists(FieldOrProperty fl, FieldOrPropertyRead flr |
- fl.isStatic() and
- fl.isFieldLike() and
- fl.getAnAssignedValue() = pred.asExpr() and
- fl.getAnAccess() = flr and
- flr = succ.asExpr() and
- flr.hasNonlocalValue()
+ exists(FieldOrProperty f | f.isStatic() |
+ f.getAnAssignedValue() = pred.asExpr() and
+ succ = TFlowInsensitiveFieldNode(f)
+ or
+ exists(FieldOrPropertyRead fr |
+ pred = TFlowInsensitiveFieldNode(f) and
+ f.getAnAccess() = fr and
+ fr = succ.asExpr() and
+ fr.hasNonlocalValue()
+ )
)
or
FlowSummaryImpl::Private::Steps::summaryJumpStep(pred.(FlowSummaryNode).getSummaryNode(),
@@ -2248,6 +2295,8 @@ module PostUpdateNodes {
override DataFlowCallable getEnclosingCallableImpl() {
result.asCallable() = cfn.getEnclosingCallable()
+ or
+ result = getEnclosingStaticFieldOrProperty(oc)
}
override DotNet::Type getTypeImpl() { result = oc.getType() }
@@ -2279,6 +2328,8 @@ module PostUpdateNodes {
override DataFlowCallable getEnclosingCallableImpl() {
result.asCallable() = cfn.getEnclosingCallable()
+ or
+ result = getEnclosingStaticFieldOrProperty(cfn.getAstNode())
}
override Type getTypeImpl() { result = cfn.getAstNode().(Expr).getType() }
@@ -2427,6 +2478,24 @@ predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preserves
nodeTo.asExpr().(EventRead).getTarget() = aee.getTarget() and
preservesValue = false
)
+ or
+ preservesValue = true and
+ exists(FieldOrProperty f, FieldOrPropertyAccess fa |
+ fa = f.getAnAccess() and
+ fa.targetIsLocalInstance()
+ |
+ exists(AssignableDefinition def |
+ def.getTargetAccess() = fa and
+ nodeFrom.asExpr() = def.getSource() and
+ nodeTo = TFlowInsensitiveFieldNode(f) and
+ nodeFrom.getEnclosingCallable() instanceof Constructor
+ )
+ or
+ nodeFrom = TFlowInsensitiveFieldNode(f) and
+ f.getAnAccess() = fa and
+ fa = nodeTo.asExpr() and
+ fa.(FieldOrPropertyRead).hasNonlocalValue()
+ )
}
/**
diff --git a/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.expected b/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.expected
index 3197db2cfe7..c16036e751c 100644
--- a/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.expected
+++ b/csharp/ql/test/library-tests/dataflow/delegates/DelegateFlow.expected
@@ -24,6 +24,10 @@ delegateCall
| DelegateFlow.cs:125:9:125:25 | function pointer call | DelegateFlow.cs:7:17:7:18 | M2 |
| DelegateFlow.cs:132:9:132:11 | delegate call | DelegateFlow.cs:131:17:131:25 | (...) => ... |
| DelegateFlow.cs:132:9:132:11 | delegate call | DelegateFlow.cs:135:29:135:37 | (...) => ... |
+| DelegateFlow.cs:153:9:153:21 | delegate call | DelegateFlow.cs:149:13:149:20 | (...) => ... |
+| DelegateFlow.cs:154:9:154:21 | delegate call | DelegateFlow.cs:150:13:150:20 | (...) => ... |
+| DelegateFlow.cs:155:9:155:16 | delegate call | DelegateFlow.cs:149:13:149:20 | (...) => ... |
+| DelegateFlow.cs:156:9:156:16 | delegate call | DelegateFlow.cs:150:13:150:20 | (...) => ... |
viableLambda
| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:16:9:16:20 | call to method M2 | DelegateFlow.cs:16:12:16:19 | (...) => ... |
| DelegateFlow.cs:9:9:9:12 | delegate call | DelegateFlow.cs:17:9:17:14 | call to method M2 | DelegateFlow.cs:5:10:5:11 | M1 |
@@ -51,5 +55,9 @@ viableLambda
| DelegateFlow.cs:125:9:125:25 | function pointer call | file://:0:0:0:0 | (none) | DelegateFlow.cs:7:17:7:18 | M2 |
| DelegateFlow.cs:132:9:132:11 | delegate call | DelegateFlow.cs:135:25:135:41 | call to method M19 | DelegateFlow.cs:135:29:135:37 | (...) => ... |
| DelegateFlow.cs:132:9:132:11 | delegate call | file://:0:0:0:0 | (none) | DelegateFlow.cs:131:17:131:25 | (...) => ... |
+| DelegateFlow.cs:153:9:153:21 | delegate call | file://:0:0:0:0 | (none) | DelegateFlow.cs:149:13:149:20 | (...) => ... |
+| DelegateFlow.cs:154:9:154:21 | delegate call | file://:0:0:0:0 | (none) | DelegateFlow.cs:150:13:150:20 | (...) => ... |
+| DelegateFlow.cs:155:9:155:16 | delegate call | file://:0:0:0:0 | (none) | DelegateFlow.cs:149:13:149:20 | (...) => ... |
+| DelegateFlow.cs:156:9:156:16 | delegate call | file://:0:0:0:0 | (none) | DelegateFlow.cs:150:13:150:20 | (...) => ... |
| file://:0:0:0:0 | [summary] call to [summary param] position 0 in Lazy in Lazy | DelegateFlow.cs:105:9:105:24 | object creation of type Lazy | DelegateFlow.cs:104:23:104:30 | (...) => ... |
| file://:0:0:0:0 | [summary] call to [summary param] position 0 in Lazy in Lazy | DelegateFlow.cs:107:9:107:24 | object creation of type Lazy | DelegateFlow.cs:106:13:106:20 | (...) => ... |
From 9098428c2ac5cf83eb0e8e4a7ba13698bbd187aa Mon Sep 17 00:00:00 2001
From: Joe Farebrother
Date: Thu, 1 Feb 2024 14:28:14 +0000
Subject: [PATCH 043/378] Add security severity
---
.../Security/CWE/CWE-287/AndroidInsecureLocalAuthentication.ql | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/java/ql/src/Security/CWE/CWE-287/AndroidInsecureLocalAuthentication.ql b/java/ql/src/Security/CWE/CWE-287/AndroidInsecureLocalAuthentication.ql
index 22f4582fec3..57f256bb40d 100644
--- a/java/ql/src/Security/CWE/CWE-287/AndroidInsecureLocalAuthentication.ql
+++ b/java/ql/src/Security/CWE/CWE-287/AndroidInsecureLocalAuthentication.ql
@@ -3,7 +3,7 @@
* @description Local authentication that does not make use of a `CryptoObject` can be bypassed.
* @kind problem
* @problem.severity warning
- * @security-severity ...TODO
+ * @security-severity 9.3
* @precision high
* @id java/android/insecure-local-authentication
* @tags security
From 5d1edd45c5191086dd3c6190ae28a3518dd0ea90 Mon Sep 17 00:00:00 2001
From: Joe Farebrother
Date: Thu, 1 Feb 2024 16:56:20 +0000
Subject: [PATCH 044/378] Add unit tests
---
.../CWE-287/InsecureLocalAuth.expected | 2 +
.../security/CWE-287/InsecureLocalAuth.ql | 19 ++++
.../query-tests/security/CWE-287/Test.java | 94 +++++++++++++++++++
.../test/query-tests/security/CWE-287/options | 1 +
4 files changed, 116 insertions(+)
create mode 100644 java/ql/test/query-tests/security/CWE-287/InsecureLocalAuth.expected
create mode 100644 java/ql/test/query-tests/security/CWE-287/InsecureLocalAuth.ql
create mode 100644 java/ql/test/query-tests/security/CWE-287/Test.java
create mode 100644 java/ql/test/query-tests/security/CWE-287/options
diff --git a/java/ql/test/query-tests/security/CWE-287/InsecureLocalAuth.expected b/java/ql/test/query-tests/security/CWE-287/InsecureLocalAuth.expected
new file mode 100644
index 00000000000..8ec8033d086
--- /dev/null
+++ b/java/ql/test/query-tests/security/CWE-287/InsecureLocalAuth.expected
@@ -0,0 +1,2 @@
+testFailures
+failures
diff --git a/java/ql/test/query-tests/security/CWE-287/InsecureLocalAuth.ql b/java/ql/test/query-tests/security/CWE-287/InsecureLocalAuth.ql
new file mode 100644
index 00000000000..36becaff755
--- /dev/null
+++ b/java/ql/test/query-tests/security/CWE-287/InsecureLocalAuth.ql
@@ -0,0 +1,19 @@
+import java
+import TestUtilities.InlineExpectationsTest
+import semmle.code.java.dataflow.DataFlow
+import semmle.code.java.security.AndroidLocalAuthQuery
+
+module InsecureAuthTest implements TestSig {
+ string getARelevantTag() { result = "insecure-auth" }
+
+ predicate hasActualResult(Location location, string element, string tag, string value) {
+ tag = "insecure-auth" and
+ exists(AuthenticationSuccessCallback cb | not exists(cb.getAResultUse()) |
+ cb.getLocation() = location and
+ element = cb.toString() and
+ value = ""
+ )
+ }
+}
+
+import MakeTest
diff --git a/java/ql/test/query-tests/security/CWE-287/Test.java b/java/ql/test/query-tests/security/CWE-287/Test.java
new file mode 100644
index 00000000000..e4317efd615
--- /dev/null
+++ b/java/ql/test/query-tests/security/CWE-287/Test.java
@@ -0,0 +1,94 @@
+import android.hardware.biometrics.BiometricPrompt;
+import android.hardware.fingerprint.FingerprintManager;
+
+class TestA {
+ public static void useKey(BiometricPrompt.CryptoObject key) {}
+
+
+ // GOOD: result is used
+ class Test1 extends BiometricPrompt.AuthenticationCallback {
+ @Override
+ public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result) {
+ TestA.useKey(result.getCryptoObject());
+ }
+ }
+
+ // BAD: result is not used
+ class Test2 extends BiometricPrompt.AuthenticationCallback {
+ @Override
+ public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result) { // $insecure-auth
+
+ }
+ }
+
+ // BAD: result is only used in a super call
+ class Test3 extends BiometricPrompt.AuthenticationCallback {
+ @Override
+ public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result) { // $insecure-auth
+ super.onAuthenticationSucceeded(result);
+ }
+ }
+
+ // GOOD: result is used
+ class Test4 extends BiometricPrompt.AuthenticationCallback {
+ @Override
+ public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result) {
+ super.onAuthenticationSucceeded(result);
+ TestA.useKey(result.getCryptoObject());
+ }
+ }
+
+ // GOOD: result is used in a super call to a class other than the base class
+ class Test5 extends Test1 {
+ @Override
+ public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result) {
+ super.onAuthenticationSucceeded(result);
+ }
+ }
+}
+
+class TestB {
+ public static void useKey(FingerprintManager.CryptoObject key) {}
+
+
+ // GOOD: result is used
+ class Test1 extends FingerprintManager.AuthenticationCallback {
+ @Override
+ public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
+ TestB.useKey(result.getCryptoObject());
+ }
+ }
+
+ // BAD: result is not used
+ class Test2 extends FingerprintManager.AuthenticationCallback {
+ @Override
+ public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) { // $insecure-auth
+
+ }
+ }
+
+ // BAD: result is only used in a super call
+ class Test3 extends FingerprintManager.AuthenticationCallback {
+ @Override
+ public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) { // $insecure-auth
+ super.onAuthenticationSucceeded(result);
+ }
+ }
+
+ // GOOD: result is used
+ class Test4 extends FingerprintManager.AuthenticationCallback {
+ @Override
+ public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
+ super.onAuthenticationSucceeded(result);
+ TestB.useKey(result.getCryptoObject());
+ }
+ }
+
+ // GOOD: result is used in a super call to a class other than the base class
+ class Test5 extends Test1 {
+ @Override
+ public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
+ super.onAuthenticationSucceeded(result);
+ }
+ }
+}
\ No newline at end of file
diff --git a/java/ql/test/query-tests/security/CWE-287/options b/java/ql/test/query-tests/security/CWE-287/options
new file mode 100644
index 00000000000..dacd3cb21df
--- /dev/null
+++ b/java/ql/test/query-tests/security/CWE-287/options
@@ -0,0 +1 @@
+//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/google-android-9.0.0
From 88c2ccbecf3371fa1ff04bb9b747e514f40cc21b Mon Sep 17 00:00:00 2001
From: Joe Farebrother
Date: Thu, 1 Feb 2024 16:59:50 +0000
Subject: [PATCH 045/378] Generate stubs
---
.../hardware/biometrics/BiometricPrompt.java | 69 +++++++++++++++++++
.../fingerprint/FingerprintManager.java | 55 +++++++++++++++
.../android/os/Bundle.java | 4 +-
.../android/os/Parcel.java | 22 +++---
.../security/identity/IdentityCredential.java | 32 +++++++++
.../identity/PersonalizationData.java | 9 +++
.../android/security/identity/ResultData.java | 24 +++++++
.../android/util/ArrayMap.java | 10 +--
8 files changed, 207 insertions(+), 18 deletions(-)
create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/hardware/biometrics/BiometricPrompt.java
create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/hardware/fingerprint/FingerprintManager.java
create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/security/identity/IdentityCredential.java
create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/security/identity/PersonalizationData.java
create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/security/identity/ResultData.java
diff --git a/java/ql/test/stubs/google-android-9.0.0/android/hardware/biometrics/BiometricPrompt.java b/java/ql/test/stubs/google-android-9.0.0/android/hardware/biometrics/BiometricPrompt.java
new file mode 100644
index 00000000000..7bc467401bd
--- /dev/null
+++ b/java/ql/test/stubs/google-android-9.0.0/android/hardware/biometrics/BiometricPrompt.java
@@ -0,0 +1,69 @@
+// Generated automatically from android.hardware.biometrics.BiometricPrompt for testing purposes
+
+package android.hardware.biometrics;
+
+import android.os.CancellationSignal;
+import android.security.identity.IdentityCredential;
+import java.security.Signature;
+import java.util.concurrent.Executor;
+import javax.crypto.Cipher;
+import javax.crypto.Mac;
+
+public class BiometricPrompt
+{
+ protected BiometricPrompt() {}
+ abstract static public class AuthenticationCallback
+ {
+ public AuthenticationCallback(){}
+ public void onAuthenticationError(int p0, CharSequence p1){}
+ public void onAuthenticationFailed(){}
+ public void onAuthenticationHelp(int p0, CharSequence p1){}
+ public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult p0){}
+ }
+ public CharSequence getDescription(){ return null; }
+ public CharSequence getNegativeButtonText(){ return null; }
+ public CharSequence getSubtitle(){ return null; }
+ public CharSequence getTitle(){ return null; }
+ public boolean isConfirmationRequired(){ return false; }
+ public int getAllowedAuthenticators(){ return 0; }
+ public static int AUTHENTICATION_RESULT_TYPE_BIOMETRIC = 0;
+ public static int AUTHENTICATION_RESULT_TYPE_DEVICE_CREDENTIAL = 0;
+ public static int BIOMETRIC_ACQUIRED_GOOD = 0;
+ public static int BIOMETRIC_ACQUIRED_IMAGER_DIRTY = 0;
+ public static int BIOMETRIC_ACQUIRED_INSUFFICIENT = 0;
+ public static int BIOMETRIC_ACQUIRED_PARTIAL = 0;
+ public static int BIOMETRIC_ACQUIRED_TOO_FAST = 0;
+ public static int BIOMETRIC_ACQUIRED_TOO_SLOW = 0;
+ public static int BIOMETRIC_ERROR_CANCELED = 0;
+ public static int BIOMETRIC_ERROR_HW_NOT_PRESENT = 0;
+ public static int BIOMETRIC_ERROR_HW_UNAVAILABLE = 0;
+ public static int BIOMETRIC_ERROR_LOCKOUT = 0;
+ public static int BIOMETRIC_ERROR_LOCKOUT_PERMANENT = 0;
+ public static int BIOMETRIC_ERROR_NO_BIOMETRICS = 0;
+ public static int BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL = 0;
+ public static int BIOMETRIC_ERROR_NO_SPACE = 0;
+ public static int BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED = 0;
+ public static int BIOMETRIC_ERROR_TIMEOUT = 0;
+ public static int BIOMETRIC_ERROR_UNABLE_TO_PROCESS = 0;
+ public static int BIOMETRIC_ERROR_USER_CANCELED = 0;
+ public static int BIOMETRIC_ERROR_VENDOR = 0;
+ public void authenticate(BiometricPrompt.CryptoObject p0, CancellationSignal p1, Executor p2, BiometricPrompt.AuthenticationCallback p3){}
+ public void authenticate(CancellationSignal p0, Executor p1, BiometricPrompt.AuthenticationCallback p2){}
+ static public class AuthenticationResult
+ {
+ public BiometricPrompt.CryptoObject getCryptoObject(){ return null; }
+ public int getAuthenticationType(){ return 0; }
+ }
+ static public class CryptoObject
+ {
+ protected CryptoObject() {}
+ public Cipher getCipher(){ return null; }
+ public CryptoObject(Cipher p0){}
+ public CryptoObject(IdentityCredential p0){}
+ public CryptoObject(Mac p0){}
+ public CryptoObject(Signature p0){}
+ public IdentityCredential getIdentityCredential(){ return null; }
+ public Mac getMac(){ return null; }
+ public Signature getSignature(){ return null; }
+ }
+}
diff --git a/java/ql/test/stubs/google-android-9.0.0/android/hardware/fingerprint/FingerprintManager.java b/java/ql/test/stubs/google-android-9.0.0/android/hardware/fingerprint/FingerprintManager.java
new file mode 100644
index 00000000000..235941fb9f4
--- /dev/null
+++ b/java/ql/test/stubs/google-android-9.0.0/android/hardware/fingerprint/FingerprintManager.java
@@ -0,0 +1,55 @@
+// Generated automatically from android.hardware.fingerprint.FingerprintManager for testing purposes
+
+package android.hardware.fingerprint;
+
+import android.os.CancellationSignal;
+import android.os.Handler;
+import java.security.Signature;
+import javax.crypto.Cipher;
+import javax.crypto.Mac;
+
+public class FingerprintManager
+{
+ abstract static public class AuthenticationCallback
+ {
+ public AuthenticationCallback(){}
+ public void onAuthenticationError(int p0, CharSequence p1){}
+ public void onAuthenticationFailed(){}
+ public void onAuthenticationHelp(int p0, CharSequence p1){}
+ public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult p0){}
+ }
+ public boolean hasEnrolledFingerprints(){ return false; }
+ public boolean isHardwareDetected(){ return false; }
+ public static int FINGERPRINT_ACQUIRED_GOOD = 0;
+ public static int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 0;
+ public static int FINGERPRINT_ACQUIRED_INSUFFICIENT = 0;
+ public static int FINGERPRINT_ACQUIRED_PARTIAL = 0;
+ public static int FINGERPRINT_ACQUIRED_TOO_FAST = 0;
+ public static int FINGERPRINT_ACQUIRED_TOO_SLOW = 0;
+ public static int FINGERPRINT_ERROR_CANCELED = 0;
+ public static int FINGERPRINT_ERROR_HW_NOT_PRESENT = 0;
+ public static int FINGERPRINT_ERROR_HW_UNAVAILABLE = 0;
+ public static int FINGERPRINT_ERROR_LOCKOUT = 0;
+ public static int FINGERPRINT_ERROR_LOCKOUT_PERMANENT = 0;
+ public static int FINGERPRINT_ERROR_NO_FINGERPRINTS = 0;
+ public static int FINGERPRINT_ERROR_NO_SPACE = 0;
+ public static int FINGERPRINT_ERROR_TIMEOUT = 0;
+ public static int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 0;
+ public static int FINGERPRINT_ERROR_USER_CANCELED = 0;
+ public static int FINGERPRINT_ERROR_VENDOR = 0;
+ public void authenticate(FingerprintManager.CryptoObject p0, CancellationSignal p1, int p2, FingerprintManager.AuthenticationCallback p3, Handler p4){}
+ static public class AuthenticationResult
+ {
+ public FingerprintManager.CryptoObject getCryptoObject(){ return null; }
+ }
+ static public class CryptoObject
+ {
+ protected CryptoObject() {}
+ public Cipher getCipher(){ return null; }
+ public CryptoObject(Cipher p0){}
+ public CryptoObject(Mac p0){}
+ public CryptoObject(Signature p0){}
+ public Mac getMac(){ return null; }
+ public Signature getSignature(){ return null; }
+ }
+}
diff --git a/java/ql/test/stubs/google-android-9.0.0/android/os/Bundle.java b/java/ql/test/stubs/google-android-9.0.0/android/os/Bundle.java
index 4beb1cf5dee..d1154c915ce 100644
--- a/java/ql/test/stubs/google-android-9.0.0/android/os/Bundle.java
+++ b/java/ql/test/stubs/google-android-9.0.0/android/os/Bundle.java
@@ -15,9 +15,9 @@ import java.util.ArrayList;
public class Bundle extends BaseBundle implements Cloneable, Parcelable
{
- public ArrayList getParcelableArrayList(String p0){ return null; }
- public SparseArray getSparseParcelableArray(String p0){ return null; }
public T getParcelable(String p0){ return null; }
+ public android.util.SparseArray getSparseParcelableArray(String p0){ return null; }
+ public java.util.ArrayList getParcelableArrayList(String p0){ return null; }
public ArrayList getCharSequenceArrayList(String p0){ return null; }
public ArrayList getIntegerArrayList(String p0){ return null; }
public ArrayList getStringArrayList(String p0){ return null; }
diff --git a/java/ql/test/stubs/google-android-9.0.0/android/os/Parcel.java b/java/ql/test/stubs/google-android-9.0.0/android/os/Parcel.java
index ef6dcdeb085..9bf19e32269 100644
--- a/java/ql/test/stubs/google-android-9.0.0/android/os/Parcel.java
+++ b/java/ql/test/stubs/google-android-9.0.0/android/os/Parcel.java
@@ -24,24 +24,24 @@ public class Parcel
{
protected Parcel() {}
protected void finalize(){}
- public ArrayMap createTypedArrayMap(Parcelable.Creator p0){ return null; }
- public List readParcelableList(List p0, ClassLoader p1){ return null; }
- public SparseArray createTypedSparseArray(Parcelable.Creator p0){ return null; }
public T readParcelable(ClassLoader p0){ return null; }
+ public android.util.ArrayMap createTypedArrayMap(Parcelable.Creator p0){ return null; }
+ public android.util.SparseArray createTypedSparseArray(Parcelable.Creator p0){ return null; }
+ public java.util.List readParcelableList(java.util.List p0, ClassLoader p1){ return null; }
public void writeParcelableArray(T[] p0, int p1){}
- public void writeParcelableList(List p0, int p1){}
+ public void writeParcelableList(java.util.List p0, int p1){}
public void writeTypedArray(T[] p0, int p1){}
- public void writeTypedArrayMap(ArrayMap p0, int p1){}
- public void writeTypedList(List p0){}
+ public void writeTypedArrayMap(android.util.ArrayMap p0, int p1){}
+ public void writeTypedList(java.util.List p0){}
public void writeTypedObject(T p0, int p1){}
- public void writeTypedSparseArray(SparseArray p0, int p1){}
- public ArrayList createTypedArrayList(Parcelable.Creator p0){ return null; }
- public SparseArray readSparseArray(ClassLoader p0){ return null; }
+ public void writeTypedSparseArray(android.util.SparseArray p0, int p1){}
public T readTypedObject(Parcelable.Creator p0){ return null; }
public T[] createTypedArray(Parcelable.Creator p0){ return null; }
+ public android.util.SparseArray readSparseArray(ClassLoader p0){ return null; }
+ public java.util.ArrayList createTypedArrayList(Parcelable.Creator p0){ return null; }
public void readTypedArray(T[] p0, Parcelable.Creator p1){}
- public void readTypedList(List p0, Parcelable.Creator p1){}
- public void writeSparseArray(SparseArray p0){}
+ public void readTypedList(java.util.List p0, Parcelable.Creator p1){}
+ public void writeSparseArray(android.util.SparseArray p0){}
public ArrayList readArrayList(ClassLoader p0){ return null; }
public ArrayList createBinderArrayList(){ return null; }
public ArrayList createStringArrayList(){ return null; }
diff --git a/java/ql/test/stubs/google-android-9.0.0/android/security/identity/IdentityCredential.java b/java/ql/test/stubs/google-android-9.0.0/android/security/identity/IdentityCredential.java
new file mode 100644
index 00000000000..12dee91453c
--- /dev/null
+++ b/java/ql/test/stubs/google-android-9.0.0/android/security/identity/IdentityCredential.java
@@ -0,0 +1,32 @@
+// Generated automatically from android.security.identity.IdentityCredential for testing purposes
+
+package android.security.identity;
+
+import android.security.identity.PersonalizationData;
+import android.security.identity.ResultData;
+import java.security.KeyPair;
+import java.security.PublicKey;
+import java.security.cert.X509Certificate;
+import java.time.Instant;
+import java.util.Collection;
+import java.util.Map;
+
+abstract public class IdentityCredential
+{
+ public abstract Collection getAuthKeysNeedingCertification();
+ public abstract Collection getCredentialKeyCertificateChain();
+ public abstract KeyPair createEphemeralKeyPair();
+ public abstract ResultData getEntries(byte[] p0, Map> p1, byte[] p2, byte[] p3);
+ public abstract byte[] decryptMessageFromReader(byte[] p0);
+ public abstract byte[] encryptMessageToReader(byte[] p0);
+ public abstract int[] getAuthenticationDataUsageCount();
+ public abstract void setAllowUsingExhaustedKeys(boolean p0);
+ public abstract void setAvailableAuthenticationKeys(int p0, int p1);
+ public abstract void setReaderEphemeralPublicKey(PublicKey p0);
+ public abstract void storeStaticAuthenticationData(X509Certificate p0, byte[] p1);
+ public byte[] delete(byte[] p0){ return null; }
+ public byte[] proveOwnership(byte[] p0){ return null; }
+ public byte[] update(PersonalizationData p0){ return null; }
+ public void setAllowUsingExpiredKeys(boolean p0){}
+ public void storeStaticAuthenticationData(X509Certificate p0, Instant p1, byte[] p2){}
+}
diff --git a/java/ql/test/stubs/google-android-9.0.0/android/security/identity/PersonalizationData.java b/java/ql/test/stubs/google-android-9.0.0/android/security/identity/PersonalizationData.java
new file mode 100644
index 00000000000..eecfc828c56
--- /dev/null
+++ b/java/ql/test/stubs/google-android-9.0.0/android/security/identity/PersonalizationData.java
@@ -0,0 +1,9 @@
+// Generated automatically from android.security.identity.PersonalizationData for testing purposes
+
+package android.security.identity;
+
+
+public class PersonalizationData
+{
+ protected PersonalizationData() {}
+}
diff --git a/java/ql/test/stubs/google-android-9.0.0/android/security/identity/ResultData.java b/java/ql/test/stubs/google-android-9.0.0/android/security/identity/ResultData.java
new file mode 100644
index 00000000000..e8ed2f13012
--- /dev/null
+++ b/java/ql/test/stubs/google-android-9.0.0/android/security/identity/ResultData.java
@@ -0,0 +1,24 @@
+// Generated automatically from android.security.identity.ResultData for testing purposes
+
+package android.security.identity;
+
+import java.util.Collection;
+
+abstract public class ResultData
+{
+ public abstract Collection getEntryNames(String p0);
+ public abstract Collection getNamespaces();
+ public abstract Collection getRetrievedEntryNames(String p0);
+ public abstract byte[] getAuthenticatedData();
+ public abstract byte[] getEntry(String p0, String p1);
+ public abstract byte[] getMessageAuthenticationCode();
+ public abstract byte[] getStaticAuthenticationData();
+ public abstract int getStatus(String p0, String p1);
+ public static int STATUS_NOT_IN_REQUEST_MESSAGE = 0;
+ public static int STATUS_NOT_REQUESTED = 0;
+ public static int STATUS_NO_ACCESS_CONTROL_PROFILES = 0;
+ public static int STATUS_NO_SUCH_ENTRY = 0;
+ public static int STATUS_OK = 0;
+ public static int STATUS_READER_AUTHENTICATION_FAILED = 0;
+ public static int STATUS_USER_AUTHENTICATION_FAILED = 0;
+}
diff --git a/java/ql/test/stubs/google-android-9.0.0/android/util/ArrayMap.java b/java/ql/test/stubs/google-android-9.0.0/android/util/ArrayMap.java
index 55c7adebea7..c0b6016c764 100644
--- a/java/ql/test/stubs/google-android-9.0.0/android/util/ArrayMap.java
+++ b/java/ql/test/stubs/google-android-9.0.0/android/util/ArrayMap.java
@@ -6,15 +6,12 @@ import java.util.Collection;
import java.util.Map;
import java.util.Set;
-public class ArrayMap implements Map
+public class ArrayMap implements java.util.Map
{
public ArrayMap(){}
public ArrayMap(ArrayMap p0){}
public ArrayMap(int p0){}
- public Collection values(){ return null; }
public K keyAt(int p0){ return null; }
- public Set keySet(){ return null; }
- public Set> entrySet(){ return null; }
public String toString(){ return null; }
public V get(Object p0){ return null; }
public V put(K p0, V p1){ return null; }
@@ -33,8 +30,11 @@ public class ArrayMap implements Map
public int indexOfKey(Object p0){ return 0; }
public int indexOfValue(Object p0){ return 0; }
public int size(){ return 0; }
+ public java.util.Collection values(){ return null; }
+ public java.util.Set keySet(){ return null; }
+ public java.util.Set> entrySet(){ return null; }
public void clear(){}
public void ensureCapacity(int p0){}
public void putAll(ArrayMap extends K, ? extends V> p0){}
- public void putAll(Map extends K, ? extends V> p0){}
+ public void putAll(java.util.Map extends K, ? extends V> p0){}
}
From 8a2485a22f7000ca7842547a7b769a3c4782ee34 Mon Sep 17 00:00:00 2001
From: Asger F
Date: Thu, 1 Feb 2024 20:54:27 +0100
Subject: [PATCH 046/378] JS: Address some comments
---
.../ql/lib/semmle/javascript/endpoints/EndpointNaming.qll | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/javascript/ql/lib/semmle/javascript/endpoints/EndpointNaming.qll b/javascript/ql/lib/semmle/javascript/endpoints/EndpointNaming.qll
index 791545fe3ce..c01683535cf 100644
--- a/javascript/ql/lib/semmle/javascript/endpoints/EndpointNaming.qll
+++ b/javascript/ql/lib/semmle/javascript/endpoints/EndpointNaming.qll
@@ -8,7 +8,7 @@
* However, there are cases where classes and functions can be exposed to client
* code without being accessible as a qualified name. For example;
* ```js
- * // 'Foo' is internal, but clients can reach its methods via `getFoo().m()`
+ * // 'Foo' is internal, but clients can call its methods, e.g. `getFoo().m()`
* class Foo {
* m() {}
* }
@@ -16,7 +16,7 @@
* return new Foo();
* }
*
- * // Clients can reach m() via getObj().m()
+ * // Clients can call m() via getObj().m()
* export function getObj() {
* return {
* m() {}
From 2a00375bb7fdc980b82cb6a03a7273f0998949a3 Mon Sep 17 00:00:00 2001
From: Joe Farebrother
Date: Fri, 2 Feb 2024 14:34:43 +0000
Subject: [PATCH 047/378] Add documentation
---
.../AndroidInsecureLocalAuthentication.qhelp | 42 ++++++++++++++++
...AndroidInsecureLocalAuthenticationBad.java | 11 +++++
...ndroidInsecureLocalAuthenticationGood.java | 48 +++++++++++++++++++
3 files changed, 101 insertions(+)
create mode 100644 java/ql/src/Security/CWE/CWE-287/AndroidInsecureLocalAuthentication.qhelp
create mode 100644 java/ql/src/Security/CWE/CWE-287/AndroidInsecureLocalAuthenticationBad.java
create mode 100644 java/ql/src/Security/CWE/CWE-287/AndroidInsecureLocalAuthenticationGood.java
diff --git a/java/ql/src/Security/CWE/CWE-287/AndroidInsecureLocalAuthentication.qhelp b/java/ql/src/Security/CWE/CWE-287/AndroidInsecureLocalAuthentication.qhelp
new file mode 100644
index 00000000000..15e6783d2dc
--- /dev/null
+++ b/java/ql/src/Security/CWE/CWE-287/AndroidInsecureLocalAuthentication.qhelp
@@ -0,0 +1,42 @@
+
+
+
+
+
+Biometric local authentication such as fingerprint recognistion can be used to protect sensitive data or actions within an application.
+However, if this authentication does not make use of a KeyStore-backed key, it is able to be bypassed by a privileged malicious application or an attacker with physical access.
+
+
+
+
+
+Generate a secure key in the Android KeyStore and ensure that the onAuthenticaionSuccess callback for a biometric prompt uses it
+in a way that is required for the sensitive parts of the application to function, such as by using it to decrypt sensitive data or credentials.
+
+
+
+
+
In the following (bad) case, no CryptoObject is required for the biometric prompt to grant access, so it can be bypassed.
+
+
In he following (good) case, a secret key is generated in the Android KeyStore that is required for the application to grant access.
+
+
+
diff --git a/java/ql/src/Security/CWE/CWE-287/AndroidInsecureLocalAuthenticationBad.java b/java/ql/src/Security/CWE/CWE-287/AndroidInsecureLocalAuthenticationBad.java
new file mode 100644
index 00000000000..464153ccbee
--- /dev/null
+++ b/java/ql/src/Security/CWE/CWE-287/AndroidInsecureLocalAuthenticationBad.java
@@ -0,0 +1,11 @@
+biometricPrompt.authenticate(
+ cancellationSignal,
+ executor,
+ new BiometricPrompt.AuthenticationCallback {
+ @Override
+ // BAD: This authentication callback does not make use of a `CryptoObject` from the `result`.
+ public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result) {
+ grantAccess()
+ }
+ }
+)
\ No newline at end of file
diff --git a/java/ql/src/Security/CWE/CWE-287/AndroidInsecureLocalAuthenticationGood.java b/java/ql/src/Security/CWE/CWE-287/AndroidInsecureLocalAuthenticationGood.java
new file mode 100644
index 00000000000..0f41b31a292
--- /dev/null
+++ b/java/ql/src/Security/CWE/CWE-287/AndroidInsecureLocalAuthenticationGood.java
@@ -0,0 +1,48 @@
+private void generateSecretKey() {
+ KeyGenParameterSpec keyGenParameterSpec = new KeyGenParameterSpec.Builder(
+ "MySecretKey",
+ KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
+ .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
+ .setUserAuthenticationRequired(true)
+ .setInvalidatedByBiometricEnrollment(true)
+ .build();
+ KeyGenerator keyGenerator = KeyGenerator.getInstance(
+ KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
+ keyGenerator.init(keyGenParameterSpec);
+ keyGenerator.generateKey();
+}
+
+
+private SecretKey getSecretKey() {
+ KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+ keyStore.load(null);
+ return ((SecretKey)keyStore.getKey("MySecretKey", null));
+}
+
+private Cipher getCipher() {
+ return Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
+ + KeyProperties.BLOCK_MODE_CBC + "/"
+ + KeyProperties.ENCRYPTION_PADDING_PKCS7);
+}
+
+public prompt() {
+ Cipher cipher = getCipher();
+ SecretKey secretKey = getSecretKey();
+ cipher.init(Cipher.DECRYPT_MODE, secretKey);
+
+ biometricPrompt.authenticate(
+ new BiometricPrompt.CryptoObject(cipher);
+ cancellationSignal,
+ executor,
+ new BiometricPrompt.AuthenticationCallback {
+ @Override
+ // GOOD: This authentication callback uses the result to decrypt some data.
+ public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result) {
+ Cipher cipher = result.getCryptoObject().getCipher();
+ byte[] decryptedData = cipher.doFinal(encryptedData);
+ grantAccessWithData(decryptedData);
+ }
+ }
+ );
+}
\ No newline at end of file
From 514a92d5bd1e24e4b7367d64430762ffd1ffbe7f Mon Sep 17 00:00:00 2001
From: Nick Rolfe
Date: Wed, 1 Nov 2023 22:33:21 +0000
Subject: [PATCH 048/378] Tree-sitter extractors: use fresh IDs for locations
Since locations for any given source file are never referenced in any
TRAP files besides the one for that particular source file, it's not
necessary to use global IDs. Using fresh IDs will reduce the size of the
ID pool (both on disk and in memory) and the speed of multi-threaded
TRAP import.
The one exception is the empty location, which still uses a global ID.
---
.../src/extractor/mod.rs | 128 ++++++++++++------
shared/tree-sitter-extractor/src/trap.rs | 22 +++
2 files changed, 105 insertions(+), 45 deletions(-)
diff --git a/shared/tree-sitter-extractor/src/extractor/mod.rs b/shared/tree-sitter-extractor/src/extractor/mod.rs
index 913e637d92b..0d493ebd9e1 100644
--- a/shared/tree-sitter-extractor/src/extractor/mod.rs
+++ b/shared/tree-sitter-extractor/src/extractor/mod.rs
@@ -43,7 +43,16 @@ fn populate_empty_file(writer: &mut trap::Writer) -> trap::Label {
pub fn populate_empty_location(writer: &mut trap::Writer) {
let file_label = populate_empty_file(writer);
- location(writer, file_label, 0, 0, 0, 0);
+ global_location(
+ writer,
+ file_label,
+ trap::Location {
+ start_line: 0,
+ start_column: 0,
+ end_line: 0,
+ end_column: 0,
+ },
+ );
}
pub fn populate_parent_folders(
@@ -85,17 +94,19 @@ pub fn populate_parent_folders(
}
}
-fn location(
+/** Get the label for the given location, defining it a global ID if it doesn't exist yet. */
+fn global_location(
writer: &mut trap::Writer,
file_label: trap::Label,
- start_line: usize,
- start_column: usize,
- end_line: usize,
- end_column: usize,
+ location: trap::Location,
) -> trap::Label {
let (loc_label, fresh) = writer.global_id(&format!(
"loc,{{{}}},{},{},{},{}",
- file_label, start_line, start_column, end_line, end_column
+ file_label,
+ location.start_line,
+ location.start_column,
+ location.end_line,
+ location.end_column
));
if fresh {
writer.add_tuple(
@@ -103,10 +114,34 @@ fn location(
vec![
trap::Arg::Label(loc_label),
trap::Arg::Label(file_label),
- trap::Arg::Int(start_line),
- trap::Arg::Int(start_column),
- trap::Arg::Int(end_line),
- trap::Arg::Int(end_column),
+ trap::Arg::Int(location.start_line),
+ trap::Arg::Int(location.start_column),
+ trap::Arg::Int(location.end_line),
+ trap::Arg::Int(location.end_column),
+ ],
+ );
+ }
+ loc_label
+}
+
+/** Get the label for the given location, creating it as a fresh ID if we haven't seen the location
+ * yet for this file. */
+fn location_label(
+ writer: &mut trap::Writer,
+ file_label: trap::Label,
+ location: trap::Location,
+) -> trap::Label {
+ let (loc_label, fresh) = writer.location_label(location);
+ if fresh {
+ writer.add_tuple(
+ "locations_default",
+ vec![
+ trap::Arg::Label(loc_label),
+ trap::Arg::Label(file_label),
+ trap::Arg::Int(location.start_line),
+ trap::Arg::Int(location.start_column),
+ trap::Arg::Int(location.end_line),
+ trap::Arg::Int(location.end_column),
],
);
}
@@ -245,26 +280,25 @@ impl<'a> Visitor<'a> {
node: Node,
status_page: bool,
) {
- let (start_line, start_column, end_line, end_column) = location_for(self, node);
- let loc = location(
- self.trap_writer,
- self.file_label,
- start_line,
- start_column,
- end_line,
- end_column,
- );
+ let loc = location_for(self, node);
+ let loc_label = location_label(self.trap_writer, self.file_label, loc);
let mut mesg = self.diagnostics_writer.new_entry(
"parse-error",
"Could not process some files due to syntax errors",
);
mesg.severity(diagnostics::Severity::Warning)
- .location(self.path, start_line, start_column, end_line, end_column)
+ .location(
+ self.path,
+ loc.start_line,
+ loc.start_column,
+ loc.end_line,
+ loc.end_column,
+ )
.message(message, args);
if status_page {
mesg.status_page();
}
- self.record_parse_error(loc, &mesg);
+ self.record_parse_error(loc_label, &mesg);
}
fn enter_node(&mut self, node: Node) -> bool {
@@ -298,15 +332,8 @@ impl<'a> Visitor<'a> {
return;
}
let (id, _, child_nodes) = self.stack.pop().expect("Vistor: empty stack");
- let (start_line, start_column, end_line, end_column) = location_for(self, node);
- let loc = location(
- self.trap_writer,
- self.file_label,
- start_line,
- start_column,
- end_line,
- end_column,
- );
+ let loc = location_for(self, node);
+ let loc_label = location_label(self.trap_writer, self.file_label, loc);
let table = self
.schema
.get(&TypeName {
@@ -333,7 +360,7 @@ impl<'a> Visitor<'a> {
trap::Arg::Label(id),
trap::Arg::Label(parent_id),
trap::Arg::Int(parent_index),
- trap::Arg::Label(loc),
+ trap::Arg::Label(loc_label),
],
);
self.trap_writer.add_tuple(
@@ -356,7 +383,7 @@ impl<'a> Visitor<'a> {
trap::Arg::Label(id),
trap::Arg::Label(parent_id),
trap::Arg::Int(parent_index),
- trap::Arg::Label(loc),
+ trap::Arg::Label(loc_label),
],
);
let mut all_args = vec![trap::Arg::Label(id)];
@@ -366,14 +393,20 @@ impl<'a> Visitor<'a> {
}
_ => {
self.record_parse_error(
- loc,
+ loc_label,
self.diagnostics_writer
.new_entry(
"parse-error",
"Could not process some files due to syntax errors",
)
.severity(diagnostics::Severity::Warning)
- .location(self.path, start_line, start_column, end_line, end_column)
+ .location(
+ self.path,
+ loc.start_line,
+ loc.start_column,
+ loc.end_line,
+ loc.end_column,
+ )
.message(
"Unknown table type: {}",
&[diagnostics::MessageArg::Code(node.kind())],
@@ -555,7 +588,7 @@ fn sliced_source_arg(source: &[u8], n: Node) -> trap::Arg {
// Emit a pair of `TrapEntry`s for the provided node, appropriately calibrated.
// The first is the location and label definition, and the second is the
// 'Located' entry.
-fn location_for(visitor: &mut Visitor, n: Node) -> (usize, usize, usize, usize) {
+fn location_for(visitor: &mut Visitor, n: Node) -> trap::Location {
// Tree-sitter row, column values are 0-based while CodeQL starts
// counting at 1. In addition Tree-sitter's row and column for the
// end position are exclusive while CodeQL's end positions are inclusive.
@@ -565,16 +598,16 @@ fn location_for(visitor: &mut Visitor, n: Node) -> (usize, usize, usize, usize)
// the end column is 0 (start of a line). In such cases the end position must be
// set to the end of the previous line.
let start_line = n.start_position().row + 1;
- let start_col = n.start_position().column + 1;
+ let start_column = n.start_position().column + 1;
let mut end_line = n.end_position().row + 1;
- let mut end_col = n.end_position().column;
- if start_line > end_line || start_line == end_line && start_col > end_col {
+ let mut end_column = n.end_position().column;
+ if start_line > end_line || start_line == end_line && start_column > end_column {
// the range is empty, clip it to sensible values
end_line = start_line;
- end_col = start_col - 1;
- } else if end_col == 0 {
+ end_column = start_column - 1;
+ } else if end_column == 0 {
let source = visitor.source;
- // end_col = 0 means that we are at the start of a line
+ // end_column = 0 means that we are at the start of a line
// unfortunately 0 is invalid as column number, therefore
// we should update the end location to be the end of the
// previous line
@@ -591,10 +624,10 @@ fn location_for(visitor: &mut Visitor, n: Node) -> (usize, usize, usize, usize)
);
}
end_line -= 1;
- end_col = 1;
+ end_column = 1;
while index > 0 && source[index - 1] != b'\n' {
index -= 1;
- end_col += 1;
+ end_column += 1;
}
} else {
visitor.diagnostics_writer.write(
@@ -612,7 +645,12 @@ fn location_for(visitor: &mut Visitor, n: Node) -> (usize, usize, usize, usize)
);
}
}
- (start_line, start_col, end_line, end_col)
+ trap::Location {
+ start_line,
+ start_column,
+ end_line,
+ end_column,
+ }
}
fn traverse(tree: &Tree, visitor: &mut Visitor) {
diff --git a/shared/tree-sitter-extractor/src/trap.rs b/shared/tree-sitter-extractor/src/trap.rs
index 135e336338f..64c06539ecb 100644
--- a/shared/tree-sitter-extractor/src/trap.rs
+++ b/shared/tree-sitter-extractor/src/trap.rs
@@ -5,6 +5,14 @@ use std::path::Path;
use flate2::write::GzEncoder;
+#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash)]
+pub struct Location {
+ pub start_line: usize,
+ pub start_column: usize,
+ pub end_line: usize,
+ pub end_column: usize,
+}
+
pub struct Writer {
/// The accumulated trap entries
trap_output: Vec,
@@ -12,6 +20,8 @@ pub struct Writer {
counter: u32,
/// cache of global keys
global_keys: std::collections::HashMap,
+ /// Labels for locations, which don't use global keys
+ location_labels: std::collections::HashMap,
}
impl Writer {
@@ -20,6 +30,7 @@ impl Writer {
counter: 0,
trap_output: Vec::new(),
global_keys: std::collections::HashMap::new(),
+ location_labels: std::collections::HashMap::new(),
}
}
@@ -50,6 +61,17 @@ impl Writer {
(label, true)
}
+ /// Gets the label for the given location. The first call for a given location will define it as
+ /// a fresh (star) ID.
+ pub fn location_label(&mut self, loc: Location) -> (Label, bool) {
+ if let Some(label) = self.location_labels.get(&loc) {
+ return (*label, false);
+ }
+ let label = self.fresh_id();
+ self.location_labels.insert(loc, label);
+ (label, true)
+ }
+
pub fn add_tuple(&mut self, table_name: &str, args: Vec) {
self.trap_output
.push(Entry::GenericTuple(table_name.to_owned(), args))
From 71852868acda81fb9e321924a0d7d7a2880b4304 Mon Sep 17 00:00:00 2001
From: Joe Farebrother
Date: Fri, 2 Feb 2024 17:19:20 +0000
Subject: [PATCH 049/378] Add case for androidx.biometric api
---
.../java/security/AndroidLocalAuthQuery.qll | 2 +
.../query-tests/security/CWE-287/Test2.java | 47 +++++++++++
.../identity/PresentationSession.java | 9 +++
.../androidx/biometric/BiometricPrompt.java | 79 +++++++++++++++++++
4 files changed, 137 insertions(+)
create mode 100644 java/ql/test/query-tests/security/CWE-287/Test2.java
create mode 100644 java/ql/test/stubs/google-android-9.0.0/android/security/identity/PresentationSession.java
create mode 100644 java/ql/test/stubs/google-android-9.0.0/androidx/biometric/BiometricPrompt.java
diff --git a/java/ql/lib/semmle/code/java/security/AndroidLocalAuthQuery.qll b/java/ql/lib/semmle/code/java/security/AndroidLocalAuthQuery.qll
index 8c052fc58ee..46b391559f1 100644
--- a/java/ql/lib/semmle/code/java/security/AndroidLocalAuthQuery.qll
+++ b/java/ql/lib/semmle/code/java/security/AndroidLocalAuthQuery.qll
@@ -9,6 +9,8 @@ private class AuthenticationCallbackClass extends Class {
"FingerprintManager$AuthenticationCallback")
or
this.hasQualifiedName("android.hardware.biometrics", "BiometricPrompt$AuthenticationCallback")
+ or
+ this.hasQualifiedName("androidx.biometric", "BiometricPrompt$AuthenticationCallback")
}
}
diff --git a/java/ql/test/query-tests/security/CWE-287/Test2.java b/java/ql/test/query-tests/security/CWE-287/Test2.java
new file mode 100644
index 00000000000..10308a2f2d3
--- /dev/null
+++ b/java/ql/test/query-tests/security/CWE-287/Test2.java
@@ -0,0 +1,47 @@
+import androidx.biometric.BiometricPrompt;
+
+class TestC {
+ public static void useKey(BiometricPrompt.CryptoObject key) {}
+
+
+ // GOOD: result is used
+ class Test1 extends BiometricPrompt.AuthenticationCallback {
+ @Override
+ public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result) {
+ TestC.useKey(result.getCryptoObject());
+ }
+ }
+
+ // BAD: result is not used
+ class Test2 extends BiometricPrompt.AuthenticationCallback {
+ @Override
+ public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result) { // $insecure-auth
+
+ }
+ }
+
+ // BAD: result is only used in a super call
+ class Test3 extends BiometricPrompt.AuthenticationCallback {
+ @Override
+ public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result) { // $insecure-auth
+ super.onAuthenticationSucceeded(result);
+ }
+ }
+
+ // GOOD: result is used
+ class Test4 extends BiometricPrompt.AuthenticationCallback {
+ @Override
+ public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result) {
+ super.onAuthenticationSucceeded(result);
+ TestC.useKey(result.getCryptoObject());
+ }
+ }
+
+ // GOOD: result is used in a super call to a class other than the base class
+ class Test5 extends Test1 {
+ @Override
+ public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result) {
+ super.onAuthenticationSucceeded(result);
+ }
+ }
+}
\ No newline at end of file
diff --git a/java/ql/test/stubs/google-android-9.0.0/android/security/identity/PresentationSession.java b/java/ql/test/stubs/google-android-9.0.0/android/security/identity/PresentationSession.java
new file mode 100644
index 00000000000..9227f8fe5d3
--- /dev/null
+++ b/java/ql/test/stubs/google-android-9.0.0/android/security/identity/PresentationSession.java
@@ -0,0 +1,9 @@
+// Generated automatically from android.security.identity.PresentationSession for testing purposes
+
+package android.security.identity;
+
+
+public class PresentationSession
+{
+ protected PresentationSession() {}
+}
diff --git a/java/ql/test/stubs/google-android-9.0.0/androidx/biometric/BiometricPrompt.java b/java/ql/test/stubs/google-android-9.0.0/androidx/biometric/BiometricPrompt.java
new file mode 100644
index 00000000000..16bf2e661ee
--- /dev/null
+++ b/java/ql/test/stubs/google-android-9.0.0/androidx/biometric/BiometricPrompt.java
@@ -0,0 +1,79 @@
+// Generated automatically from androidx.biometric.BiometricPrompt for testing purposes
+
+package androidx.biometric;
+
+import android.security.identity.IdentityCredential;
+import android.security.identity.PresentationSession;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentActivity;
+import java.security.Signature;
+import java.util.concurrent.Executor;
+import javax.crypto.Cipher;
+import javax.crypto.Mac;
+
+public class BiometricPrompt
+{
+ protected BiometricPrompt() {}
+ abstract static public class AuthenticationCallback
+ {
+ public AuthenticationCallback(){}
+ public void onAuthenticationError(int p0, CharSequence p1){}
+ public void onAuthenticationFailed(){}
+ public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult p0){}
+ }
+ public BiometricPrompt(Fragment p0, BiometricPrompt.AuthenticationCallback p1){}
+ public BiometricPrompt(Fragment p0, Executor p1, BiometricPrompt.AuthenticationCallback p2){}
+ public BiometricPrompt(FragmentActivity p0, BiometricPrompt.AuthenticationCallback p1){}
+ public BiometricPrompt(FragmentActivity p0, Executor p1, BiometricPrompt.AuthenticationCallback p2){}
+ public static int AUTHENTICATION_RESULT_TYPE_BIOMETRIC = 0;
+ public static int AUTHENTICATION_RESULT_TYPE_DEVICE_CREDENTIAL = 0;
+ public static int AUTHENTICATION_RESULT_TYPE_UNKNOWN = 0;
+ public static int ERROR_CANCELED = 0;
+ public static int ERROR_HW_NOT_PRESENT = 0;
+ public static int ERROR_HW_UNAVAILABLE = 0;
+ public static int ERROR_LOCKOUT = 0;
+ public static int ERROR_LOCKOUT_PERMANENT = 0;
+ public static int ERROR_NEGATIVE_BUTTON = 0;
+ public static int ERROR_NO_BIOMETRICS = 0;
+ public static int ERROR_NO_DEVICE_CREDENTIAL = 0;
+ public static int ERROR_NO_SPACE = 0;
+ public static int ERROR_SECURITY_UPDATE_REQUIRED = 0;
+ public static int ERROR_TIMEOUT = 0;
+ public static int ERROR_UNABLE_TO_PROCESS = 0;
+ public static int ERROR_USER_CANCELED = 0;
+ public static int ERROR_VENDOR = 0;
+ public void authenticate(BiometricPrompt.PromptInfo p0){}
+ public void authenticate(BiometricPrompt.PromptInfo p0, BiometricPrompt.CryptoObject p1){}
+ public void cancelAuthentication(){}
+ static public class AuthenticationResult
+ {
+ protected AuthenticationResult() {}
+ public BiometricPrompt.CryptoObject getCryptoObject(){ return null; }
+ public int getAuthenticationType(){ return 0; }
+ }
+ static public class CryptoObject
+ {
+ protected CryptoObject() {}
+ public Cipher getCipher(){ return null; }
+ public CryptoObject(Cipher p0){}
+ public CryptoObject(IdentityCredential p0){}
+ public CryptoObject(Mac p0){}
+ public CryptoObject(PresentationSession p0){}
+ public CryptoObject(Signature p0){}
+ public IdentityCredential getIdentityCredential(){ return null; }
+ public Mac getMac(){ return null; }
+ public PresentationSession getPresentationSession(){ return null; }
+ public Signature getSignature(){ return null; }
+ }
+ static public class PromptInfo
+ {
+ protected PromptInfo() {}
+ public CharSequence getDescription(){ return null; }
+ public CharSequence getNegativeButtonText(){ return null; }
+ public CharSequence getSubtitle(){ return null; }
+ public CharSequence getTitle(){ return null; }
+ public boolean isConfirmationRequired(){ return false; }
+ public boolean isDeviceCredentialAllowed(){ return false; }
+ public int getAllowedAuthenticators(){ return 0; }
+ }
+}
From 5022adba562db5543b5d8de8fea512e7d2020029 Mon Sep 17 00:00:00 2001
From: Joe Farebrother
Date: Fri, 2 Feb 2024 17:26:00 +0000
Subject: [PATCH 050/378] Fixes to qhelp example
---
.../CWE/CWE-287/AndroidInsecureLocalAuthenticationGood.java | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/java/ql/src/Security/CWE/CWE-287/AndroidInsecureLocalAuthenticationGood.java b/java/ql/src/Security/CWE/CWE-287/AndroidInsecureLocalAuthenticationGood.java
index 0f41b31a292..2ffcbbb6e26 100644
--- a/java/ql/src/Security/CWE/CWE-287/AndroidInsecureLocalAuthenticationGood.java
+++ b/java/ql/src/Security/CWE/CWE-287/AndroidInsecureLocalAuthenticationGood.java
@@ -26,16 +26,16 @@ private Cipher getCipher() {
+ KeyProperties.ENCRYPTION_PADDING_PKCS7);
}
-public prompt() {
+public prompt(byte[] encryptedData) {
Cipher cipher = getCipher();
SecretKey secretKey = getSecretKey();
cipher.init(Cipher.DECRYPT_MODE, secretKey);
biometricPrompt.authenticate(
- new BiometricPrompt.CryptoObject(cipher);
+ new BiometricPrompt.CryptoObject(cipher),
cancellationSignal,
executor,
- new BiometricPrompt.AuthenticationCallback {
+ new BiometricPrompt.AuthenticationCallback() {
@Override
// GOOD: This authentication callback uses the result to decrypt some data.
public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result) {
From 596f48ca951f54c5e3eda6b7793450e04dc2cf98 Mon Sep 17 00:00:00 2001
From: Joe Farebrother
Date: Fri, 2 Feb 2024 17:35:07 +0000
Subject: [PATCH 051/378] Add change note
---
.../change-notes/2024-02-02-android-insecure-local-auth.md | 5 +++++
1 file changed, 5 insertions(+)
create mode 100644 java/ql/src/change-notes/2024-02-02-android-insecure-local-auth.md
diff --git a/java/ql/src/change-notes/2024-02-02-android-insecure-local-auth.md b/java/ql/src/change-notes/2024-02-02-android-insecure-local-auth.md
new file mode 100644
index 00000000000..dc7ebcaade3
--- /dev/null
+++ b/java/ql/src/change-notes/2024-02-02-android-insecure-local-auth.md
@@ -0,0 +1,5 @@
+
+---
+category: newQuery
+---
+* Added a new query `java/android/insecure-local-authentication` for finding uses of biometric authentication APIs that do not make use of a `KeyStore`-backed key and thus may be bypassed.
\ No newline at end of file
From b8dc6338646935c32f6a69aeb573ccce0acb8b9b Mon Sep 17 00:00:00 2001
From: erik-krogh
Date: Mon, 5 Feb 2024 11:16:16 +0100
Subject: [PATCH 052/378] add cs/path-injection as markdown to make nicer diffs
---
.../Security Features/CWE-022/TaintedPath.md | 50 +++++++++++++++++++
1 file changed, 50 insertions(+)
create mode 100644 csharp/ql/src/Security Features/CWE-022/TaintedPath.md
diff --git a/csharp/ql/src/Security Features/CWE-022/TaintedPath.md b/csharp/ql/src/Security Features/CWE-022/TaintedPath.md
new file mode 100644
index 00000000000..ddd80d92051
--- /dev/null
+++ b/csharp/ql/src/Security Features/CWE-022/TaintedPath.md
@@ -0,0 +1,50 @@
+# Uncontrolled data used in path expression
+Accessing paths controlled by users can allow an attacker to access unexpected resources. This can result in sensitive information being revealed or deleted, or an attacker being able to influence behavior by modifying unexpected files.
+
+Paths that are naively constructed from data controlled by a user may contain unexpected special characters, such as "..". Such a path may potentially point to any directory on the file system.
+
+
+## Recommendation
+Validate user input before using it to construct a file path. Ideally, follow these rules:
+
+* Do not allow more than a single "." character.
+* Do not allow directory separators such as "/" or "\\" (depending on the file system).
+* Do not rely on simply replacing problematic sequences such as "../". For example, after applying this filter to ".../...//" the resulting string would still be "../".
+* Use a whitelist of known good patterns.
+* Sanitize potentially tainted paths using `HttpRequest.MapPath`.
+
+## Example
+In the first example, a file name is read from a `HttpRequest` and then used to access a file. However, a malicious user could enter a file name which is an absolute path - for example, "/etc/passwd". In the second example, it appears that the user is restricted to opening a file within the "user" home directory. However, a malicious user could enter a filename which contains special characters. For example, the string "../../etc/passwd" will result in the code reading the file located at "/home/\[user\]/../../etc/passwd", which is the system's password file. This file would then be sent back to the user, giving them access to all the system's passwords.
+
+
+```csharp
+using System;
+using System.IO;
+using System.Web;
+
+public class TaintedPathHandler : IHttpHandler
+{
+ public void ProcessRequest(HttpContext ctx)
+ {
+ String path = ctx.Request.QueryString["path"];
+ // BAD: This could read any file on the filesystem.
+ ctx.Response.Write(File.ReadAllText(path));
+
+ // BAD: This could still read any file on the filesystem.
+ ctx.Response.Write(File.ReadAllText("/home/user/" + path));
+
+ // GOOD: MapPath ensures the path is safe to read from.
+ string safePath = ctx.Request.MapPath(path, ctx.Request.ApplicationPath, false);
+ ctx.Response.Write(File.ReadAllText(safePath));
+ }
+}
+
+```
+
+## References
+* OWASP: [Path Traversal](https://owasp.org/www-community/attacks/Path_Traversal).
+* Common Weakness Enumeration: [CWE-22](https://cwe.mitre.org/data/definitions/22.html).
+* Common Weakness Enumeration: [CWE-23](https://cwe.mitre.org/data/definitions/23.html).
+* Common Weakness Enumeration: [CWE-36](https://cwe.mitre.org/data/definitions/36.html).
+* Common Weakness Enumeration: [CWE-73](https://cwe.mitre.org/data/definitions/73.html).
+* Common Weakness Enumeration: [CWE-99](https://cwe.mitre.org/data/definitions/99.html).
From 9dfac3a4ccf14fb304ca8b13243a9dc8dec07174 Mon Sep 17 00:00:00 2001
From: erik-krogh
Date: Mon, 5 Feb 2024 11:20:24 +0100
Subject: [PATCH 053/378] move qhelp samples to an `examples` folder
---
csharp/ql/src/Security Features/CWE-022/TaintedPath.qhelp | 2 +-
csharp/ql/src/Security Features/CWE-022/ZipSlip.qhelp | 4 ++--
.../Security Features/CWE-022/{ => examples}/TaintedPath.cs | 0
.../Security Features/CWE-022/{ => examples}/ZipSlipBad.cs | 0
.../Security Features/CWE-022/{ => examples}/ZipSlipGood.cs | 0
5 files changed, 3 insertions(+), 3 deletions(-)
rename csharp/ql/src/Security Features/CWE-022/{ => examples}/TaintedPath.cs (100%)
rename csharp/ql/src/Security Features/CWE-022/{ => examples}/ZipSlipBad.cs (100%)
rename csharp/ql/src/Security Features/CWE-022/{ => examples}/ZipSlipGood.cs (100%)
diff --git a/csharp/ql/src/Security Features/CWE-022/TaintedPath.qhelp b/csharp/ql/src/Security Features/CWE-022/TaintedPath.qhelp
index e838d8c56a4..3ff4e5447cd 100644
--- a/csharp/ql/src/Security Features/CWE-022/TaintedPath.qhelp
+++ b/csharp/ql/src/Security Features/CWE-022/TaintedPath.qhelp
@@ -34,7 +34,7 @@ enter a filename which contains special characters. For example, the string "../
reading the file located at "/home/[user]/../../etc/passwd", which is the system's password file. This file would then be
sent back to the user, giving them access to all the system's passwords.
-
+
diff --git a/csharp/ql/src/Security Features/CWE-022/ZipSlip.qhelp b/csharp/ql/src/Security Features/CWE-022/ZipSlip.qhelp
index a1f39d27b8c..d75ababa6a8 100644
--- a/csharp/ql/src/Security Features/CWE-022/ZipSlip.qhelp
+++ b/csharp/ql/src/Security Features/CWE-022/ZipSlip.qhelp
@@ -50,7 +50,7 @@ the result is within the destination directory. If provided with a zip file cont
path like ..\sneaky-file, then this file would be written outside the destination
directory.
-
+
To fix this vulnerability, we need to make three changes. Firstly, we need to resolve any
directory traversal or other special characters in the path by using Path.GetFullPath.
@@ -59,7 +59,7 @@ Secondly, we need to identify the destination output directory, again using
the resolved output starts with the resolved destination directory, and throw an exception if this
is not the case.
-
+
diff --git a/csharp/ql/src/Security Features/CWE-022/TaintedPath.cs b/csharp/ql/src/Security Features/CWE-022/examples/TaintedPath.cs
similarity index 100%
rename from csharp/ql/src/Security Features/CWE-022/TaintedPath.cs
rename to csharp/ql/src/Security Features/CWE-022/examples/TaintedPath.cs
diff --git a/csharp/ql/src/Security Features/CWE-022/ZipSlipBad.cs b/csharp/ql/src/Security Features/CWE-022/examples/ZipSlipBad.cs
similarity index 100%
rename from csharp/ql/src/Security Features/CWE-022/ZipSlipBad.cs
rename to csharp/ql/src/Security Features/CWE-022/examples/ZipSlipBad.cs
diff --git a/csharp/ql/src/Security Features/CWE-022/ZipSlipGood.cs b/csharp/ql/src/Security Features/CWE-022/examples/ZipSlipGood.cs
similarity index 100%
rename from csharp/ql/src/Security Features/CWE-022/ZipSlipGood.cs
rename to csharp/ql/src/Security Features/CWE-022/examples/ZipSlipGood.cs
From 6748f6e5c7d85d9610573d53fb46a56109eb7acc Mon Sep 17 00:00:00 2001
From: Harry Maclean
Date: Thu, 18 Jan 2024 09:44:23 +0000
Subject: [PATCH 054/378] Ruby: Add docs for MaD
---
.../customizing-library-models-for-ruby.rst | 406 ++++++++++++++++++
1 file changed, 406 insertions(+)
create mode 100644 docs/codeql/codeql-language-guides/customizing-library-models-for-ruby.rst
diff --git a/docs/codeql/codeql-language-guides/customizing-library-models-for-ruby.rst b/docs/codeql/codeql-language-guides/customizing-library-models-for-ruby.rst
new file mode 100644
index 00000000000..d073eaa4d88
--- /dev/null
+++ b/docs/codeql/codeql-language-guides/customizing-library-models-for-ruby.rst
@@ -0,0 +1,406 @@
+.. _customizing-library-models-for-ruby:
+
+:orphan:
+:nosearch:
+
+Customizing Library Models for Ruby
+===================================
+
+.. include:: ../reusables/beta-note-customizing-library-models.rst
+
+Ruby analysis can be customized by adding library models in data extension files.
+
+A data extension for Ruby is a YAML file of the form:
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/ruby-all
+ extensible:
+ data:
+ -
+ -
+ - ...
+
+The CodeQL library for JavaScript exposes the following extensible predicates:
+
+- **sourceModel**\(type, path, kind)
+- **sinkModel**\(type, path, kind)
+- **typeModel**\(type1, type2, path)
+- **summaryModel**\(type, path, input, output, kind)
+
+See the `CLI documentation for how to load and use data extensions in a CodeQL evaluation run `__ (internal access required).
+
+We'll explain how to use these using a few examples, and provide some reference material at the end of this article.
+
+Example: Taint sink in the 'tty-command' gem
+------------------------------------------
+
+In this example, we'll show how to add the following argument, passed to **tty-command**, as a command-line injection sink:
+
+.. code-block:: ruby
+
+ tty = TTY::Command.new
+ tty.run(cmd) # <-- add 'cmd' as a taint sink
+
+For this example, you can use the following data extension:
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/ruby-all
+ extensible: sinkModel
+ data:
+ - ["TTY::Command", "Method[run].Argument[0]", "command-injection"]
+
+
+- Since we're adding a new sink, we add a tuple to the **sinkModel** extensible predicate.
+- The first column, **"TTY::Command"**, identifies a set of values from which to begin the search for the sink.
+ The string **"TTY::Command""** means we start at the places where the codebase constructs instances of the class **TTY::Command**.
+- The second column is an access path that is evaluated from left to right, starting at the values that were identified by the first column.
+
+ - **Method[run]** selects calls to the **run** method of the **TTY::Command** class.
+ - **Argument[0]** selects the first argument to calls to that member.
+
+- **command-injection** indicates that this is considered a sink for the command injection query.
+
+Example: Taint sources from `sinatra` block parameters
+------------------------------------------------------
+
+In this example, we'll show how the `x` parameter below could be marked as a remote flow source:
+
+.. code-block:: ruby
+
+ class MyApp < Sinatra::Base
+ get '/' do |x| # <-- add 'x' as a taint source
+ # ...
+ end
+ end
+
+For this example you could use the following data extension:
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/ruby-all
+ extensible: sourceModel
+ data:
+ - [
+ "Sinatra::Base!",
+ "Method[get].Argument[block].Parameter[0]",
+ "remote",
+ ]
+
+- Since we're adding a new taint source, we add a tuple to the **sourceModel** extensible predicate.
+- The first column, **"Sinatra::Base!"**, begins the search at references to the **Sinatra::Base** class.
+ The **!** suffix indicates that we want to search for references to the class itself, rather than instances of the class.
+- **Method[get]** selects calls to the **get** method of the **Sinatra::Base** class.
+- **Argument[block]** selects the block argument to the **get** method call.
+- **Parameter[0]** selects the first parameter of the block argument (the parameter named **x**).
+- Finally, the kind **remote** indicates that this is considered a source of remote flow.
+
+Example: Using types to add MySQL injection sinks
+-------------------------------------------------
+
+In this example, we'll show how to add the following SQL injection sink:
+
+.. code-block:: ruby
+
+ def submit(q)
+ client = Mysql2::Client.new
+ client.query(q) # <-- add 'q' as a SQL injection sink
+ end
+
+We can recognize this using the following extension:
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/ruby-all
+ extensible: sinkModel
+ data:
+ - ["Mysql2::Client", "Method[query].Argument[0]", "sql-injection"]
+
+- The first column, **"Mysql2::Client"**, begins the search at any instance of the **Mysql2::Client** class.
+- **Method[query]** selects any call to the **query** method on that instance.
+- **Argument[0]** selects the first argument to the method call.
+- **sql-injection** indicates that this is considered a sink for the SQL injection query.
+
+Continued example: Using type models
+------------------------------------
+
+Consider this variation on the previous example, the mysql2 EventMachine API is used.
+The client is obtained via a call to **Mysql2::EM::Client.new**.
+
+.. code-block:: ruby
+
+ def submit(client, q)
+ client = Mysql2::EM::Client.new
+ client.query(q)
+ end
+
+So far we have only one model for **Mysql2::Client**, but in the real world we
+may have many models for the various methods available. Because **Mysql2::EM::Client** is a subclass of **Mysql2::Client**, it inherits all of the same methods.
+Instead of updating all our models to include both classes, we can add a type
+model to indicate that **Mysql2::EM::Client** is a subclass of **Mysql2::Client**:
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/ruby-all
+ extensible: typeModel
+ data:
+ - ["Mysql2::Client", "Mysql2::EM::Client", ""]
+
+Example: Adding flow through 'URI.decode_uri_component'
+-------------------------------------------------------
+
+In this example, we'll show how to add flow through calls to `URI.decode_uri_component`:
+
+.. code-block:: ruby
+
+ y = URI.decode_uri_component(x); # add taint flow from 'x' to 'y'
+
+We can model this using the following data extension:
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/ruby-all
+ extensible: summaryModel
+ data:
+ - [
+ "URI",
+ "Method[decode_uri_component]",
+ "Argument[0]",
+ "ReturnValue",
+ "taint",
+ ]
+
+
+- Since we're adding flow through a method call, we add a tuple to the **summaryModel** extensible predicate.
+- The first column, **"URI!"**, begins the search for relevant calls at references to the **URI** class.
+- The **!** suffix indicates that we are looking for the class itself, rather than instances of the class.
+- The second column, **Member[decode_uri_component]**, is a path leading to the method calls we wish to model.
+ In this case, we select references to the **decode_uri_component** method from the **URI** class.
+- The third column, **Argument[0]**, indicates the input of the flow. In this case, the first argument to the method call.
+- The fourth column, **ReturnValue**, indicates the output of the flow. In this case, the return value of the method call.
+- The last column, **taint**, indicates the kind of flow to add. The value **taint** means the output is not necessarily equal
+ to the input, but was derived from the input in a taint-preserving way.
+
+Example: Adding flow through 'File#each'
+----------------------------------------
+
+In this example, we'll show how to add flow through calls to **File#each** from the standard library, which iterates over the lines of a file:
+
+.. code-block:: ruby
+
+ f = File.new("example.txt")
+ f.each { |line| ... } # add taint flow from `f` to `line`
+
+We can model this using the following data extension:
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/ruby-all
+ extensible: summaryModel
+ data:
+ - [
+ "File",
+ "Method[each]",
+ "Argument[self]",
+ "Argument[block].Parameter[0]",
+ "taint",
+ ]
+
+
+- Since we're adding flow through a method call, we add a tuple to the **summaryModel** extensible predicate.
+- The first column, **"File"**, begins the search for relevant calls at places where the **File** class is used.
+- The second column, **Method[each]**, selects references to the **each** method on the **File** class.
+- The third column specifies the input of the flow. **Argument[self]** selects the **self** argument of **each**, which is the **File** instance being iterated over.
+
+- The fourth column specifies the output of the flow:
+
+ - **Argument[block]** selects the block argument of **each** (the block which is executed for each line in the file).
+ - **Parameter[0]** selects the first parameter of the block (the parameter named **line**).
+
+- The last column, **taint**, indicates the kind of flow to add.
+
+Reference material
+------------------
+
+The following sections provide reference material for extensible predicates, access paths, types, and kinds.
+
+Extensible predicates
+---------------------
+
+sourceModel(type, path, kind)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Adds a new taint source. Most taint-tracking queries will use the new source.
+
+- **type**: Name of a type from which to evaluate **path**.
+- **path**: Access path leading to the source.
+- **kind**: Kind of source to add. Currently only **remote** is used.
+
+Example:
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/ruby-all
+ extensible: sourceModel
+ data:
+ - ["User", "Method[name]", "remote"]
+
+sinkModel(type, path, kind)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Adds a new taint sink. Sinks are query-specific and will typically affect one or two queries.
+
+- **type**: Name of a type from which to evaluate **path**.
+- **path**: Access path leading to the sink.
+- **kind**: Kind of sink to add. See the section on sink kinds for a list of supported kinds.
+
+Example:
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/ruby-all
+ extensible: sinkModel
+ data:
+ - ["ExecuteShell", "Method[run].Argument[0]", "command-injection"]
+
+summaryModel(type, path, input, output, kind)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Adds flow through a method call.
+
+- **type**: Name of a type from which to evaluate **path**.
+- **path**: Access path leading to a method call.
+- **input**: Path relative to the method call that leads to input of the flow.
+- **output**: Path relative to the method call leading to the output of the flow.
+- **kind**: Kind of summary to add. Can be **taint** for taint-propagating flow, or **value** for value-preserving flow.
+
+Example:
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/ruby-all
+ extensible: summaryModel
+ data:
+ - [
+ "URI",
+ "Method[decode_uri_component]",
+ "Argument[0]",
+ "ReturnValue",
+ "taint",
+ ]
+
+typeModel(type1, type2, path)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Adds a new definition of a type.
+
+- **type1**: Name of the type to define.
+- **type2**: Name of the type from which to evaluate **path**.
+- **path**: Access path leading from **type2** to **type1**.
+
+Example:
+
+.. code-block:: yaml
+
+ extensions:
+ - addsTo:
+ pack: codeql/ruby-all
+ extensible: typeModel
+ data:
+ - [
+ "Mysql2::Client",
+ "MyDbWrapper",
+ "Method[getConnection].ReturnValue",
+ ]
+
+Types
+-----
+
+A type is a string that identifies a set of values.
+In each of the extensible predicates mentioned in previous section, the first column is always the name of a type.
+A type can be defined by adding **typeModel** tuples for that type.
+
+Access paths
+------------
+
+The **path**, **input**, and **output** columns consist of a **.**-separated list of components, which is evaluated from left to right,
+with each step selecting a new set of values derived from the previous set of values.
+
+The following components are supported:
+
+- **Argument[**\ `number`\ **]** selects the argument at the given index.
+- **Argument[**\ `string`:\ **]** selects the keyword argument with the given name.
+- **Argument[self]** selects the receiver of a method call.
+- **Argument[block]** selects the block argument.
+- **Argument[any]** selects any argument, except self or block arguments.
+- **Argument[any-named]** selects any keyword argument.
+- **Argument[hash-splat]** selects a special argument representing all keyword arguments passed in the method call.
+- **Parameter[**\ `number`\ **]** selects the argument at the given index.
+- **Parameter[**\ `string`:\ **]** selects the keyword argument with the given name.
+- **Parameter[self]** selects the **self** parameter of a method.
+- **Parameter[block]** selects the block parameter.
+- **Parameter[any]** selects any parameter, except self or block parameters.
+- **Parameter[any-named]** selects any keyword parameter.
+- **Parameter[hash-splat]** selects the hash splat parameter, often written as **\*\*kwargs**.
+- **ReturnValue** selects the return value of a call.
+- **Method[**\ `name`\ **]** selects a call to the method with the given name.
+- **Element[any]** selects any element of an array or hash.
+- **Element[**\ `number`\ **]** selects an array element at the given index.
+- **Element[**\ `string`\ **]** selects a hash element at the given key.
+- **Field[@**\ `string`\ **]** selects an instance variable with the given name.
+- **Fuzzy** selects all values that are derived from the current value through a combination of the other operations described in this list.
+ For example, this can be used to find all values that appear to originate from a particular class. This can be useful for finding method calls
+ from a known class, but where the receiver type is not known or is difficult to model.
+
+Additional notes about the syntax of operands:
+
+- Multiple operands may be given to a single component, as a shorthand for the union of the operands. For example, **Method[foo,bar]** matches the union of **Method[foo]** and **Method[bar]**.
+- Numeric operands to **Argument**, **Parameter**, and **Element** may be given as a lower bound. For example, **Argument[1..]** matches all arguments except 0.
+
+Kinds
+-----
+
+Source kinds
+~~~~~~~~~~~~
+
+- **remote**: A generic source of remote flow. Most taint-tracking queries will use such a source. Currently this is the only supported source kind.
+
+Sink kinds
+~~~~~~~~~~
+
+Unlike sources, sinks tend to be highly query-specific, rarely affecting more than one or two queries.
+Not every query supports customizable sinks. If the following sinks are not suitable for your use case, you should add a new query.
+
+- **code-injection**: A sink that can be used to inject code, such as in calls to **eval**.
+- **command-injection**: A sink that can be used to inject shell commands, such as in calls to **child_process.spawn**.
+- **path-injection**: A sink that can be used for path injection in a file system access, such as in calls to **fs.readFile**.
+- **sql-injection**: A sink that can be used for SQL injection, such as in a MySQL **query** call.
+- **url-redirection**: A sink that can be used to redirect the user to a malicious URL.
+- **log-injection**: A sink that can be used for log injection, such as in a **console.log** call.
+
+Summary kinds
+~~~~~~~~~~~~~
+
+- **taint**: A summary that propagates taint. This means the output is not necessarily equal to the input, but it was derived from the input in an unrestrictive way. An attacker who controls the input will have significant control over the output as well.
+- **value**: A summary that preserves the value of the input or creates a copy of the input such that all of its object properties are preserved.
From 03ab3c1a5d8e533cd0288eb28cdbe85b24e4293e Mon Sep 17 00:00:00 2001
From: Harry Maclean
Date: Mon, 5 Feb 2024 11:15:53 +0000
Subject: [PATCH 055/378] Ruby: Fix title underline
---
.../customizing-library-models-for-ruby.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/codeql/codeql-language-guides/customizing-library-models-for-ruby.rst b/docs/codeql/codeql-language-guides/customizing-library-models-for-ruby.rst
index d073eaa4d88..6bc80472596 100644
--- a/docs/codeql/codeql-language-guides/customizing-library-models-for-ruby.rst
+++ b/docs/codeql/codeql-language-guides/customizing-library-models-for-ruby.rst
@@ -35,7 +35,7 @@ See the `CLI documentation for how to load and use data extensions in a CodeQL e
We'll explain how to use these using a few examples, and provide some reference material at the end of this article.
Example: Taint sink in the 'tty-command' gem
-------------------------------------------
+--------------------------------------------
In this example, we'll show how to add the following argument, passed to **tty-command**, as a command-line injection sink:
From 8160291be1e4ca30795b1a61f3e22e3b49cbe370 Mon Sep 17 00:00:00 2001
From: erik-krogh
Date: Mon, 5 Feb 2024 13:08:21 +0100
Subject: [PATCH 056/378] copy (and adjust) the path-injection QHelp from Java
to C#
---
.../CWE-022/TaintedPath.qhelp | 53 +++++++++++++------
.../CWE-022/examples/TaintedPath.cs | 11 +---
.../CWE-022/examples/TaintedPathGoodFolder.cs | 24 +++++++++
.../examples/TaintedPathGoodNormalize.cs | 20 +++++++
4 files changed, 82 insertions(+), 26 deletions(-)
create mode 100644 csharp/ql/src/Security Features/CWE-022/examples/TaintedPathGoodFolder.cs
create mode 100644 csharp/ql/src/Security Features/CWE-022/examples/TaintedPathGoodNormalize.cs
diff --git a/csharp/ql/src/Security Features/CWE-022/TaintedPath.qhelp b/csharp/ql/src/Security Features/CWE-022/TaintedPath.qhelp
index 3ff4e5447cd..bf3132a9719 100644
--- a/csharp/ql/src/Security Features/CWE-022/TaintedPath.qhelp
+++ b/csharp/ql/src/Security Features/CWE-022/TaintedPath.qhelp
@@ -7,35 +7,54 @@
can result in sensitive information being revealed or deleted, or an attacker being able to influence
behavior by modifying unexpected files.
-
Paths that are naively constructed from data controlled by a user may contain unexpected special characters,
-such as "..". Such a path may potentially point to any directory on the file system.
+
Paths that are naively constructed from data controlled by a user may be absolute paths, or may contain
+unexpected special characters such as "..". Such a path could point anywhere on the file system.
-
Validate user input before using it to construct a file path. Ideally, follow these rules:
+
Validate user input before using it to construct a file path.
-
-
Do not allow more than a single "." character.
-
Do not allow directory separators such as "/" or "\" (depending on the file system).
-
Do not rely on simply replacing problematic sequences such as "../". For example, after applying this filter to
-".../...//" the resulting string would still be "../".
-
Use a whitelist of known good patterns.
-
Sanitize potentially tainted paths using HttpRequest.MapPath.
-
+
Common validation methods include checking that the normalized path is relative and does not contain
+any ".." components, or checking that the path is contained within a safe folder. The method you should use depends
+on how the path is used in the application, and whether the path should be a single path component.
+
+
+
If the path should be a single path component (such as a file name), you can check for the existence
+of any path separators ("/" or "\"), or ".." sequences in the input, and reject the input if any are found.
+
+
+
+Note that removing "../" sequences is not sufficient, since the input could still contain a path separator
+followed by "..". For example, the input ".../...//" would still result in the string "../" if only "../" sequences
+are removed.
+
+
+
Finally, the simplest (but most restrictive) option is to use an allow list of safe patterns and make sure that
+the user input matches one of these patterns.
-
In the first example, a file name is read from a HttpRequest and then used to access a file. However, a
-malicious user could enter a file name which is an absolute path - for example, "/etc/passwd". In the second example, it
-appears that the user is restricted to opening a file within the "user" home directory. However, a malicious user could
-enter a filename which contains special characters. For example, the string "../../etc/passwd" will result in the code
-reading the file located at "/home/[user]/../../etc/passwd", which is the system's password file. This file would then be
-sent back to the user, giving them access to all the system's passwords.
+
In this example, a user-provided file name is read from a HTTP request and then used to access a file
+and send it back to the user. However, a malicious user could enter a file name anywhere on the file system,
+such as "/etc/passwd" or "../../../etc/passwd".
+
+If the input should only be a file name, you can check that it doesn't contain any path separators or ".." sequences.
+
+
+
+
+
+If the input should be within a specific directory, you can check that the resolved path
+is still contained within that directory.
+
+
+
+
diff --git a/csharp/ql/src/Security Features/CWE-022/examples/TaintedPath.cs b/csharp/ql/src/Security Features/CWE-022/examples/TaintedPath.cs
index ac2add1b9b0..c185267a038 100644
--- a/csharp/ql/src/Security Features/CWE-022/examples/TaintedPath.cs
+++ b/csharp/ql/src/Security Features/CWE-022/examples/TaintedPath.cs
@@ -6,15 +6,8 @@ public class TaintedPathHandler : IHttpHandler
{
public void ProcessRequest(HttpContext ctx)
{
- String path = ctx.Request.QueryString["path"];
+ String filename = ctx.Request.QueryString["path"];
// BAD: This could read any file on the filesystem.
- ctx.Response.Write(File.ReadAllText(path));
-
- // BAD: This could still read any file on the filesystem.
- ctx.Response.Write(File.ReadAllText("/home/user/" + path));
-
- // GOOD: MapPath ensures the path is safe to read from.
- string safePath = ctx.Request.MapPath(path, ctx.Request.ApplicationPath, false);
- ctx.Response.Write(File.ReadAllText(safePath));
+ ctx.Response.Write(File.ReadAllText(filename));
}
}
diff --git a/csharp/ql/src/Security Features/CWE-022/examples/TaintedPathGoodFolder.cs b/csharp/ql/src/Security Features/CWE-022/examples/TaintedPathGoodFolder.cs
new file mode 100644
index 00000000000..33443abb717
--- /dev/null
+++ b/csharp/ql/src/Security Features/CWE-022/examples/TaintedPathGoodFolder.cs
@@ -0,0 +1,24 @@
+using System;
+using System.IO;
+using System.Web;
+
+public class TaintedPathHandler : IHttpHandler
+{
+ public void ProcessRequest(HttpContext ctx)
+ {
+ String filename = ctx.Request.QueryString["path"];
+
+ string publicFolder = Path.GetFullPath("/home/" + user + "/public");
+ string filePath = Path.GetFullPath(Path.Combine(publicFolder, filename));
+
+ // GOOD: ensure that the path stays within the public folder
+ if (!filePath.StartsWith(publicFolder + Path.DirectorySeparatorChar))
+ {
+ ctx.Response.StatusCode = 400;
+ ctx.Response.StatusDescription = "Bad Request";
+ ctx.Response.Write("Invalid path");
+ return;
+ }
+ ctx.Response.Write(File.ReadAllText(filename));
+ }
+}
diff --git a/csharp/ql/src/Security Features/CWE-022/examples/TaintedPathGoodNormalize.cs b/csharp/ql/src/Security Features/CWE-022/examples/TaintedPathGoodNormalize.cs
new file mode 100644
index 00000000000..939ceffff23
--- /dev/null
+++ b/csharp/ql/src/Security Features/CWE-022/examples/TaintedPathGoodNormalize.cs
@@ -0,0 +1,20 @@
+using System;
+using System.IO;
+using System.Web;
+
+public class TaintedPathHandler : IHttpHandler
+{
+ public void ProcessRequest(HttpContext ctx)
+ {
+ String filename = ctx.Request.QueryString["path"];
+ // GOOD: ensure that the filename has no path separators or parent directory references
+ if (filename.Contains("..") || filename.Contains("/") || filename.Contains("\\"))
+ {
+ ctx.Response.StatusCode = 400;
+ ctx.Response.StatusDescription = "Bad Request";
+ ctx.Response.Write("Invalid path");
+ return;
+ }
+ ctx.Response.Write(File.ReadAllText(filename));
+ }
+}
From a240618ae490a88225b190f1f728ff476a614603 Mon Sep 17 00:00:00 2001
From: erik-krogh
Date: Mon, 5 Feb 2024 13:09:02 +0100
Subject: [PATCH 057/378] generate the new rendered markdown
---
.../Security Features/CWE-022/TaintedPath.md | 82 +++++++++++++++----
1 file changed, 67 insertions(+), 15 deletions(-)
diff --git a/csharp/ql/src/Security Features/CWE-022/TaintedPath.md b/csharp/ql/src/Security Features/CWE-022/TaintedPath.md
index ddd80d92051..c6204c2914e 100644
--- a/csharp/ql/src/Security Features/CWE-022/TaintedPath.md
+++ b/csharp/ql/src/Security Features/CWE-022/TaintedPath.md
@@ -1,20 +1,23 @@
# Uncontrolled data used in path expression
Accessing paths controlled by users can allow an attacker to access unexpected resources. This can result in sensitive information being revealed or deleted, or an attacker being able to influence behavior by modifying unexpected files.
-Paths that are naively constructed from data controlled by a user may contain unexpected special characters, such as "..". Such a path may potentially point to any directory on the file system.
+Paths that are naively constructed from data controlled by a user may be absolute paths, or may contain unexpected special characters such as "..". Such a path could point anywhere on the file system.
## Recommendation
-Validate user input before using it to construct a file path. Ideally, follow these rules:
+Validate user input before using it to construct a file path.
+
+Common validation methods include checking that the normalized path is relative and does not contain any ".." components, or checking that the path is contained within a safe folder. The method you should use depends on how the path is used in the application, and whether the path should be a single path component.
+
+If the path should be a single path component (such as a file name), you can check for the existence of any path separators ("/" or "\\"), or ".." sequences in the input, and reject the input if any are found.
+
+Note that removing "../" sequences is *not* sufficient, since the input could still contain a path separator followed by "..". For example, the input ".../...//" would still result in the string "../" if only "../" sequences are removed.
+
+Finally, the simplest (but most restrictive) option is to use an allow list of safe patterns and make sure that the user input matches one of these patterns.
-* Do not allow more than a single "." character.
-* Do not allow directory separators such as "/" or "\\" (depending on the file system).
-* Do not rely on simply replacing problematic sequences such as "../". For example, after applying this filter to ".../...//" the resulting string would still be "../".
-* Use a whitelist of known good patterns.
-* Sanitize potentially tainted paths using `HttpRequest.MapPath`.
## Example
-In the first example, a file name is read from a `HttpRequest` and then used to access a file. However, a malicious user could enter a file name which is an absolute path - for example, "/etc/passwd". In the second example, it appears that the user is restricted to opening a file within the "user" home directory. However, a malicious user could enter a filename which contains special characters. For example, the string "../../etc/passwd" will result in the code reading the file located at "/home/\[user\]/../../etc/passwd", which is the system's password file. This file would then be sent back to the user, giving them access to all the system's passwords.
+In this example, a user-provided file name is read from a HTTP request and then used to access a file and send it back to the user. However, a malicious user could enter a file name anywhere on the file system, such as "/etc/passwd" or "../../../etc/passwd".
```csharp
@@ -26,16 +29,65 @@ public class TaintedPathHandler : IHttpHandler
{
public void ProcessRequest(HttpContext ctx)
{
- String path = ctx.Request.QueryString["path"];
+ String filename = ctx.Request.QueryString["path"];
// BAD: This could read any file on the filesystem.
- ctx.Response.Write(File.ReadAllText(path));
+ ctx.Response.Write(File.ReadAllText(filename));
+ }
+}
- // BAD: This could still read any file on the filesystem.
- ctx.Response.Write(File.ReadAllText("/home/user/" + path));
+```
+If the input should only be a file name, you can check that it doesn't contain any path separators or ".." sequences.
- // GOOD: MapPath ensures the path is safe to read from.
- string safePath = ctx.Request.MapPath(path, ctx.Request.ApplicationPath, false);
- ctx.Response.Write(File.ReadAllText(safePath));
+
+```csharp
+using System;
+using System.IO;
+using System.Web;
+
+public class TaintedPathHandler : IHttpHandler
+{
+ public void ProcessRequest(HttpContext ctx)
+ {
+ String filename = ctx.Request.QueryString["path"];
+ // GOOD: ensure that the filename has no path separators or parent directory references
+ if (filename.Contains("..") || filename.Contains("/") || filename.Contains("\\"))
+ {
+ ctx.Response.StatusCode = 400;
+ ctx.Response.StatusDescription = "Bad Request";
+ ctx.Response.Write("Invalid path");
+ return;
+ }
+ ctx.Response.Write(File.ReadAllText(filename));
+ }
+}
+
+```
+If the input should be within a specific directory, you can check that the resolved path is still contained within that directory.
+
+
+```csharp
+using System;
+using System.IO;
+using System.Web;
+
+public class TaintedPathHandler : IHttpHandler
+{
+ public void ProcessRequest(HttpContext ctx)
+ {
+ String filename = ctx.Request.QueryString["path"];
+
+ string publicFolder = Path.GetFullPath("/home/" + user + "/public");
+ string filePath = Path.GetFullPath(Path.Combine(publicFolder, filename));
+
+ // GOOD: ensure that the path stays within the public folder
+ if (!filePath.StartsWith(publicFolder + Path.DirectorySeparatorChar))
+ {
+ ctx.Response.StatusCode = 400;
+ ctx.Response.StatusDescription = "Bad Request";
+ ctx.Response.Write("Invalid path");
+ return;
+ }
+ ctx.Response.Write(File.ReadAllText(filename));
}
}
From a6b094cf533075e48aed6107ef7c26600751826e Mon Sep 17 00:00:00 2001
From: erik-krogh
Date: Mon, 5 Feb 2024 13:54:13 +0100
Subject: [PATCH 058/378] delete the rendered markdown again
---
.../Security Features/CWE-022/TaintedPath.md | 102 ------------------
1 file changed, 102 deletions(-)
delete mode 100644 csharp/ql/src/Security Features/CWE-022/TaintedPath.md
diff --git a/csharp/ql/src/Security Features/CWE-022/TaintedPath.md b/csharp/ql/src/Security Features/CWE-022/TaintedPath.md
deleted file mode 100644
index c6204c2914e..00000000000
--- a/csharp/ql/src/Security Features/CWE-022/TaintedPath.md
+++ /dev/null
@@ -1,102 +0,0 @@
-# Uncontrolled data used in path expression
-Accessing paths controlled by users can allow an attacker to access unexpected resources. This can result in sensitive information being revealed or deleted, or an attacker being able to influence behavior by modifying unexpected files.
-
-Paths that are naively constructed from data controlled by a user may be absolute paths, or may contain unexpected special characters such as "..". Such a path could point anywhere on the file system.
-
-
-## Recommendation
-Validate user input before using it to construct a file path.
-
-Common validation methods include checking that the normalized path is relative and does not contain any ".." components, or checking that the path is contained within a safe folder. The method you should use depends on how the path is used in the application, and whether the path should be a single path component.
-
-If the path should be a single path component (such as a file name), you can check for the existence of any path separators ("/" or "\\"), or ".." sequences in the input, and reject the input if any are found.
-
-Note that removing "../" sequences is *not* sufficient, since the input could still contain a path separator followed by "..". For example, the input ".../...//" would still result in the string "../" if only "../" sequences are removed.
-
-Finally, the simplest (but most restrictive) option is to use an allow list of safe patterns and make sure that the user input matches one of these patterns.
-
-
-## Example
-In this example, a user-provided file name is read from a HTTP request and then used to access a file and send it back to the user. However, a malicious user could enter a file name anywhere on the file system, such as "/etc/passwd" or "../../../etc/passwd".
-
-
-```csharp
-using System;
-using System.IO;
-using System.Web;
-
-public class TaintedPathHandler : IHttpHandler
-{
- public void ProcessRequest(HttpContext ctx)
- {
- String filename = ctx.Request.QueryString["path"];
- // BAD: This could read any file on the filesystem.
- ctx.Response.Write(File.ReadAllText(filename));
- }
-}
-
-```
-If the input should only be a file name, you can check that it doesn't contain any path separators or ".." sequences.
-
-
-```csharp
-using System;
-using System.IO;
-using System.Web;
-
-public class TaintedPathHandler : IHttpHandler
-{
- public void ProcessRequest(HttpContext ctx)
- {
- String filename = ctx.Request.QueryString["path"];
- // GOOD: ensure that the filename has no path separators or parent directory references
- if (filename.Contains("..") || filename.Contains("/") || filename.Contains("\\"))
- {
- ctx.Response.StatusCode = 400;
- ctx.Response.StatusDescription = "Bad Request";
- ctx.Response.Write("Invalid path");
- return;
- }
- ctx.Response.Write(File.ReadAllText(filename));
- }
-}
-
-```
-If the input should be within a specific directory, you can check that the resolved path is still contained within that directory.
-
-
-```csharp
-using System;
-using System.IO;
-using System.Web;
-
-public class TaintedPathHandler : IHttpHandler
-{
- public void ProcessRequest(HttpContext ctx)
- {
- String filename = ctx.Request.QueryString["path"];
-
- string publicFolder = Path.GetFullPath("/home/" + user + "/public");
- string filePath = Path.GetFullPath(Path.Combine(publicFolder, filename));
-
- // GOOD: ensure that the path stays within the public folder
- if (!filePath.StartsWith(publicFolder + Path.DirectorySeparatorChar))
- {
- ctx.Response.StatusCode = 400;
- ctx.Response.StatusDescription = "Bad Request";
- ctx.Response.Write("Invalid path");
- return;
- }
- ctx.Response.Write(File.ReadAllText(filename));
- }
-}
-
-```
-
-## References
-* OWASP: [Path Traversal](https://owasp.org/www-community/attacks/Path_Traversal).
-* Common Weakness Enumeration: [CWE-22](https://cwe.mitre.org/data/definitions/22.html).
-* Common Weakness Enumeration: [CWE-23](https://cwe.mitre.org/data/definitions/23.html).
-* Common Weakness Enumeration: [CWE-36](https://cwe.mitre.org/data/definitions/36.html).
-* Common Weakness Enumeration: [CWE-73](https://cwe.mitre.org/data/definitions/73.html).
-* Common Weakness Enumeration: [CWE-99](https://cwe.mitre.org/data/definitions/99.html).
From f792b5842125560303ecc0019017262e0b3e3400 Mon Sep 17 00:00:00 2001
From: Harry Maclean
Date: Mon, 5 Feb 2024 16:45:59 +0000
Subject: [PATCH 059/378] Ruby: Recognise more ActiveRecord connections
---
.../codeql/ruby/frameworks/ActiveRecord.qll | 6 +-
.../active_record/ActiveRecord.expected | 227 +++++++++---------
.../frameworks/active_record/ActiveRecord.rb | 4 +
3 files changed, 125 insertions(+), 112 deletions(-)
diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll
index 843eb4f8d6e..4596c432070 100644
--- a/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll
+++ b/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll
@@ -77,7 +77,11 @@ private predicate isUnlikelyExternalCall(API::MethodAccessNode node) {
}
private API::Node activeRecordConnectionInstance() {
- result = activeRecordBaseClass().getReturn("connection")
+ result =
+ [
+ activeRecordBaseClass().getReturn("connection"),
+ activeRecordBaseClass().getInstance().getReturn("connection")
+ ]
}
/**
diff --git a/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.expected b/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.expected
index d7195d11ad7..b273bddbee6 100644
--- a/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.expected
+++ b/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.expected
@@ -1,7 +1,7 @@
activeRecordModelClasses
| ActiveRecord.rb:1:1:3:3 | UserGroup |
-| ActiveRecord.rb:5:1:15:3 | User |
-| ActiveRecord.rb:17:1:21:3 | Admin |
+| ActiveRecord.rb:5:1:19:3 | User |
+| ActiveRecord.rb:21:1:25:3 | Admin |
| associations.rb:1:1:3:3 | Author |
| associations.rb:5:1:9:3 | Post |
| associations.rb:11:1:13:3 | Tag |
@@ -10,17 +10,20 @@ activeRecordInstances
| ActiveRecord.rb:9:5:9:68 | call to find |
| ActiveRecord.rb:13:5:13:40 | call to find_by |
| ActiveRecord.rb:13:5:13:46 | call to users |
-| ActiveRecord.rb:35:5:35:51 | call to authenticate |
-| ActiveRecord.rb:36:5:36:30 | call to find_by_name |
-| ActiveRecord.rb:55:5:57:7 | if ... |
-| ActiveRecord.rb:55:43:56:40 | then ... |
-| ActiveRecord.rb:56:7:56:40 | call to find_by |
-| ActiveRecord.rb:60:5:60:33 | call to find_by |
-| ActiveRecord.rb:62:5:62:34 | call to find |
-| ActiveRecord.rb:72:5:72:24 | call to create |
-| ActiveRecord.rb:76:5:76:66 | call to create |
-| ActiveRecord.rb:80:5:80:68 | call to create |
-| ActiveRecord.rb:84:5:84:16 | call to create |
+| ActiveRecord.rb:16:3:18:5 | self (exec) |
+| ActiveRecord.rb:16:3:18:5 | self in exec |
+| ActiveRecord.rb:17:5:17:14 | self |
+| ActiveRecord.rb:39:5:39:51 | call to authenticate |
+| ActiveRecord.rb:40:5:40:30 | call to find_by_name |
+| ActiveRecord.rb:59:5:61:7 | if ... |
+| ActiveRecord.rb:59:43:60:40 | then ... |
+| ActiveRecord.rb:60:7:60:40 | call to find_by |
+| ActiveRecord.rb:64:5:64:33 | call to find_by |
+| ActiveRecord.rb:66:5:66:34 | call to find |
+| ActiveRecord.rb:76:5:76:24 | call to create |
+| ActiveRecord.rb:80:5:80:66 | call to create |
+| ActiveRecord.rb:84:5:84:68 | call to create |
+| ActiveRecord.rb:88:5:88:16 | call to create |
| associations.rb:19:1:19:7 | author1 |
| associations.rb:19:1:19:20 | ... = ... |
| associations.rb:19:11:19:20 | call to new |
@@ -105,46 +108,47 @@ activeRecordInstances
| associations.rb:53:1:53:34 | call to find |
activeRecordSqlExecutionRanges
| ActiveRecord.rb:9:33:9:67 | "name='#{...}' and pass='#{...}'" |
-| ActiveRecord.rb:19:16:19:24 | condition |
-| ActiveRecord.rb:28:30:28:44 | ...[...] |
-| ActiveRecord.rb:29:20:29:42 | "id = '#{...}'" |
-| ActiveRecord.rb:30:21:30:45 | call to [] |
-| ActiveRecord.rb:31:16:31:21 | <<-SQL |
-| ActiveRecord.rb:34:20:34:47 | "user.id = '#{...}'" |
-| ActiveRecord.rb:46:20:46:32 | ... + ... |
-| ActiveRecord.rb:52:16:52:28 | "name #{...}" |
-| ActiveRecord.rb:56:20:56:39 | "username = #{...}" |
-| ActiveRecord.rb:68:21:68:44 | ...[...] |
-| ActiveRecord.rb:106:27:106:76 | "this is an unsafe annotation:..." |
+| ActiveRecord.rb:17:24:17:24 | q |
+| ActiveRecord.rb:23:16:23:24 | condition |
+| ActiveRecord.rb:32:30:32:44 | ...[...] |
+| ActiveRecord.rb:33:20:33:42 | "id = '#{...}'" |
+| ActiveRecord.rb:34:21:34:45 | call to [] |
+| ActiveRecord.rb:35:16:35:21 | <<-SQL |
+| ActiveRecord.rb:38:20:38:47 | "user.id = '#{...}'" |
+| ActiveRecord.rb:50:20:50:32 | ... + ... |
+| ActiveRecord.rb:56:16:56:28 | "name #{...}" |
+| ActiveRecord.rb:60:20:60:39 | "username = #{...}" |
+| ActiveRecord.rb:72:21:72:44 | ...[...] |
+| ActiveRecord.rb:110:27:110:76 | "this is an unsafe annotation:..." |
activeRecordModelClassMethodCalls
| ActiveRecord.rb:2:3:2:17 | call to has_many |
| ActiveRecord.rb:6:3:6:24 | call to belongs_to |
| ActiveRecord.rb:9:5:9:68 | call to find |
| ActiveRecord.rb:13:5:13:40 | call to find_by |
| ActiveRecord.rb:13:5:13:46 | call to users |
-| ActiveRecord.rb:19:5:19:25 | call to destroy_by |
-| ActiveRecord.rb:28:5:28:45 | call to calculate |
-| ActiveRecord.rb:29:5:29:43 | call to delete_by |
-| ActiveRecord.rb:30:5:30:46 | call to destroy_by |
-| ActiveRecord.rb:31:5:31:35 | call to where |
-| ActiveRecord.rb:34:5:34:14 | call to where |
-| ActiveRecord.rb:34:5:34:48 | call to not |
-| ActiveRecord.rb:36:5:36:30 | call to find_by_name |
-| ActiveRecord.rb:37:5:37:36 | call to not_a_find_by_method |
-| ActiveRecord.rb:46:5:46:33 | call to delete_by |
-| ActiveRecord.rb:52:5:52:29 | call to order |
-| ActiveRecord.rb:56:7:56:40 | call to find_by |
-| ActiveRecord.rb:60:5:60:33 | call to find_by |
-| ActiveRecord.rb:62:5:62:34 | call to find |
-| ActiveRecord.rb:72:5:72:24 | call to create |
-| ActiveRecord.rb:76:5:76:66 | call to create |
-| ActiveRecord.rb:80:5:80:68 | call to create |
-| ActiveRecord.rb:84:5:84:16 | call to create |
-| ActiveRecord.rb:88:5:88:27 | call to update |
-| ActiveRecord.rb:92:5:92:69 | call to update |
-| ActiveRecord.rb:96:5:96:71 | call to update |
-| ActiveRecord.rb:102:13:102:54 | call to annotate |
-| ActiveRecord.rb:106:13:106:77 | call to annotate |
+| ActiveRecord.rb:23:5:23:25 | call to destroy_by |
+| ActiveRecord.rb:32:5:32:45 | call to calculate |
+| ActiveRecord.rb:33:5:33:43 | call to delete_by |
+| ActiveRecord.rb:34:5:34:46 | call to destroy_by |
+| ActiveRecord.rb:35:5:35:35 | call to where |
+| ActiveRecord.rb:38:5:38:14 | call to where |
+| ActiveRecord.rb:38:5:38:48 | call to not |
+| ActiveRecord.rb:40:5:40:30 | call to find_by_name |
+| ActiveRecord.rb:41:5:41:36 | call to not_a_find_by_method |
+| ActiveRecord.rb:50:5:50:33 | call to delete_by |
+| ActiveRecord.rb:56:5:56:29 | call to order |
+| ActiveRecord.rb:60:7:60:40 | call to find_by |
+| ActiveRecord.rb:64:5:64:33 | call to find_by |
+| ActiveRecord.rb:66:5:66:34 | call to find |
+| ActiveRecord.rb:76:5:76:24 | call to create |
+| ActiveRecord.rb:80:5:80:66 | call to create |
+| ActiveRecord.rb:84:5:84:68 | call to create |
+| ActiveRecord.rb:88:5:88:16 | call to create |
+| ActiveRecord.rb:92:5:92:27 | call to update |
+| ActiveRecord.rb:96:5:96:69 | call to update |
+| ActiveRecord.rb:100:5:100:71 | call to update |
+| ActiveRecord.rb:106:13:106:54 | call to annotate |
+| ActiveRecord.rb:110:13:110:77 | call to annotate |
| associations.rb:2:3:2:17 | call to has_many |
| associations.rb:6:3:6:20 | call to belongs_to |
| associations.rb:7:3:7:20 | call to has_many |
@@ -200,41 +204,41 @@ activeRecordModelClassMethodCalls
activeRecordModelClassMethodCallsReplacement
| ActiveRecord.rb:1:1:3:3 | UserGroup | ActiveRecord.rb:2:3:2:17 | call to has_many |
| ActiveRecord.rb:1:1:3:3 | UserGroup | ActiveRecord.rb:13:5:13:40 | call to find_by |
-| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:6:3:6:24 | call to belongs_to |
-| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:9:5:9:68 | call to find |
-| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:19:5:19:25 | call to destroy_by |
-| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:28:5:28:45 | call to calculate |
-| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:29:5:29:43 | call to delete_by |
-| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:30:5:30:46 | call to destroy_by |
-| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:31:5:31:35 | call to where |
-| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:34:5:34:14 | call to where |
-| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:35:5:35:51 | call to authenticate |
-| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:36:5:36:30 | call to find_by_name |
-| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:37:5:37:36 | call to not_a_find_by_method |
-| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:46:5:46:33 | call to delete_by |
-| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:52:5:52:29 | call to order |
-| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:56:7:56:40 | call to find_by |
-| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:60:5:60:33 | call to find_by |
-| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:62:5:62:34 | call to find |
-| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:68:5:68:45 | call to delete_by |
-| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:72:5:72:24 | call to create |
-| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:76:5:76:66 | call to create |
-| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:80:5:80:68 | call to create |
-| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:84:5:84:16 | call to create |
-| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:88:5:88:27 | call to update |
-| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:92:5:92:69 | call to update |
-| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:96:5:96:71 | call to update |
-| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:102:13:102:54 | call to annotate |
-| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:106:13:106:77 | call to annotate |
-| ActiveRecord.rb:17:1:21:3 | Admin | ActiveRecord.rb:19:5:19:25 | call to destroy_by |
-| ActiveRecord.rb:17:1:21:3 | Admin | ActiveRecord.rb:68:5:68:45 | call to delete_by |
-| ActiveRecord.rb:17:1:21:3 | Admin | ActiveRecord.rb:72:5:72:24 | call to create |
-| ActiveRecord.rb:17:1:21:3 | Admin | ActiveRecord.rb:76:5:76:66 | call to create |
-| ActiveRecord.rb:17:1:21:3 | Admin | ActiveRecord.rb:80:5:80:68 | call to create |
-| ActiveRecord.rb:17:1:21:3 | Admin | ActiveRecord.rb:84:5:84:16 | call to create |
-| ActiveRecord.rb:17:1:21:3 | Admin | ActiveRecord.rb:88:5:88:27 | call to update |
-| ActiveRecord.rb:17:1:21:3 | Admin | ActiveRecord.rb:92:5:92:69 | call to update |
-| ActiveRecord.rb:17:1:21:3 | Admin | ActiveRecord.rb:96:5:96:71 | call to update |
+| ActiveRecord.rb:5:1:19:3 | User | ActiveRecord.rb:6:3:6:24 | call to belongs_to |
+| ActiveRecord.rb:5:1:19:3 | User | ActiveRecord.rb:9:5:9:68 | call to find |
+| ActiveRecord.rb:5:1:19:3 | User | ActiveRecord.rb:23:5:23:25 | call to destroy_by |
+| ActiveRecord.rb:5:1:19:3 | User | ActiveRecord.rb:32:5:32:45 | call to calculate |
+| ActiveRecord.rb:5:1:19:3 | User | ActiveRecord.rb:33:5:33:43 | call to delete_by |
+| ActiveRecord.rb:5:1:19:3 | User | ActiveRecord.rb:34:5:34:46 | call to destroy_by |
+| ActiveRecord.rb:5:1:19:3 | User | ActiveRecord.rb:35:5:35:35 | call to where |
+| ActiveRecord.rb:5:1:19:3 | User | ActiveRecord.rb:38:5:38:14 | call to where |
+| ActiveRecord.rb:5:1:19:3 | User | ActiveRecord.rb:39:5:39:51 | call to authenticate |
+| ActiveRecord.rb:5:1:19:3 | User | ActiveRecord.rb:40:5:40:30 | call to find_by_name |
+| ActiveRecord.rb:5:1:19:3 | User | ActiveRecord.rb:41:5:41:36 | call to not_a_find_by_method |
+| ActiveRecord.rb:5:1:19:3 | User | ActiveRecord.rb:50:5:50:33 | call to delete_by |
+| ActiveRecord.rb:5:1:19:3 | User | ActiveRecord.rb:56:5:56:29 | call to order |
+| ActiveRecord.rb:5:1:19:3 | User | ActiveRecord.rb:60:7:60:40 | call to find_by |
+| ActiveRecord.rb:5:1:19:3 | User | ActiveRecord.rb:64:5:64:33 | call to find_by |
+| ActiveRecord.rb:5:1:19:3 | User | ActiveRecord.rb:66:5:66:34 | call to find |
+| ActiveRecord.rb:5:1:19:3 | User | ActiveRecord.rb:72:5:72:45 | call to delete_by |
+| ActiveRecord.rb:5:1:19:3 | User | ActiveRecord.rb:76:5:76:24 | call to create |
+| ActiveRecord.rb:5:1:19:3 | User | ActiveRecord.rb:80:5:80:66 | call to create |
+| ActiveRecord.rb:5:1:19:3 | User | ActiveRecord.rb:84:5:84:68 | call to create |
+| ActiveRecord.rb:5:1:19:3 | User | ActiveRecord.rb:88:5:88:16 | call to create |
+| ActiveRecord.rb:5:1:19:3 | User | ActiveRecord.rb:92:5:92:27 | call to update |
+| ActiveRecord.rb:5:1:19:3 | User | ActiveRecord.rb:96:5:96:69 | call to update |
+| ActiveRecord.rb:5:1:19:3 | User | ActiveRecord.rb:100:5:100:71 | call to update |
+| ActiveRecord.rb:5:1:19:3 | User | ActiveRecord.rb:106:13:106:54 | call to annotate |
+| ActiveRecord.rb:5:1:19:3 | User | ActiveRecord.rb:110:13:110:77 | call to annotate |
+| ActiveRecord.rb:21:1:25:3 | Admin | ActiveRecord.rb:23:5:23:25 | call to destroy_by |
+| ActiveRecord.rb:21:1:25:3 | Admin | ActiveRecord.rb:72:5:72:45 | call to delete_by |
+| ActiveRecord.rb:21:1:25:3 | Admin | ActiveRecord.rb:76:5:76:24 | call to create |
+| ActiveRecord.rb:21:1:25:3 | Admin | ActiveRecord.rb:80:5:80:66 | call to create |
+| ActiveRecord.rb:21:1:25:3 | Admin | ActiveRecord.rb:84:5:84:68 | call to create |
+| ActiveRecord.rb:21:1:25:3 | Admin | ActiveRecord.rb:88:5:88:16 | call to create |
+| ActiveRecord.rb:21:1:25:3 | Admin | ActiveRecord.rb:92:5:92:27 | call to update |
+| ActiveRecord.rb:21:1:25:3 | Admin | ActiveRecord.rb:96:5:96:69 | call to update |
+| ActiveRecord.rb:21:1:25:3 | Admin | ActiveRecord.rb:100:5:100:71 | call to update |
| associations.rb:1:1:3:3 | Author | associations.rb:2:3:2:17 | call to has_many |
| associations.rb:1:1:3:3 | Author | associations.rb:19:11:19:20 | call to new |
| associations.rb:5:1:9:3 | Post | associations.rb:6:3:6:20 | call to belongs_to |
@@ -244,28 +248,29 @@ activeRecordModelClassMethodCallsReplacement
| associations.rb:15:1:17:3 | Comment | associations.rb:16:3:16:18 | call to belongs_to |
potentiallyUnsafeSqlExecutingMethodCall
| ActiveRecord.rb:9:5:9:68 | call to find |
-| ActiveRecord.rb:19:5:19:25 | call to destroy_by |
-| ActiveRecord.rb:28:5:28:45 | call to calculate |
-| ActiveRecord.rb:29:5:29:43 | call to delete_by |
-| ActiveRecord.rb:30:5:30:46 | call to destroy_by |
-| ActiveRecord.rb:31:5:31:35 | call to where |
-| ActiveRecord.rb:34:5:34:48 | call to not |
-| ActiveRecord.rb:46:5:46:33 | call to delete_by |
-| ActiveRecord.rb:52:5:52:29 | call to order |
-| ActiveRecord.rb:56:7:56:40 | call to find_by |
-| ActiveRecord.rb:106:13:106:77 | call to annotate |
+| ActiveRecord.rb:23:5:23:25 | call to destroy_by |
+| ActiveRecord.rb:32:5:32:45 | call to calculate |
+| ActiveRecord.rb:33:5:33:43 | call to delete_by |
+| ActiveRecord.rb:34:5:34:46 | call to destroy_by |
+| ActiveRecord.rb:35:5:35:35 | call to where |
+| ActiveRecord.rb:38:5:38:48 | call to not |
+| ActiveRecord.rb:50:5:50:33 | call to delete_by |
+| ActiveRecord.rb:56:5:56:29 | call to order |
+| ActiveRecord.rb:60:7:60:40 | call to find_by |
+| ActiveRecord.rb:110:13:110:77 | call to annotate |
activeRecordModelInstantiations
-| ActiveRecord.rb:9:5:9:68 | call to find | ActiveRecord.rb:5:1:15:3 | User |
+| ActiveRecord.rb:9:5:9:68 | call to find | ActiveRecord.rb:5:1:19:3 | User |
| ActiveRecord.rb:13:5:13:40 | call to find_by | ActiveRecord.rb:1:1:3:3 | UserGroup |
-| ActiveRecord.rb:13:5:13:46 | call to users | ActiveRecord.rb:5:1:15:3 | User |
-| ActiveRecord.rb:36:5:36:30 | call to find_by_name | ActiveRecord.rb:5:1:15:3 | User |
-| ActiveRecord.rb:56:7:56:40 | call to find_by | ActiveRecord.rb:5:1:15:3 | User |
-| ActiveRecord.rb:60:5:60:33 | call to find_by | ActiveRecord.rb:5:1:15:3 | User |
-| ActiveRecord.rb:62:5:62:34 | call to find | ActiveRecord.rb:5:1:15:3 | User |
-| ActiveRecord.rb:72:5:72:24 | call to create | ActiveRecord.rb:17:1:21:3 | Admin |
-| ActiveRecord.rb:76:5:76:66 | call to create | ActiveRecord.rb:17:1:21:3 | Admin |
-| ActiveRecord.rb:80:5:80:68 | call to create | ActiveRecord.rb:17:1:21:3 | Admin |
-| ActiveRecord.rb:84:5:84:16 | call to create | ActiveRecord.rb:17:1:21:3 | Admin |
+| ActiveRecord.rb:13:5:13:46 | call to users | ActiveRecord.rb:5:1:19:3 | User |
+| ActiveRecord.rb:16:3:18:5 | self in exec | ActiveRecord.rb:5:1:19:3 | User |
+| ActiveRecord.rb:40:5:40:30 | call to find_by_name | ActiveRecord.rb:5:1:19:3 | User |
+| ActiveRecord.rb:60:7:60:40 | call to find_by | ActiveRecord.rb:5:1:19:3 | User |
+| ActiveRecord.rb:64:5:64:33 | call to find_by | ActiveRecord.rb:5:1:19:3 | User |
+| ActiveRecord.rb:66:5:66:34 | call to find | ActiveRecord.rb:5:1:19:3 | User |
+| ActiveRecord.rb:76:5:76:24 | call to create | ActiveRecord.rb:21:1:25:3 | Admin |
+| ActiveRecord.rb:80:5:80:66 | call to create | ActiveRecord.rb:21:1:25:3 | Admin |
+| ActiveRecord.rb:84:5:84:68 | call to create | ActiveRecord.rb:21:1:25:3 | Admin |
+| ActiveRecord.rb:88:5:88:16 | call to create | ActiveRecord.rb:21:1:25:3 | Admin |
| associations.rb:19:11:19:20 | call to new | associations.rb:1:1:3:3 | Author |
| associations.rb:21:9:21:21 | call to posts | associations.rb:5:1:9:3 | Post |
| associations.rb:21:9:21:28 | call to create | associations.rb:5:1:9:3 | Post |
@@ -307,13 +312,13 @@ activeRecordModelInstantiations
| associations.rb:53:1:53:13 | call to posts | associations.rb:5:1:9:3 | Post |
| associations.rb:53:1:53:20 | call to reload | associations.rb:5:1:9:3 | Post |
persistentWriteAccesses
-| ActiveRecord.rb:72:5:72:24 | call to create | ActiveRecord.rb:72:18:72:23 | call to params |
-| ActiveRecord.rb:76:5:76:66 | call to create | ActiveRecord.rb:76:24:76:36 | ...[...] |
-| ActiveRecord.rb:76:5:76:66 | call to create | ActiveRecord.rb:76:49:76:65 | ...[...] |
-| ActiveRecord.rb:80:5:80:68 | call to create | ActiveRecord.rb:80:25:80:37 | ...[...] |
-| ActiveRecord.rb:80:5:80:68 | call to create | ActiveRecord.rb:80:50:80:66 | ...[...] |
-| ActiveRecord.rb:88:5:88:27 | call to update | ActiveRecord.rb:88:21:88:26 | call to params |
-| ActiveRecord.rb:92:5:92:69 | call to update | ActiveRecord.rb:92:27:92:39 | ...[...] |
-| ActiveRecord.rb:92:5:92:69 | call to update | ActiveRecord.rb:92:52:92:68 | ...[...] |
-| ActiveRecord.rb:96:5:96:71 | call to update | ActiveRecord.rb:96:21:96:70 | call to [] |
+| ActiveRecord.rb:76:5:76:24 | call to create | ActiveRecord.rb:76:18:76:23 | call to params |
+| ActiveRecord.rb:80:5:80:66 | call to create | ActiveRecord.rb:80:24:80:36 | ...[...] |
+| ActiveRecord.rb:80:5:80:66 | call to create | ActiveRecord.rb:80:49:80:65 | ...[...] |
+| ActiveRecord.rb:84:5:84:68 | call to create | ActiveRecord.rb:84:25:84:37 | ...[...] |
+| ActiveRecord.rb:84:5:84:68 | call to create | ActiveRecord.rb:84:50:84:66 | ...[...] |
+| ActiveRecord.rb:92:5:92:27 | call to update | ActiveRecord.rb:92:21:92:26 | call to params |
+| ActiveRecord.rb:96:5:96:69 | call to update | ActiveRecord.rb:96:27:96:39 | ...[...] |
+| ActiveRecord.rb:96:5:96:69 | call to update | ActiveRecord.rb:96:52:96:68 | ...[...] |
+| ActiveRecord.rb:100:5:100:71 | call to update | ActiveRecord.rb:100:21:100:70 | call to [] |
| associations.rb:31:16:31:22 | ... = ... | associations.rb:31:16:31:22 | author2 |
diff --git a/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.rb b/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.rb
index 8e5961c8771..dca8f3c43d3 100644
--- a/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.rb
+++ b/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.rb
@@ -12,6 +12,10 @@ class User < ApplicationRecord
def self.from(user_group_id)
UserGroup.find_by(id: user_group_id).users
end
+
+ def exec(q)
+ connection.execute(q)
+ end
end
class Admin < User
From c1b35fbf475dbb89785ca6041492cf6eb1339981 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
Date: Mon, 5 Feb 2024 17:58:57 +0000
Subject: [PATCH 060/378] Release preparation for version 2.16.2
---
cpp/ql/lib/CHANGELOG.md | 7 +++++++
.../lib/change-notes/2024-01-30-throwing-model.md | 4 ----
.../0.12.5.md} | 8 +++++---
cpp/ql/lib/codeql-pack.release.yml | 2 +-
cpp/ql/lib/qlpack.yml | 2 +-
cpp/ql/src/CHANGELOG.md | 11 +++++++++++
.../change-notes/2024-01-19-extracted-files.md | 4 ----
...e_positive_incorrect_string_type_conversion.md | 4 ----
.../2024-01-29-incorrectly-checked-scanf-2.md | 4 ----
.../2024-01-29-incorrectly-checked-scanf.md | 4 ----
...24-01-29-uninitialized-local-false-positive.md | 5 -----
cpp/ql/src/change-notes/released/0.9.4.md | 10 ++++++++++
cpp/ql/src/codeql-pack.release.yml | 2 +-
cpp/ql/src/qlpack.yml | 2 +-
csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md | 4 ++++
.../Solorigate/lib/change-notes/released/1.7.8.md | 3 +++
.../Solorigate/lib/codeql-pack.release.yml | 2 +-
csharp/ql/campaigns/Solorigate/lib/qlpack.yml | 2 +-
csharp/ql/campaigns/Solorigate/src/CHANGELOG.md | 4 ++++
.../Solorigate/src/change-notes/released/1.7.8.md | 3 +++
.../Solorigate/src/codeql-pack.release.yml | 2 +-
csharp/ql/campaigns/Solorigate/src/qlpack.yml | 2 +-
csharp/ql/lib/CHANGELOG.md | 11 +++++++++++
.../2024-01-25-extractor-option-logging.md | 6 ------
.../2024-01-26-collection-expression.md | 4 ----
.../2024-01-31-compilation-expanded-args.md | 5 -----
csharp/ql/lib/change-notes/released/0.8.8.md | 10 ++++++++++
csharp/ql/lib/codeql-pack.release.yml | 2 +-
csharp/ql/lib/qlpack.yml | 2 +-
csharp/ql/src/CHANGELOG.md | 6 ++++++
.../0.8.8.md} | 7 ++++---
csharp/ql/src/codeql-pack.release.yml | 2 +-
csharp/ql/src/qlpack.yml | 2 +-
go/ql/consistency-queries/CHANGELOG.md | 4 ++++
.../change-notes/released/0.0.7.md | 3 +++
go/ql/consistency-queries/codeql-pack.release.yml | 2 +-
go/ql/consistency-queries/qlpack.yml | 2 +-
go/ql/lib/CHANGELOG.md | 4 ++++
go/ql/lib/change-notes/released/0.7.8.md | 3 +++
go/ql/lib/codeql-pack.release.yml | 2 +-
go/ql/lib/qlpack.yml | 2 +-
go/ql/src/CHANGELOG.md | 4 ++++
go/ql/src/change-notes/released/0.7.8.md | 3 +++
go/ql/src/codeql-pack.release.yml | 2 +-
go/ql/src/qlpack.yml | 2 +-
java/ql/automodel/src/CHANGELOG.md | 4 ++++
.../automodel/src/change-notes/released/0.0.14.md | 3 +++
java/ql/automodel/src/codeql-pack.release.yml | 2 +-
java/ql/automodel/src/qlpack.yml | 2 +-
java/ql/lib/CHANGELOG.md | 10 ++++++++++
java/ql/lib/change-notes/2024-01-24-new-models.md | 7 -------
.../0.8.8.md} | 11 ++++++++---
java/ql/lib/codeql-pack.release.yml | 2 +-
java/ql/lib/qlpack.yml | 2 +-
java/ql/src/CHANGELOG.md | 15 +++++++++++----
...-01-15-android-sensitive-notification-query.md | 4 ----
...24-01-29-android-sensitive-text-field-query.md | 4 ----
java/ql/src/change-notes/released/0.8.8.md | 6 ++++++
java/ql/src/codeql-pack.release.yml | 2 +-
java/ql/src/qlpack.yml | 2 +-
javascript/ql/lib/CHANGELOG.md | 4 ++++
javascript/ql/lib/change-notes/released/0.8.8.md | 3 +++
javascript/ql/lib/codeql-pack.release.yml | 2 +-
javascript/ql/lib/qlpack.yml | 2 +-
javascript/ql/src/CHANGELOG.md | 4 ++++
javascript/ql/src/change-notes/released/0.8.8.md | 3 +++
javascript/ql/src/codeql-pack.release.yml | 2 +-
javascript/ql/src/qlpack.yml | 2 +-
misc/suite-helpers/CHANGELOG.md | 4 ++++
misc/suite-helpers/change-notes/released/0.7.8.md | 3 +++
misc/suite-helpers/codeql-pack.release.yml | 2 +-
misc/suite-helpers/qlpack.yml | 2 +-
python/ql/lib/CHANGELOG.md | 10 ++++++++++
.../change-notes/2024-01-21-regex-ascii-flag.md | 4 ----
.../ql/lib/change-notes/2024-01-22-html-escape.md | 4 ----
python/ql/lib/change-notes/released/0.11.8.md | 9 +++++++++
python/ql/lib/codeql-pack.release.yml | 2 +-
python/ql/lib/qlpack.yml | 2 +-
python/ql/src/CHANGELOG.md | 4 ++++
python/ql/src/change-notes/released/0.9.8.md | 3 +++
python/ql/src/codeql-pack.release.yml | 2 +-
python/ql/src/qlpack.yml | 2 +-
ruby/ql/lib/CHANGELOG.md | 6 ++++++
.../0.8.8.md} | 7 ++++---
ruby/ql/lib/codeql-pack.release.yml | 2 +-
ruby/ql/lib/qlpack.yml | 2 +-
ruby/ql/src/CHANGELOG.md | 11 +++++++++++
.../2023-12-18-insecure-randomness-query.md | 4 ----
.../2024-01-30-unsafe-deserialization-sinks.md | 5 -----
ruby/ql/src/change-notes/released/0.8.8.md | 10 ++++++++++
ruby/ql/src/codeql-pack.release.yml | 2 +-
ruby/ql/src/qlpack.yml | 2 +-
shared/controlflow/CHANGELOG.md | 4 ++++
shared/controlflow/change-notes/released/0.1.8.md | 3 +++
shared/controlflow/codeql-pack.release.yml | 2 +-
shared/controlflow/qlpack.yml | 2 +-
shared/dataflow/CHANGELOG.md | 4 ++++
shared/dataflow/change-notes/released/0.1.8.md | 3 +++
shared/dataflow/codeql-pack.release.yml | 2 +-
shared/dataflow/qlpack.yml | 2 +-
shared/mad/CHANGELOG.md | 4 ++++
shared/mad/change-notes/released/0.2.8.md | 3 +++
shared/mad/codeql-pack.release.yml | 2 +-
shared/mad/qlpack.yml | 2 +-
shared/rangeanalysis/CHANGELOG.md | 4 ++++
.../rangeanalysis/change-notes/released/0.0.7.md | 3 +++
shared/rangeanalysis/codeql-pack.release.yml | 2 +-
shared/rangeanalysis/qlpack.yml | 2 +-
shared/regex/CHANGELOG.md | 4 ++++
shared/regex/change-notes/released/0.2.8.md | 3 +++
shared/regex/codeql-pack.release.yml | 2 +-
shared/regex/qlpack.yml | 2 +-
shared/ssa/CHANGELOG.md | 4 ++++
shared/ssa/change-notes/released/0.2.8.md | 3 +++
shared/ssa/codeql-pack.release.yml | 2 +-
shared/ssa/qlpack.yml | 2 +-
shared/threat-models/CHANGELOG.md | 4 ++++
.../threat-models/change-notes/released/0.0.7.md | 3 +++
shared/threat-models/codeql-pack.release.yml | 2 +-
shared/threat-models/qlpack.yml | 2 +-
shared/tutorial/CHANGELOG.md | 4 ++++
shared/tutorial/change-notes/released/0.2.8.md | 3 +++
shared/tutorial/codeql-pack.release.yml | 2 +-
shared/tutorial/qlpack.yml | 2 +-
shared/typetracking/CHANGELOG.md | 4 ++++
.../typetracking/change-notes/released/0.2.8.md | 3 +++
shared/typetracking/codeql-pack.release.yml | 2 +-
shared/typetracking/qlpack.yml | 2 +-
shared/typos/CHANGELOG.md | 4 ++++
shared/typos/change-notes/released/0.2.8.md | 3 +++
shared/typos/codeql-pack.release.yml | 2 +-
shared/typos/qlpack.yml | 2 +-
shared/util/CHANGELOG.md | 4 ++++
shared/util/change-notes/released/0.2.8.md | 3 +++
shared/util/codeql-pack.release.yml | 2 +-
shared/util/qlpack.yml | 2 +-
shared/yaml/CHANGELOG.md | 4 ++++
shared/yaml/change-notes/released/0.2.8.md | 3 +++
shared/yaml/codeql-pack.release.yml | 2 +-
shared/yaml/qlpack.yml | 2 +-
swift/ql/lib/CHANGELOG.md | 4 ++++
swift/ql/lib/change-notes/released/0.3.8.md | 3 +++
swift/ql/lib/codeql-pack.release.yml | 2 +-
swift/ql/lib/qlpack.yml | 2 +-
swift/ql/src/CHANGELOG.md | 4 ++++
swift/ql/src/change-notes/released/0.3.8.md | 3 +++
swift/ql/src/codeql-pack.release.yml | 2 +-
swift/ql/src/qlpack.yml | 2 +-
148 files changed, 383 insertions(+), 154 deletions(-)
delete mode 100644 cpp/ql/lib/change-notes/2024-01-30-throwing-model.md
rename cpp/ql/lib/change-notes/{2024-01-30-preproc-block.md => released/0.12.5.md} (55%)
delete mode 100644 cpp/ql/src/change-notes/2024-01-19-extracted-files.md
delete mode 100644 cpp/ql/src/change-notes/2024-01-29-false_positive_incorrect_string_type_conversion.md
delete mode 100644 cpp/ql/src/change-notes/2024-01-29-incorrectly-checked-scanf-2.md
delete mode 100644 cpp/ql/src/change-notes/2024-01-29-incorrectly-checked-scanf.md
delete mode 100644 cpp/ql/src/change-notes/2024-01-29-uninitialized-local-false-positive.md
create mode 100644 cpp/ql/src/change-notes/released/0.9.4.md
create mode 100644 csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.7.8.md
create mode 100644 csharp/ql/campaigns/Solorigate/src/change-notes/released/1.7.8.md
delete mode 100644 csharp/ql/lib/change-notes/2024-01-25-extractor-option-logging.md
delete mode 100644 csharp/ql/lib/change-notes/2024-01-26-collection-expression.md
delete mode 100644 csharp/ql/lib/change-notes/2024-01-31-compilation-expanded-args.md
create mode 100644 csharp/ql/lib/change-notes/released/0.8.8.md
rename csharp/ql/src/change-notes/{2024-01-22-url-redirect-sanitizer.md => released/0.8.8.md} (75%)
create mode 100644 go/ql/consistency-queries/change-notes/released/0.0.7.md
create mode 100644 go/ql/lib/change-notes/released/0.7.8.md
create mode 100644 go/ql/src/change-notes/released/0.7.8.md
create mode 100644 java/ql/automodel/src/change-notes/released/0.0.14.md
delete mode 100644 java/ql/lib/change-notes/2024-01-24-new-models.md
rename java/ql/lib/change-notes/{2024-01-23-add-uuid-and-date-to-simpletypesanitizer.md => released/0.8.8.md} (52%)
delete mode 100644 java/ql/src/change-notes/2024-01-15-android-sensitive-notification-query.md
delete mode 100644 java/ql/src/change-notes/2024-01-29-android-sensitive-text-field-query.md
create mode 100644 java/ql/src/change-notes/released/0.8.8.md
create mode 100644 javascript/ql/lib/change-notes/released/0.8.8.md
create mode 100644 javascript/ql/src/change-notes/released/0.8.8.md
create mode 100644 misc/suite-helpers/change-notes/released/0.7.8.md
delete mode 100644 python/ql/lib/change-notes/2024-01-21-regex-ascii-flag.md
delete mode 100644 python/ql/lib/change-notes/2024-01-22-html-escape.md
create mode 100644 python/ql/lib/change-notes/released/0.11.8.md
create mode 100644 python/ql/src/change-notes/released/0.9.8.md
rename ruby/ql/lib/change-notes/{2024-01-22-erb-render-flow.md => released/0.8.8.md} (79%)
delete mode 100644 ruby/ql/src/change-notes/2023-12-18-insecure-randomness-query.md
delete mode 100644 ruby/ql/src/change-notes/2024-01-30-unsafe-deserialization-sinks.md
create mode 100644 ruby/ql/src/change-notes/released/0.8.8.md
create mode 100644 shared/controlflow/change-notes/released/0.1.8.md
create mode 100644 shared/dataflow/change-notes/released/0.1.8.md
create mode 100644 shared/mad/change-notes/released/0.2.8.md
create mode 100644 shared/rangeanalysis/change-notes/released/0.0.7.md
create mode 100644 shared/regex/change-notes/released/0.2.8.md
create mode 100644 shared/ssa/change-notes/released/0.2.8.md
create mode 100644 shared/threat-models/change-notes/released/0.0.7.md
create mode 100644 shared/tutorial/change-notes/released/0.2.8.md
create mode 100644 shared/typetracking/change-notes/released/0.2.8.md
create mode 100644 shared/typos/change-notes/released/0.2.8.md
create mode 100644 shared/util/change-notes/released/0.2.8.md
create mode 100644 shared/yaml/change-notes/released/0.2.8.md
create mode 100644 swift/ql/lib/change-notes/released/0.3.8.md
create mode 100644 swift/ql/src/change-notes/released/0.3.8.md
diff --git a/cpp/ql/lib/CHANGELOG.md b/cpp/ql/lib/CHANGELOG.md
index dc092f2ed35..b552a329250 100644
--- a/cpp/ql/lib/CHANGELOG.md
+++ b/cpp/ql/lib/CHANGELOG.md
@@ -1,3 +1,10 @@
+## 0.12.5
+
+### New Features
+
+* Added the `PreprocBlock.qll` library to this repository. This library offers a view of `#if`, `#elif`, `#else` and similar directives as a tree with navigable parent-child relationships.
+* Added a new `ThrowingFunction` abstract class that can be used to model an external function that may throw an exception.
+
## 0.12.4
### Minor Analysis Improvements
diff --git a/cpp/ql/lib/change-notes/2024-01-30-throwing-model.md b/cpp/ql/lib/change-notes/2024-01-30-throwing-model.md
deleted file mode 100644
index 591cc8cc771..00000000000
--- a/cpp/ql/lib/change-notes/2024-01-30-throwing-model.md
+++ /dev/null
@@ -1,4 +0,0 @@
----
-category: feature
----
-* Added a new `ThrowingFunction` abstract class that can be used to model an external function that may throw an exception.
\ No newline at end of file
diff --git a/cpp/ql/lib/change-notes/2024-01-30-preproc-block.md b/cpp/ql/lib/change-notes/released/0.12.5.md
similarity index 55%
rename from cpp/ql/lib/change-notes/2024-01-30-preproc-block.md
rename to cpp/ql/lib/change-notes/released/0.12.5.md
index 6995ec954ff..1ae4668a5c9 100644
--- a/cpp/ql/lib/change-notes/2024-01-30-preproc-block.md
+++ b/cpp/ql/lib/change-notes/released/0.12.5.md
@@ -1,4 +1,6 @@
----
-category: feature
----
+## 0.12.5
+
+### New Features
+
* Added the `PreprocBlock.qll` library to this repository. This library offers a view of `#if`, `#elif`, `#else` and similar directives as a tree with navigable parent-child relationships.
+* Added a new `ThrowingFunction` abstract class that can be used to model an external function that may throw an exception.
diff --git a/cpp/ql/lib/codeql-pack.release.yml b/cpp/ql/lib/codeql-pack.release.yml
index b458bb47c53..79f80ae516c 100644
--- a/cpp/ql/lib/codeql-pack.release.yml
+++ b/cpp/ql/lib/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 0.12.4
+lastReleaseVersion: 0.12.5
diff --git a/cpp/ql/lib/qlpack.yml b/cpp/ql/lib/qlpack.yml
index f0479b167c6..b1b4172e977 100644
--- a/cpp/ql/lib/qlpack.yml
+++ b/cpp/ql/lib/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/cpp-all
-version: 0.12.5-dev
+version: 0.12.5
groups: cpp
dbscheme: semmlecode.cpp.dbscheme
extractor: cpp
diff --git a/cpp/ql/src/CHANGELOG.md b/cpp/ql/src/CHANGELOG.md
index 0e67defb949..68bcdbc5b07 100644
--- a/cpp/ql/src/CHANGELOG.md
+++ b/cpp/ql/src/CHANGELOG.md
@@ -1,3 +1,14 @@
+## 0.9.4
+
+### Minor Analysis Improvements
+
+* Corrected 2 false positive with `cpp/incorrect-string-type-conversion`: conversion of byte arrays to wchar and new array allocations converted to wchar.
+* The "Incorrect return-value check for a 'scanf'-like function" query (`cpp/incorrectly-checked-scanf`) no longer reports an alert when an explicit check for EOF is added.
+* The "Incorrect return-value check for a 'scanf'-like function" query (`cpp/incorrectly-checked-scanf`) now recognizes more EOF checks.
+* The "Potentially uninitialized local variable" query (`cpp/uninitialized-local`) no longer reports an alert when the local variable is used as a qualifier to a static member function call.
+* ```
+* The diagnostic query `cpp/diagnostics/successfully-extracted-files` now considers any C/C++ file seen during extraction, even one with some errors, to be extracted / scanned. This affects the Code Scanning UI measure of scanned C/C++ files.
+
## 0.9.3
### Minor Analysis Improvements
diff --git a/cpp/ql/src/change-notes/2024-01-19-extracted-files.md b/cpp/ql/src/change-notes/2024-01-19-extracted-files.md
deleted file mode 100644
index df6de1576ac..00000000000
--- a/cpp/ql/src/change-notes/2024-01-19-extracted-files.md
+++ /dev/null
@@ -1,4 +0,0 @@
----
-category: minorAnalysis
----
-* The diagnostic query `cpp/diagnostics/successfully-extracted-files` now considers any C/C++ file seen during extraction, even one with some errors, to be extracted / scanned. This affects the Code Scanning UI measure of scanned C/C++ files.
diff --git a/cpp/ql/src/change-notes/2024-01-29-false_positive_incorrect_string_type_conversion.md b/cpp/ql/src/change-notes/2024-01-29-false_positive_incorrect_string_type_conversion.md
deleted file mode 100644
index 8f081c746f1..00000000000
--- a/cpp/ql/src/change-notes/2024-01-29-false_positive_incorrect_string_type_conversion.md
+++ /dev/null
@@ -1,4 +0,0 @@
----
-category: minorAnalysis
----
-* Corrected 2 false positive with `cpp/incorrect-string-type-conversion`: conversion of byte arrays to wchar and new array allocations converted to wchar.
\ No newline at end of file
diff --git a/cpp/ql/src/change-notes/2024-01-29-incorrectly-checked-scanf-2.md b/cpp/ql/src/change-notes/2024-01-29-incorrectly-checked-scanf-2.md
deleted file mode 100644
index cc361145db9..00000000000
--- a/cpp/ql/src/change-notes/2024-01-29-incorrectly-checked-scanf-2.md
+++ /dev/null
@@ -1,4 +0,0 @@
----
-category: minorAnalysis
----
-* The "Incorrect return-value check for a 'scanf'-like function" query (`cpp/incorrectly-checked-scanf`) now recognizes more EOF checks.
diff --git a/cpp/ql/src/change-notes/2024-01-29-incorrectly-checked-scanf.md b/cpp/ql/src/change-notes/2024-01-29-incorrectly-checked-scanf.md
deleted file mode 100644
index 7085b9ce0a8..00000000000
--- a/cpp/ql/src/change-notes/2024-01-29-incorrectly-checked-scanf.md
+++ /dev/null
@@ -1,4 +0,0 @@
----
-category: minorAnalysis
----
-* The "Incorrect return-value check for a 'scanf'-like function" query (`cpp/incorrectly-checked-scanf`) no longer reports an alert when an explicit check for EOF is added.
diff --git a/cpp/ql/src/change-notes/2024-01-29-uninitialized-local-false-positive.md b/cpp/ql/src/change-notes/2024-01-29-uninitialized-local-false-positive.md
deleted file mode 100644
index 0d07482b755..00000000000
--- a/cpp/ql/src/change-notes/2024-01-29-uninitialized-local-false-positive.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-category: minorAnalysis
----
-* The "Potentially uninitialized local variable" query (`cpp/uninitialized-local`) no longer reports an alert when the local variable is used as a qualifier to a static member function call.
-* ```
\ No newline at end of file
diff --git a/cpp/ql/src/change-notes/released/0.9.4.md b/cpp/ql/src/change-notes/released/0.9.4.md
new file mode 100644
index 00000000000..6525a90f9bb
--- /dev/null
+++ b/cpp/ql/src/change-notes/released/0.9.4.md
@@ -0,0 +1,10 @@
+## 0.9.4
+
+### Minor Analysis Improvements
+
+* Corrected 2 false positive with `cpp/incorrect-string-type-conversion`: conversion of byte arrays to wchar and new array allocations converted to wchar.
+* The "Incorrect return-value check for a 'scanf'-like function" query (`cpp/incorrectly-checked-scanf`) no longer reports an alert when an explicit check for EOF is added.
+* The "Incorrect return-value check for a 'scanf'-like function" query (`cpp/incorrectly-checked-scanf`) now recognizes more EOF checks.
+* The "Potentially uninitialized local variable" query (`cpp/uninitialized-local`) no longer reports an alert when the local variable is used as a qualifier to a static member function call.
+* ```
+* The diagnostic query `cpp/diagnostics/successfully-extracted-files` now considers any C/C++ file seen during extraction, even one with some errors, to be extracted / scanned. This affects the Code Scanning UI measure of scanned C/C++ files.
diff --git a/cpp/ql/src/codeql-pack.release.yml b/cpp/ql/src/codeql-pack.release.yml
index 7af7247cbb0..694907ca221 100644
--- a/cpp/ql/src/codeql-pack.release.yml
+++ b/cpp/ql/src/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 0.9.3
+lastReleaseVersion: 0.9.4
diff --git a/cpp/ql/src/qlpack.yml b/cpp/ql/src/qlpack.yml
index a04a6468617..0da41987b3e 100644
--- a/cpp/ql/src/qlpack.yml
+++ b/cpp/ql/src/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/cpp-queries
-version: 0.9.4-dev
+version: 0.9.4
groups:
- cpp
- queries
diff --git a/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md b/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md
index 8afcdeb67f3..1e9fa50c21f 100644
--- a/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md
+++ b/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 1.7.8
+
+No user-facing changes.
+
## 1.7.7
No user-facing changes.
diff --git a/csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.7.8.md b/csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.7.8.md
new file mode 100644
index 00000000000..89c236d93c5
--- /dev/null
+++ b/csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.7.8.md
@@ -0,0 +1,3 @@
+## 1.7.8
+
+No user-facing changes.
diff --git a/csharp/ql/campaigns/Solorigate/lib/codeql-pack.release.yml b/csharp/ql/campaigns/Solorigate/lib/codeql-pack.release.yml
index df4010bd267..e003efd5127 100644
--- a/csharp/ql/campaigns/Solorigate/lib/codeql-pack.release.yml
+++ b/csharp/ql/campaigns/Solorigate/lib/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 1.7.7
+lastReleaseVersion: 1.7.8
diff --git a/csharp/ql/campaigns/Solorigate/lib/qlpack.yml b/csharp/ql/campaigns/Solorigate/lib/qlpack.yml
index 56cadaf8534..77b1c8b5154 100644
--- a/csharp/ql/campaigns/Solorigate/lib/qlpack.yml
+++ b/csharp/ql/campaigns/Solorigate/lib/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/csharp-solorigate-all
-version: 1.7.8-dev
+version: 1.7.8
groups:
- csharp
- solorigate
diff --git a/csharp/ql/campaigns/Solorigate/src/CHANGELOG.md b/csharp/ql/campaigns/Solorigate/src/CHANGELOG.md
index 8afcdeb67f3..1e9fa50c21f 100644
--- a/csharp/ql/campaigns/Solorigate/src/CHANGELOG.md
+++ b/csharp/ql/campaigns/Solorigate/src/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 1.7.8
+
+No user-facing changes.
+
## 1.7.7
No user-facing changes.
diff --git a/csharp/ql/campaigns/Solorigate/src/change-notes/released/1.7.8.md b/csharp/ql/campaigns/Solorigate/src/change-notes/released/1.7.8.md
new file mode 100644
index 00000000000..89c236d93c5
--- /dev/null
+++ b/csharp/ql/campaigns/Solorigate/src/change-notes/released/1.7.8.md
@@ -0,0 +1,3 @@
+## 1.7.8
+
+No user-facing changes.
diff --git a/csharp/ql/campaigns/Solorigate/src/codeql-pack.release.yml b/csharp/ql/campaigns/Solorigate/src/codeql-pack.release.yml
index df4010bd267..e003efd5127 100644
--- a/csharp/ql/campaigns/Solorigate/src/codeql-pack.release.yml
+++ b/csharp/ql/campaigns/Solorigate/src/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 1.7.7
+lastReleaseVersion: 1.7.8
diff --git a/csharp/ql/campaigns/Solorigate/src/qlpack.yml b/csharp/ql/campaigns/Solorigate/src/qlpack.yml
index 0b783c75d5a..9851e27c691 100644
--- a/csharp/ql/campaigns/Solorigate/src/qlpack.yml
+++ b/csharp/ql/campaigns/Solorigate/src/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/csharp-solorigate-queries
-version: 1.7.8-dev
+version: 1.7.8
groups:
- csharp
- solorigate
diff --git a/csharp/ql/lib/CHANGELOG.md b/csharp/ql/lib/CHANGELOG.md
index 0b168b22df6..196cd5ecc92 100644
--- a/csharp/ql/lib/CHANGELOG.md
+++ b/csharp/ql/lib/CHANGELOG.md
@@ -1,3 +1,14 @@
+## 0.8.8
+
+### Minor Analysis Improvements
+
+* Added a new database relation to store compiler arguments specified inside `@[...].rsp` file arguments. The arguments
+are returned by `Compilation::getExpandedArgument/1` and `Compilation::getExpandedArguments/0`.
+* C# 12: Added extractor, QL library and data flow support for collection expressions like `[1, y, 4, .. x]`.
+* The C# extractor now accepts an extractor option `logging.verbosity` that specifies the verbosity of the logs. The
+option is added via `codeql database create --language=csharp -Ologging.verbosity=debug ...` or by setting the
+corresponding environment variable `CODEQL_EXTRACTOR_CSHARP_OPTION_LOGGING_VERBOSITY`.
+
## 0.8.7
### Minor Analysis Improvements
diff --git a/csharp/ql/lib/change-notes/2024-01-25-extractor-option-logging.md b/csharp/ql/lib/change-notes/2024-01-25-extractor-option-logging.md
deleted file mode 100644
index 71cb3202675..00000000000
--- a/csharp/ql/lib/change-notes/2024-01-25-extractor-option-logging.md
+++ /dev/null
@@ -1,6 +0,0 @@
----
-category: minorAnalysis
----
-* The C# extractor now accepts an extractor option `logging.verbosity` that specifies the verbosity of the logs. The
-option is added via `codeql database create --language=csharp -Ologging.verbosity=debug ...` or by setting the
-corresponding environment variable `CODEQL_EXTRACTOR_CSHARP_OPTION_LOGGING_VERBOSITY`.
\ No newline at end of file
diff --git a/csharp/ql/lib/change-notes/2024-01-26-collection-expression.md b/csharp/ql/lib/change-notes/2024-01-26-collection-expression.md
deleted file mode 100644
index 10a958dcf47..00000000000
--- a/csharp/ql/lib/change-notes/2024-01-26-collection-expression.md
+++ /dev/null
@@ -1,4 +0,0 @@
----
-category: minorAnalysis
----
-* C# 12: Added extractor, QL library and data flow support for collection expressions like `[1, y, 4, .. x]`.
diff --git a/csharp/ql/lib/change-notes/2024-01-31-compilation-expanded-args.md b/csharp/ql/lib/change-notes/2024-01-31-compilation-expanded-args.md
deleted file mode 100644
index 8767c0d1d65..00000000000
--- a/csharp/ql/lib/change-notes/2024-01-31-compilation-expanded-args.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-category: minorAnalysis
----
-* Added a new database relation to store compiler arguments specified inside `@[...].rsp` file arguments. The arguments
-are returned by `Compilation::getExpandedArgument/1` and `Compilation::getExpandedArguments/0`.
diff --git a/csharp/ql/lib/change-notes/released/0.8.8.md b/csharp/ql/lib/change-notes/released/0.8.8.md
new file mode 100644
index 00000000000..96b317ecd06
--- /dev/null
+++ b/csharp/ql/lib/change-notes/released/0.8.8.md
@@ -0,0 +1,10 @@
+## 0.8.8
+
+### Minor Analysis Improvements
+
+* Added a new database relation to store compiler arguments specified inside `@[...].rsp` file arguments. The arguments
+are returned by `Compilation::getExpandedArgument/1` and `Compilation::getExpandedArguments/0`.
+* C# 12: Added extractor, QL library and data flow support for collection expressions like `[1, y, 4, .. x]`.
+* The C# extractor now accepts an extractor option `logging.verbosity` that specifies the verbosity of the logs. The
+option is added via `codeql database create --language=csharp -Ologging.verbosity=debug ...` or by setting the
+corresponding environment variable `CODEQL_EXTRACTOR_CSHARP_OPTION_LOGGING_VERBOSITY`.
diff --git a/csharp/ql/lib/codeql-pack.release.yml b/csharp/ql/lib/codeql-pack.release.yml
index 2ef6dc421f3..da0a61b4048 100644
--- a/csharp/ql/lib/codeql-pack.release.yml
+++ b/csharp/ql/lib/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 0.8.7
+lastReleaseVersion: 0.8.8
diff --git a/csharp/ql/lib/qlpack.yml b/csharp/ql/lib/qlpack.yml
index 9d8db7347cb..2b137281da6 100644
--- a/csharp/ql/lib/qlpack.yml
+++ b/csharp/ql/lib/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/csharp-all
-version: 0.8.8-dev
+version: 0.8.8
groups: csharp
dbscheme: semmlecode.csharp.dbscheme
extractor: csharp
diff --git a/csharp/ql/src/CHANGELOG.md b/csharp/ql/src/CHANGELOG.md
index 6572f664b0e..ac2fbfce855 100644
--- a/csharp/ql/src/CHANGELOG.md
+++ b/csharp/ql/src/CHANGELOG.md
@@ -1,3 +1,9 @@
+## 0.8.8
+
+### Minor Analysis Improvements
+
+* Added string interpolation expressions and `string.Format` as possible sanitizers for the `cs/web/unvalidated-url-redirection` query.
+
## 0.8.7
### Minor Analysis Improvements
diff --git a/csharp/ql/src/change-notes/2024-01-22-url-redirect-sanitizer.md b/csharp/ql/src/change-notes/released/0.8.8.md
similarity index 75%
rename from csharp/ql/src/change-notes/2024-01-22-url-redirect-sanitizer.md
rename to csharp/ql/src/change-notes/released/0.8.8.md
index 92a65075a65..d6f017bcf41 100644
--- a/csharp/ql/src/change-notes/2024-01-22-url-redirect-sanitizer.md
+++ b/csharp/ql/src/change-notes/released/0.8.8.md
@@ -1,4 +1,5 @@
----
-category: minorAnalysis
----
+## 0.8.8
+
+### Minor Analysis Improvements
+
* Added string interpolation expressions and `string.Format` as possible sanitizers for the `cs/web/unvalidated-url-redirection` query.
diff --git a/csharp/ql/src/codeql-pack.release.yml b/csharp/ql/src/codeql-pack.release.yml
index 2ef6dc421f3..da0a61b4048 100644
--- a/csharp/ql/src/codeql-pack.release.yml
+++ b/csharp/ql/src/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 0.8.7
+lastReleaseVersion: 0.8.8
diff --git a/csharp/ql/src/qlpack.yml b/csharp/ql/src/qlpack.yml
index c3973948993..a16c72edd72 100644
--- a/csharp/ql/src/qlpack.yml
+++ b/csharp/ql/src/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/csharp-queries
-version: 0.8.8-dev
+version: 0.8.8
groups:
- csharp
- queries
diff --git a/go/ql/consistency-queries/CHANGELOG.md b/go/ql/consistency-queries/CHANGELOG.md
index ad2e63eb470..8f58f5145db 100644
--- a/go/ql/consistency-queries/CHANGELOG.md
+++ b/go/ql/consistency-queries/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.0.7
+
+No user-facing changes.
+
## 0.0.6
No user-facing changes.
diff --git a/go/ql/consistency-queries/change-notes/released/0.0.7.md b/go/ql/consistency-queries/change-notes/released/0.0.7.md
new file mode 100644
index 00000000000..84da6f18c42
--- /dev/null
+++ b/go/ql/consistency-queries/change-notes/released/0.0.7.md
@@ -0,0 +1,3 @@
+## 0.0.7
+
+No user-facing changes.
diff --git a/go/ql/consistency-queries/codeql-pack.release.yml b/go/ql/consistency-queries/codeql-pack.release.yml
index cf398ce02aa..a2a5484910b 100644
--- a/go/ql/consistency-queries/codeql-pack.release.yml
+++ b/go/ql/consistency-queries/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 0.0.6
+lastReleaseVersion: 0.0.7
diff --git a/go/ql/consistency-queries/qlpack.yml b/go/ql/consistency-queries/qlpack.yml
index 88886034408..c7522dd8e35 100644
--- a/go/ql/consistency-queries/qlpack.yml
+++ b/go/ql/consistency-queries/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql-go-consistency-queries
-version: 0.0.7-dev
+version: 0.0.7
groups:
- go
- queries
diff --git a/go/ql/lib/CHANGELOG.md b/go/ql/lib/CHANGELOG.md
index b9ff6e4e0e2..475352f1df2 100644
--- a/go/ql/lib/CHANGELOG.md
+++ b/go/ql/lib/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.7.8
+
+No user-facing changes.
+
## 0.7.7
### Deprecated APIs
diff --git a/go/ql/lib/change-notes/released/0.7.8.md b/go/ql/lib/change-notes/released/0.7.8.md
new file mode 100644
index 00000000000..5627ed51a17
--- /dev/null
+++ b/go/ql/lib/change-notes/released/0.7.8.md
@@ -0,0 +1,3 @@
+## 0.7.8
+
+No user-facing changes.
diff --git a/go/ql/lib/codeql-pack.release.yml b/go/ql/lib/codeql-pack.release.yml
index 89cc2330c10..b6b12196b26 100644
--- a/go/ql/lib/codeql-pack.release.yml
+++ b/go/ql/lib/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 0.7.7
+lastReleaseVersion: 0.7.8
diff --git a/go/ql/lib/qlpack.yml b/go/ql/lib/qlpack.yml
index 67c991934e0..5f317377d45 100644
--- a/go/ql/lib/qlpack.yml
+++ b/go/ql/lib/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/go-all
-version: 0.7.8-dev
+version: 0.7.8
groups: go
dbscheme: go.dbscheme
extractor: go
diff --git a/go/ql/src/CHANGELOG.md b/go/ql/src/CHANGELOG.md
index dafcd7aa695..66533a629f2 100644
--- a/go/ql/src/CHANGELOG.md
+++ b/go/ql/src/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.7.8
+
+No user-facing changes.
+
## 0.7.7
### Minor Analysis Improvements
diff --git a/go/ql/src/change-notes/released/0.7.8.md b/go/ql/src/change-notes/released/0.7.8.md
new file mode 100644
index 00000000000..5627ed51a17
--- /dev/null
+++ b/go/ql/src/change-notes/released/0.7.8.md
@@ -0,0 +1,3 @@
+## 0.7.8
+
+No user-facing changes.
diff --git a/go/ql/src/codeql-pack.release.yml b/go/ql/src/codeql-pack.release.yml
index 89cc2330c10..b6b12196b26 100644
--- a/go/ql/src/codeql-pack.release.yml
+++ b/go/ql/src/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 0.7.7
+lastReleaseVersion: 0.7.8
diff --git a/go/ql/src/qlpack.yml b/go/ql/src/qlpack.yml
index a760c342970..81654540219 100644
--- a/go/ql/src/qlpack.yml
+++ b/go/ql/src/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/go-queries
-version: 0.7.8-dev
+version: 0.7.8
groups:
- go
- queries
diff --git a/java/ql/automodel/src/CHANGELOG.md b/java/ql/automodel/src/CHANGELOG.md
index eb9aae31d41..fa718635e0c 100644
--- a/java/ql/automodel/src/CHANGELOG.md
+++ b/java/ql/automodel/src/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.0.14
+
+No user-facing changes.
+
## 0.0.13
No user-facing changes.
diff --git a/java/ql/automodel/src/change-notes/released/0.0.14.md b/java/ql/automodel/src/change-notes/released/0.0.14.md
new file mode 100644
index 00000000000..63b4d50ca45
--- /dev/null
+++ b/java/ql/automodel/src/change-notes/released/0.0.14.md
@@ -0,0 +1,3 @@
+## 0.0.14
+
+No user-facing changes.
diff --git a/java/ql/automodel/src/codeql-pack.release.yml b/java/ql/automodel/src/codeql-pack.release.yml
index 044e54e4f7e..ca29e45d0a6 100644
--- a/java/ql/automodel/src/codeql-pack.release.yml
+++ b/java/ql/automodel/src/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 0.0.13
+lastReleaseVersion: 0.0.14
diff --git a/java/ql/automodel/src/qlpack.yml b/java/ql/automodel/src/qlpack.yml
index 0845b6f1761..3334223e9e4 100644
--- a/java/ql/automodel/src/qlpack.yml
+++ b/java/ql/automodel/src/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/java-automodel-queries
-version: 0.0.14-dev
+version: 0.0.14
groups:
- java
- automodel
diff --git a/java/ql/lib/CHANGELOG.md b/java/ql/lib/CHANGELOG.md
index 3621a766e8a..4b34106dc09 100644
--- a/java/ql/lib/CHANGELOG.md
+++ b/java/ql/lib/CHANGELOG.md
@@ -1,3 +1,13 @@
+## 0.8.8
+
+### Minor Analysis Improvements
+
+* Added models for the following packages:
+
+ * com.fasterxml.jackson.databind
+ * javax.servlet
+* Added the `java.util.Date` and `java.util.UUID` classes to the list of types in the `SimpleTypeSanitizer` class in `semmle.code.java.security.Sanitizers`.
+
## 0.8.7
### New Features
diff --git a/java/ql/lib/change-notes/2024-01-24-new-models.md b/java/ql/lib/change-notes/2024-01-24-new-models.md
deleted file mode 100644
index 8646ac1f0cb..00000000000
--- a/java/ql/lib/change-notes/2024-01-24-new-models.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-category: minorAnalysis
----
-* Added models for the following packages:
-
- * com.fasterxml.jackson.databind
- * javax.servlet
diff --git a/java/ql/lib/change-notes/2024-01-23-add-uuid-and-date-to-simpletypesanitizer.md b/java/ql/lib/change-notes/released/0.8.8.md
similarity index 52%
rename from java/ql/lib/change-notes/2024-01-23-add-uuid-and-date-to-simpletypesanitizer.md
rename to java/ql/lib/change-notes/released/0.8.8.md
index 96d6b9e0334..62186579014 100644
--- a/java/ql/lib/change-notes/2024-01-23-add-uuid-and-date-to-simpletypesanitizer.md
+++ b/java/ql/lib/change-notes/released/0.8.8.md
@@ -1,4 +1,9 @@
----
-category: minorAnalysis
----
+## 0.8.8
+
+### Minor Analysis Improvements
+
+* Added models for the following packages:
+
+ * com.fasterxml.jackson.databind
+ * javax.servlet
* Added the `java.util.Date` and `java.util.UUID` classes to the list of types in the `SimpleTypeSanitizer` class in `semmle.code.java.security.Sanitizers`.
diff --git a/java/ql/lib/codeql-pack.release.yml b/java/ql/lib/codeql-pack.release.yml
index 2ef6dc421f3..da0a61b4048 100644
--- a/java/ql/lib/codeql-pack.release.yml
+++ b/java/ql/lib/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 0.8.7
+lastReleaseVersion: 0.8.8
diff --git a/java/ql/lib/qlpack.yml b/java/ql/lib/qlpack.yml
index 62f4a0d7e96..6e4e1269d9c 100644
--- a/java/ql/lib/qlpack.yml
+++ b/java/ql/lib/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/java-all
-version: 0.8.8-dev
+version: 0.8.8
groups: java
dbscheme: config/semmlecode.dbscheme
extractor: java
diff --git a/java/ql/src/CHANGELOG.md b/java/ql/src/CHANGELOG.md
index 84096230dd1..466b98fea11 100644
--- a/java/ql/src/CHANGELOG.md
+++ b/java/ql/src/CHANGELOG.md
@@ -1,3 +1,10 @@
+## 0.8.8
+
+### New Queries
+
+* Added a new query `java/android/sensitive-text` to detect instances of sensitive data being exposed through text fields without being properly masked.
+* Added a new query `java/android/sensitive-notification` to detect instances of sensitive data being exposed through Android notifications.
+
## 0.8.7
### New Queries
@@ -10,10 +17,6 @@
## 0.8.6
-### Deprecated Queries
-
-* The three queries `java/insufficient-key-size`, `java/server-side-template-injection`, and `java/android/implicit-pendingintents` had accidentally general extension points allowing arbitrary string-based flow state. This has been fixed and the old extension points have been deprecated where possible, and otherwise updated.
-
### New Queries
* Added the `java/insecure-randomness` query to detect uses of weakly random values which an attacker may be able to predict. Also added the `crypto-parameter` sink kind for sinks which represent the parameters and keys of cryptographic operations.
@@ -24,6 +27,10 @@
* The query `java/android/missing-certificate-pinning` should no longer alert about requests pointing to the local filesystem.
* Removed some spurious sinks related to `com.opensymphony.xwork2.TextProvider.getText` from the query `java/ognl-injection`.
+### Bug Fixes
+
+* The three queries `java/insufficient-key-size`, `java/server-side-template-injection`, and `java/android/implicit-pendingintents` had accidentally general extension points allowing arbitrary string-based flow state. This has been fixed and the old extension points have been deprecated where possible, and otherwise updated.
+
## 0.8.5
No user-facing changes.
diff --git a/java/ql/src/change-notes/2024-01-15-android-sensitive-notification-query.md b/java/ql/src/change-notes/2024-01-15-android-sensitive-notification-query.md
deleted file mode 100644
index 427ebbe94ff..00000000000
--- a/java/ql/src/change-notes/2024-01-15-android-sensitive-notification-query.md
+++ /dev/null
@@ -1,4 +0,0 @@
----
-category: newQuery
----
-* Added a new query `java/android/sensitive-notification` to detect instances of sensitive data being exposed through Android notifications.
\ No newline at end of file
diff --git a/java/ql/src/change-notes/2024-01-29-android-sensitive-text-field-query.md b/java/ql/src/change-notes/2024-01-29-android-sensitive-text-field-query.md
deleted file mode 100644
index 5e5156944a7..00000000000
--- a/java/ql/src/change-notes/2024-01-29-android-sensitive-text-field-query.md
+++ /dev/null
@@ -1,4 +0,0 @@
----
-category: newQuery
----
-* Added a new query `java/android/sensitive-text` to detect instances of sensitive data being exposed through text fields without being properly masked.
\ No newline at end of file
diff --git a/java/ql/src/change-notes/released/0.8.8.md b/java/ql/src/change-notes/released/0.8.8.md
new file mode 100644
index 00000000000..94f005fdca8
--- /dev/null
+++ b/java/ql/src/change-notes/released/0.8.8.md
@@ -0,0 +1,6 @@
+## 0.8.8
+
+### New Queries
+
+* Added a new query `java/android/sensitive-text` to detect instances of sensitive data being exposed through text fields without being properly masked.
+* Added a new query `java/android/sensitive-notification` to detect instances of sensitive data being exposed through Android notifications.
diff --git a/java/ql/src/codeql-pack.release.yml b/java/ql/src/codeql-pack.release.yml
index 2ef6dc421f3..da0a61b4048 100644
--- a/java/ql/src/codeql-pack.release.yml
+++ b/java/ql/src/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 0.8.7
+lastReleaseVersion: 0.8.8
diff --git a/java/ql/src/qlpack.yml b/java/ql/src/qlpack.yml
index 4d0d39baca3..73e8a062ffe 100644
--- a/java/ql/src/qlpack.yml
+++ b/java/ql/src/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/java-queries
-version: 0.8.8-dev
+version: 0.8.8
groups:
- java
- queries
diff --git a/javascript/ql/lib/CHANGELOG.md b/javascript/ql/lib/CHANGELOG.md
index 29005b5ce87..06e40ac7bd5 100644
--- a/javascript/ql/lib/CHANGELOG.md
+++ b/javascript/ql/lib/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.8.8
+
+No user-facing changes.
+
## 0.8.7
### Minor Analysis Improvements
diff --git a/javascript/ql/lib/change-notes/released/0.8.8.md b/javascript/ql/lib/change-notes/released/0.8.8.md
new file mode 100644
index 00000000000..14d202dac00
--- /dev/null
+++ b/javascript/ql/lib/change-notes/released/0.8.8.md
@@ -0,0 +1,3 @@
+## 0.8.8
+
+No user-facing changes.
diff --git a/javascript/ql/lib/codeql-pack.release.yml b/javascript/ql/lib/codeql-pack.release.yml
index 2ef6dc421f3..da0a61b4048 100644
--- a/javascript/ql/lib/codeql-pack.release.yml
+++ b/javascript/ql/lib/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 0.8.7
+lastReleaseVersion: 0.8.8
diff --git a/javascript/ql/lib/qlpack.yml b/javascript/ql/lib/qlpack.yml
index bd0c1a815f3..fa544548ea7 100644
--- a/javascript/ql/lib/qlpack.yml
+++ b/javascript/ql/lib/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/javascript-all
-version: 0.8.8-dev
+version: 0.8.8
groups: javascript
dbscheme: semmlecode.javascript.dbscheme
extractor: javascript
diff --git a/javascript/ql/src/CHANGELOG.md b/javascript/ql/src/CHANGELOG.md
index ba868a7d629..300da5225f9 100644
--- a/javascript/ql/src/CHANGELOG.md
+++ b/javascript/ql/src/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.8.8
+
+No user-facing changes.
+
## 0.8.7
### Minor Analysis Improvements
diff --git a/javascript/ql/src/change-notes/released/0.8.8.md b/javascript/ql/src/change-notes/released/0.8.8.md
new file mode 100644
index 00000000000..14d202dac00
--- /dev/null
+++ b/javascript/ql/src/change-notes/released/0.8.8.md
@@ -0,0 +1,3 @@
+## 0.8.8
+
+No user-facing changes.
diff --git a/javascript/ql/src/codeql-pack.release.yml b/javascript/ql/src/codeql-pack.release.yml
index 2ef6dc421f3..da0a61b4048 100644
--- a/javascript/ql/src/codeql-pack.release.yml
+++ b/javascript/ql/src/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 0.8.7
+lastReleaseVersion: 0.8.8
diff --git a/javascript/ql/src/qlpack.yml b/javascript/ql/src/qlpack.yml
index 51a22b542e0..1ebbfc58787 100644
--- a/javascript/ql/src/qlpack.yml
+++ b/javascript/ql/src/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/javascript-queries
-version: 0.8.8-dev
+version: 0.8.8
groups:
- javascript
- queries
diff --git a/misc/suite-helpers/CHANGELOG.md b/misc/suite-helpers/CHANGELOG.md
index 1c10493c9e7..61d4b001d25 100644
--- a/misc/suite-helpers/CHANGELOG.md
+++ b/misc/suite-helpers/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.7.8
+
+No user-facing changes.
+
## 0.7.7
No user-facing changes.
diff --git a/misc/suite-helpers/change-notes/released/0.7.8.md b/misc/suite-helpers/change-notes/released/0.7.8.md
new file mode 100644
index 00000000000..5627ed51a17
--- /dev/null
+++ b/misc/suite-helpers/change-notes/released/0.7.8.md
@@ -0,0 +1,3 @@
+## 0.7.8
+
+No user-facing changes.
diff --git a/misc/suite-helpers/codeql-pack.release.yml b/misc/suite-helpers/codeql-pack.release.yml
index 89cc2330c10..b6b12196b26 100644
--- a/misc/suite-helpers/codeql-pack.release.yml
+++ b/misc/suite-helpers/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 0.7.7
+lastReleaseVersion: 0.7.8
diff --git a/misc/suite-helpers/qlpack.yml b/misc/suite-helpers/qlpack.yml
index 82d40178d7e..4db5dfcf454 100644
--- a/misc/suite-helpers/qlpack.yml
+++ b/misc/suite-helpers/qlpack.yml
@@ -1,4 +1,4 @@
name: codeql/suite-helpers
-version: 0.7.8-dev
+version: 0.7.8
groups: shared
warnOnImplicitThis: true
diff --git a/python/ql/lib/CHANGELOG.md b/python/ql/lib/CHANGELOG.md
index ca684c59320..01692622749 100644
--- a/python/ql/lib/CHANGELOG.md
+++ b/python/ql/lib/CHANGELOG.md
@@ -1,3 +1,13 @@
+## 0.11.8
+
+### Minor Analysis Improvements
+
+* Added `html.escape` as a sanitizer for HTML.
+
+### Bug Fixes
+
+* Fixed the `a` (ASCII) inline flag not being recognized by the regular expression library.
+
## 0.11.7
### Minor Analysis Improvements
diff --git a/python/ql/lib/change-notes/2024-01-21-regex-ascii-flag.md b/python/ql/lib/change-notes/2024-01-21-regex-ascii-flag.md
deleted file mode 100644
index 5d8741b1bd3..00000000000
--- a/python/ql/lib/change-notes/2024-01-21-regex-ascii-flag.md
+++ /dev/null
@@ -1,4 +0,0 @@
----
-category: fix
----
-* Fixed the `a` (ASCII) inline flag not being recognized by the regular expression library.
diff --git a/python/ql/lib/change-notes/2024-01-22-html-escape.md b/python/ql/lib/change-notes/2024-01-22-html-escape.md
deleted file mode 100644
index 0ae31aee545..00000000000
--- a/python/ql/lib/change-notes/2024-01-22-html-escape.md
+++ /dev/null
@@ -1,4 +0,0 @@
----
-category: minorAnalysis
----
-* Added `html.escape` as a sanitizer for HTML.
diff --git a/python/ql/lib/change-notes/released/0.11.8.md b/python/ql/lib/change-notes/released/0.11.8.md
new file mode 100644
index 00000000000..d61a4451868
--- /dev/null
+++ b/python/ql/lib/change-notes/released/0.11.8.md
@@ -0,0 +1,9 @@
+## 0.11.8
+
+### Minor Analysis Improvements
+
+* Added `html.escape` as a sanitizer for HTML.
+
+### Bug Fixes
+
+* Fixed the `a` (ASCII) inline flag not being recognized by the regular expression library.
diff --git a/python/ql/lib/codeql-pack.release.yml b/python/ql/lib/codeql-pack.release.yml
index 59fa16251b6..345c308d402 100644
--- a/python/ql/lib/codeql-pack.release.yml
+++ b/python/ql/lib/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 0.11.7
+lastReleaseVersion: 0.11.8
diff --git a/python/ql/lib/qlpack.yml b/python/ql/lib/qlpack.yml
index 23bff260f7a..a2c343cca3f 100644
--- a/python/ql/lib/qlpack.yml
+++ b/python/ql/lib/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/python-all
-version: 0.11.8-dev
+version: 0.11.8
groups: python
dbscheme: semmlecode.python.dbscheme
extractor: python
diff --git a/python/ql/src/CHANGELOG.md b/python/ql/src/CHANGELOG.md
index b42dcfd8b31..17931ead8b1 100644
--- a/python/ql/src/CHANGELOG.md
+++ b/python/ql/src/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.9.8
+
+No user-facing changes.
+
## 0.9.7
### Minor Analysis Improvements
diff --git a/python/ql/src/change-notes/released/0.9.8.md b/python/ql/src/change-notes/released/0.9.8.md
new file mode 100644
index 00000000000..d1ca1c4d647
--- /dev/null
+++ b/python/ql/src/change-notes/released/0.9.8.md
@@ -0,0 +1,3 @@
+## 0.9.8
+
+No user-facing changes.
diff --git a/python/ql/src/codeql-pack.release.yml b/python/ql/src/codeql-pack.release.yml
index 0921a438254..9ca6c6f2678 100644
--- a/python/ql/src/codeql-pack.release.yml
+++ b/python/ql/src/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 0.9.7
+lastReleaseVersion: 0.9.8
diff --git a/python/ql/src/qlpack.yml b/python/ql/src/qlpack.yml
index 5de71eb6e3a..538e5ad799c 100644
--- a/python/ql/src/qlpack.yml
+++ b/python/ql/src/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/python-queries
-version: 0.9.8-dev
+version: 0.9.8
groups:
- python
- queries
diff --git a/ruby/ql/lib/CHANGELOG.md b/ruby/ql/lib/CHANGELOG.md
index e9e4507d8df..8a9e4e6c8b7 100644
--- a/ruby/ql/lib/CHANGELOG.md
+++ b/ruby/ql/lib/CHANGELOG.md
@@ -1,3 +1,9 @@
+## 0.8.8
+
+### Minor Analysis Improvements
+
+* Flow is now tracked through Rails `render` calls, when the argument is a `ViewComponent`. In this case, data flow is tracked into the accompanying `.html.erb` file.
+
## 0.8.7
### Minor Analysis Improvements
diff --git a/ruby/ql/lib/change-notes/2024-01-22-erb-render-flow.md b/ruby/ql/lib/change-notes/released/0.8.8.md
similarity index 79%
rename from ruby/ql/lib/change-notes/2024-01-22-erb-render-flow.md
rename to ruby/ql/lib/change-notes/released/0.8.8.md
index f9e68ef580e..dc4b3dd43e3 100644
--- a/ruby/ql/lib/change-notes/2024-01-22-erb-render-flow.md
+++ b/ruby/ql/lib/change-notes/released/0.8.8.md
@@ -1,4 +1,5 @@
----
-category: minorAnalysis
----
+## 0.8.8
+
+### Minor Analysis Improvements
+
* Flow is now tracked through Rails `render` calls, when the argument is a `ViewComponent`. In this case, data flow is tracked into the accompanying `.html.erb` file.
diff --git a/ruby/ql/lib/codeql-pack.release.yml b/ruby/ql/lib/codeql-pack.release.yml
index 2ef6dc421f3..da0a61b4048 100644
--- a/ruby/ql/lib/codeql-pack.release.yml
+++ b/ruby/ql/lib/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 0.8.7
+lastReleaseVersion: 0.8.8
diff --git a/ruby/ql/lib/qlpack.yml b/ruby/ql/lib/qlpack.yml
index 8179ac53996..7eb6222e101 100644
--- a/ruby/ql/lib/qlpack.yml
+++ b/ruby/ql/lib/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/ruby-all
-version: 0.8.8-dev
+version: 0.8.8
groups: ruby
extractor: ruby
dbscheme: ruby.dbscheme
diff --git a/ruby/ql/src/CHANGELOG.md b/ruby/ql/src/CHANGELOG.md
index 05a89118b05..9eff67dab9e 100644
--- a/ruby/ql/src/CHANGELOG.md
+++ b/ruby/ql/src/CHANGELOG.md
@@ -1,3 +1,14 @@
+## 0.8.8
+
+### New Queries
+
+* Added a new experimental query, `rb/insecure-randomness`, to detect when application uses random values that are not cryptographically secure.
+
+### Minor Analysis Improvements
+
+* Added new unsafe deserialization sinks for the ox gem.
+* Added an additional unsafe deserialization sink for the oj gem.
+
## 0.8.7
No user-facing changes.
diff --git a/ruby/ql/src/change-notes/2023-12-18-insecure-randomness-query.md b/ruby/ql/src/change-notes/2023-12-18-insecure-randomness-query.md
deleted file mode 100644
index a4b3cd5a1f5..00000000000
--- a/ruby/ql/src/change-notes/2023-12-18-insecure-randomness-query.md
+++ /dev/null
@@ -1,4 +0,0 @@
----
-category: newQuery
----
-* Added a new experimental query, `rb/insecure-randomness`, to detect when application uses random values that are not cryptographically secure.
\ No newline at end of file
diff --git a/ruby/ql/src/change-notes/2024-01-30-unsafe-deserialization-sinks.md b/ruby/ql/src/change-notes/2024-01-30-unsafe-deserialization-sinks.md
deleted file mode 100644
index 3ba080e91ab..00000000000
--- a/ruby/ql/src/change-notes/2024-01-30-unsafe-deserialization-sinks.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-category: minorAnalysis
----
-* Added new unsafe deserialization sinks for the ox gem.
-* Added an additional unsafe deserialization sink for the oj gem.
diff --git a/ruby/ql/src/change-notes/released/0.8.8.md b/ruby/ql/src/change-notes/released/0.8.8.md
new file mode 100644
index 00000000000..b8aaed87425
--- /dev/null
+++ b/ruby/ql/src/change-notes/released/0.8.8.md
@@ -0,0 +1,10 @@
+## 0.8.8
+
+### New Queries
+
+* Added a new experimental query, `rb/insecure-randomness`, to detect when application uses random values that are not cryptographically secure.
+
+### Minor Analysis Improvements
+
+* Added new unsafe deserialization sinks for the ox gem.
+* Added an additional unsafe deserialization sink for the oj gem.
diff --git a/ruby/ql/src/codeql-pack.release.yml b/ruby/ql/src/codeql-pack.release.yml
index 2ef6dc421f3..da0a61b4048 100644
--- a/ruby/ql/src/codeql-pack.release.yml
+++ b/ruby/ql/src/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 0.8.7
+lastReleaseVersion: 0.8.8
diff --git a/ruby/ql/src/qlpack.yml b/ruby/ql/src/qlpack.yml
index 6891e0227d3..7c1995c00e5 100644
--- a/ruby/ql/src/qlpack.yml
+++ b/ruby/ql/src/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/ruby-queries
-version: 0.8.8-dev
+version: 0.8.8
groups:
- ruby
- queries
diff --git a/shared/controlflow/CHANGELOG.md b/shared/controlflow/CHANGELOG.md
index 6635db28abc..d72921d34c1 100644
--- a/shared/controlflow/CHANGELOG.md
+++ b/shared/controlflow/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.1.8
+
+No user-facing changes.
+
## 0.1.7
No user-facing changes.
diff --git a/shared/controlflow/change-notes/released/0.1.8.md b/shared/controlflow/change-notes/released/0.1.8.md
new file mode 100644
index 00000000000..5b20b52baf1
--- /dev/null
+++ b/shared/controlflow/change-notes/released/0.1.8.md
@@ -0,0 +1,3 @@
+## 0.1.8
+
+No user-facing changes.
diff --git a/shared/controlflow/codeql-pack.release.yml b/shared/controlflow/codeql-pack.release.yml
index 949d4c64c66..3136ea4a1cc 100644
--- a/shared/controlflow/codeql-pack.release.yml
+++ b/shared/controlflow/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 0.1.7
+lastReleaseVersion: 0.1.8
diff --git a/shared/controlflow/qlpack.yml b/shared/controlflow/qlpack.yml
index c6c4fb5f728..79d4a386cf1 100644
--- a/shared/controlflow/qlpack.yml
+++ b/shared/controlflow/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/controlflow
-version: 0.1.8-dev
+version: 0.1.8
groups: shared
library: true
dependencies:
diff --git a/shared/dataflow/CHANGELOG.md b/shared/dataflow/CHANGELOG.md
index c537cb3bb8e..e9b6c3bc904 100644
--- a/shared/dataflow/CHANGELOG.md
+++ b/shared/dataflow/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.1.8
+
+No user-facing changes.
+
## 0.1.7
No user-facing changes.
diff --git a/shared/dataflow/change-notes/released/0.1.8.md b/shared/dataflow/change-notes/released/0.1.8.md
new file mode 100644
index 00000000000..5b20b52baf1
--- /dev/null
+++ b/shared/dataflow/change-notes/released/0.1.8.md
@@ -0,0 +1,3 @@
+## 0.1.8
+
+No user-facing changes.
diff --git a/shared/dataflow/codeql-pack.release.yml b/shared/dataflow/codeql-pack.release.yml
index 949d4c64c66..3136ea4a1cc 100644
--- a/shared/dataflow/codeql-pack.release.yml
+++ b/shared/dataflow/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 0.1.7
+lastReleaseVersion: 0.1.8
diff --git a/shared/dataflow/qlpack.yml b/shared/dataflow/qlpack.yml
index 91d1454351c..ffb4d0754be 100644
--- a/shared/dataflow/qlpack.yml
+++ b/shared/dataflow/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/dataflow
-version: 0.1.8-dev
+version: 0.1.8
groups: shared
library: true
dependencies:
diff --git a/shared/mad/CHANGELOG.md b/shared/mad/CHANGELOG.md
index 438ce8241a6..35042f79b69 100644
--- a/shared/mad/CHANGELOG.md
+++ b/shared/mad/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.2.8
+
+No user-facing changes.
+
## 0.2.7
No user-facing changes.
diff --git a/shared/mad/change-notes/released/0.2.8.md b/shared/mad/change-notes/released/0.2.8.md
new file mode 100644
index 00000000000..2f8aa0dd21e
--- /dev/null
+++ b/shared/mad/change-notes/released/0.2.8.md
@@ -0,0 +1,3 @@
+## 0.2.8
+
+No user-facing changes.
diff --git a/shared/mad/codeql-pack.release.yml b/shared/mad/codeql-pack.release.yml
index 6d3c0021858..66ad7f587f8 100644
--- a/shared/mad/codeql-pack.release.yml
+++ b/shared/mad/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 0.2.7
+lastReleaseVersion: 0.2.8
diff --git a/shared/mad/qlpack.yml b/shared/mad/qlpack.yml
index 31a8e8b7534..c4eade3b256 100644
--- a/shared/mad/qlpack.yml
+++ b/shared/mad/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/mad
-version: 0.2.8-dev
+version: 0.2.8
groups: shared
library: true
dependencies: null
diff --git a/shared/rangeanalysis/CHANGELOG.md b/shared/rangeanalysis/CHANGELOG.md
index 6f334d57356..9ad1339683f 100644
--- a/shared/rangeanalysis/CHANGELOG.md
+++ b/shared/rangeanalysis/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.0.7
+
+No user-facing changes.
+
## 0.0.6
No user-facing changes.
diff --git a/shared/rangeanalysis/change-notes/released/0.0.7.md b/shared/rangeanalysis/change-notes/released/0.0.7.md
new file mode 100644
index 00000000000..84da6f18c42
--- /dev/null
+++ b/shared/rangeanalysis/change-notes/released/0.0.7.md
@@ -0,0 +1,3 @@
+## 0.0.7
+
+No user-facing changes.
diff --git a/shared/rangeanalysis/codeql-pack.release.yml b/shared/rangeanalysis/codeql-pack.release.yml
index cf398ce02aa..a2a5484910b 100644
--- a/shared/rangeanalysis/codeql-pack.release.yml
+++ b/shared/rangeanalysis/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 0.0.6
+lastReleaseVersion: 0.0.7
diff --git a/shared/rangeanalysis/qlpack.yml b/shared/rangeanalysis/qlpack.yml
index 6317ae4cac4..faa059f069a 100644
--- a/shared/rangeanalysis/qlpack.yml
+++ b/shared/rangeanalysis/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/rangeanalysis
-version: 0.0.7-dev
+version: 0.0.7
groups: shared
library: true
dependencies:
diff --git a/shared/regex/CHANGELOG.md b/shared/regex/CHANGELOG.md
index 267288c38df..bf0aa553157 100644
--- a/shared/regex/CHANGELOG.md
+++ b/shared/regex/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.2.8
+
+No user-facing changes.
+
## 0.2.7
No user-facing changes.
diff --git a/shared/regex/change-notes/released/0.2.8.md b/shared/regex/change-notes/released/0.2.8.md
new file mode 100644
index 00000000000..2f8aa0dd21e
--- /dev/null
+++ b/shared/regex/change-notes/released/0.2.8.md
@@ -0,0 +1,3 @@
+## 0.2.8
+
+No user-facing changes.
diff --git a/shared/regex/codeql-pack.release.yml b/shared/regex/codeql-pack.release.yml
index 6d3c0021858..66ad7f587f8 100644
--- a/shared/regex/codeql-pack.release.yml
+++ b/shared/regex/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 0.2.7
+lastReleaseVersion: 0.2.8
diff --git a/shared/regex/qlpack.yml b/shared/regex/qlpack.yml
index c75c3ca7b2d..57aa69e9629 100644
--- a/shared/regex/qlpack.yml
+++ b/shared/regex/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/regex
-version: 0.2.8-dev
+version: 0.2.8
groups: shared
library: true
dependencies:
diff --git a/shared/ssa/CHANGELOG.md b/shared/ssa/CHANGELOG.md
index 8a920eb7bed..7c9b57d2b8e 100644
--- a/shared/ssa/CHANGELOG.md
+++ b/shared/ssa/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.2.8
+
+No user-facing changes.
+
## 0.2.7
### Minor Analysis Improvements
diff --git a/shared/ssa/change-notes/released/0.2.8.md b/shared/ssa/change-notes/released/0.2.8.md
new file mode 100644
index 00000000000..2f8aa0dd21e
--- /dev/null
+++ b/shared/ssa/change-notes/released/0.2.8.md
@@ -0,0 +1,3 @@
+## 0.2.8
+
+No user-facing changes.
diff --git a/shared/ssa/codeql-pack.release.yml b/shared/ssa/codeql-pack.release.yml
index 6d3c0021858..66ad7f587f8 100644
--- a/shared/ssa/codeql-pack.release.yml
+++ b/shared/ssa/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 0.2.7
+lastReleaseVersion: 0.2.8
diff --git a/shared/ssa/qlpack.yml b/shared/ssa/qlpack.yml
index 92717e37ccb..f47e195b548 100644
--- a/shared/ssa/qlpack.yml
+++ b/shared/ssa/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/ssa
-version: 0.2.8-dev
+version: 0.2.8
groups: shared
library: true
dependencies:
diff --git a/shared/threat-models/CHANGELOG.md b/shared/threat-models/CHANGELOG.md
index ad2e63eb470..8f58f5145db 100644
--- a/shared/threat-models/CHANGELOG.md
+++ b/shared/threat-models/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.0.7
+
+No user-facing changes.
+
## 0.0.6
No user-facing changes.
diff --git a/shared/threat-models/change-notes/released/0.0.7.md b/shared/threat-models/change-notes/released/0.0.7.md
new file mode 100644
index 00000000000..84da6f18c42
--- /dev/null
+++ b/shared/threat-models/change-notes/released/0.0.7.md
@@ -0,0 +1,3 @@
+## 0.0.7
+
+No user-facing changes.
diff --git a/shared/threat-models/codeql-pack.release.yml b/shared/threat-models/codeql-pack.release.yml
index cf398ce02aa..a2a5484910b 100644
--- a/shared/threat-models/codeql-pack.release.yml
+++ b/shared/threat-models/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 0.0.6
+lastReleaseVersion: 0.0.7
diff --git a/shared/threat-models/qlpack.yml b/shared/threat-models/qlpack.yml
index 4fd423016e2..b056dd0d720 100644
--- a/shared/threat-models/qlpack.yml
+++ b/shared/threat-models/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/threat-models
-version: 0.0.7-dev
+version: 0.0.7
library: true
groups: shared
dataExtensions:
diff --git a/shared/tutorial/CHANGELOG.md b/shared/tutorial/CHANGELOG.md
index d89b3171dc6..bc33883a950 100644
--- a/shared/tutorial/CHANGELOG.md
+++ b/shared/tutorial/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.2.8
+
+No user-facing changes.
+
## 0.2.7
No user-facing changes.
diff --git a/shared/tutorial/change-notes/released/0.2.8.md b/shared/tutorial/change-notes/released/0.2.8.md
new file mode 100644
index 00000000000..2f8aa0dd21e
--- /dev/null
+++ b/shared/tutorial/change-notes/released/0.2.8.md
@@ -0,0 +1,3 @@
+## 0.2.8
+
+No user-facing changes.
diff --git a/shared/tutorial/codeql-pack.release.yml b/shared/tutorial/codeql-pack.release.yml
index 6d3c0021858..66ad7f587f8 100644
--- a/shared/tutorial/codeql-pack.release.yml
+++ b/shared/tutorial/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 0.2.7
+lastReleaseVersion: 0.2.8
diff --git a/shared/tutorial/qlpack.yml b/shared/tutorial/qlpack.yml
index 573d2d5e5bd..23525cbfc60 100644
--- a/shared/tutorial/qlpack.yml
+++ b/shared/tutorial/qlpack.yml
@@ -1,7 +1,7 @@
name: codeql/tutorial
description: Library for the CodeQL detective tutorials, helping new users learn to
write CodeQL queries.
-version: 0.2.8-dev
+version: 0.2.8
groups: shared
library: true
warnOnImplicitThis: true
diff --git a/shared/typetracking/CHANGELOG.md b/shared/typetracking/CHANGELOG.md
index b47b17710e8..4c21bc408be 100644
--- a/shared/typetracking/CHANGELOG.md
+++ b/shared/typetracking/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.2.8
+
+No user-facing changes.
+
## 0.2.7
No user-facing changes.
diff --git a/shared/typetracking/change-notes/released/0.2.8.md b/shared/typetracking/change-notes/released/0.2.8.md
new file mode 100644
index 00000000000..2f8aa0dd21e
--- /dev/null
+++ b/shared/typetracking/change-notes/released/0.2.8.md
@@ -0,0 +1,3 @@
+## 0.2.8
+
+No user-facing changes.
diff --git a/shared/typetracking/codeql-pack.release.yml b/shared/typetracking/codeql-pack.release.yml
index 6d3c0021858..66ad7f587f8 100644
--- a/shared/typetracking/codeql-pack.release.yml
+++ b/shared/typetracking/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 0.2.7
+lastReleaseVersion: 0.2.8
diff --git a/shared/typetracking/qlpack.yml b/shared/typetracking/qlpack.yml
index a35e17dee12..09757c9de82 100644
--- a/shared/typetracking/qlpack.yml
+++ b/shared/typetracking/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/typetracking
-version: 0.2.8-dev
+version: 0.2.8
groups: shared
library: true
dependencies:
diff --git a/shared/typos/CHANGELOG.md b/shared/typos/CHANGELOG.md
index 101d57dbad8..2b0bb7d2f75 100644
--- a/shared/typos/CHANGELOG.md
+++ b/shared/typos/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.2.8
+
+No user-facing changes.
+
## 0.2.7
No user-facing changes.
diff --git a/shared/typos/change-notes/released/0.2.8.md b/shared/typos/change-notes/released/0.2.8.md
new file mode 100644
index 00000000000..2f8aa0dd21e
--- /dev/null
+++ b/shared/typos/change-notes/released/0.2.8.md
@@ -0,0 +1,3 @@
+## 0.2.8
+
+No user-facing changes.
diff --git a/shared/typos/codeql-pack.release.yml b/shared/typos/codeql-pack.release.yml
index 6d3c0021858..66ad7f587f8 100644
--- a/shared/typos/codeql-pack.release.yml
+++ b/shared/typos/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 0.2.7
+lastReleaseVersion: 0.2.8
diff --git a/shared/typos/qlpack.yml b/shared/typos/qlpack.yml
index bc2565304e4..4466e61ee0b 100644
--- a/shared/typos/qlpack.yml
+++ b/shared/typos/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/typos
-version: 0.2.8-dev
+version: 0.2.8
groups: shared
library: true
warnOnImplicitThis: true
diff --git a/shared/util/CHANGELOG.md b/shared/util/CHANGELOG.md
index edfa06a5da2..273afd4129b 100644
--- a/shared/util/CHANGELOG.md
+++ b/shared/util/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.2.8
+
+No user-facing changes.
+
## 0.2.7
No user-facing changes.
diff --git a/shared/util/change-notes/released/0.2.8.md b/shared/util/change-notes/released/0.2.8.md
new file mode 100644
index 00000000000..2f8aa0dd21e
--- /dev/null
+++ b/shared/util/change-notes/released/0.2.8.md
@@ -0,0 +1,3 @@
+## 0.2.8
+
+No user-facing changes.
diff --git a/shared/util/codeql-pack.release.yml b/shared/util/codeql-pack.release.yml
index 6d3c0021858..66ad7f587f8 100644
--- a/shared/util/codeql-pack.release.yml
+++ b/shared/util/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 0.2.7
+lastReleaseVersion: 0.2.8
diff --git a/shared/util/qlpack.yml b/shared/util/qlpack.yml
index cddb6cc42f1..ae11a5bf58b 100644
--- a/shared/util/qlpack.yml
+++ b/shared/util/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/util
-version: 0.2.8-dev
+version: 0.2.8
groups: shared
library: true
dependencies: null
diff --git a/shared/yaml/CHANGELOG.md b/shared/yaml/CHANGELOG.md
index c5b3ec6b30e..e2991032640 100644
--- a/shared/yaml/CHANGELOG.md
+++ b/shared/yaml/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.2.8
+
+No user-facing changes.
+
## 0.2.7
No user-facing changes.
diff --git a/shared/yaml/change-notes/released/0.2.8.md b/shared/yaml/change-notes/released/0.2.8.md
new file mode 100644
index 00000000000..2f8aa0dd21e
--- /dev/null
+++ b/shared/yaml/change-notes/released/0.2.8.md
@@ -0,0 +1,3 @@
+## 0.2.8
+
+No user-facing changes.
diff --git a/shared/yaml/codeql-pack.release.yml b/shared/yaml/codeql-pack.release.yml
index 6d3c0021858..66ad7f587f8 100644
--- a/shared/yaml/codeql-pack.release.yml
+++ b/shared/yaml/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 0.2.7
+lastReleaseVersion: 0.2.8
diff --git a/shared/yaml/qlpack.yml b/shared/yaml/qlpack.yml
index 2680ca9cbb9..4d656f79862 100644
--- a/shared/yaml/qlpack.yml
+++ b/shared/yaml/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/yaml
-version: 0.2.8-dev
+version: 0.2.8
groups: shared
library: true
warnOnImplicitThis: true
diff --git a/swift/ql/lib/CHANGELOG.md b/swift/ql/lib/CHANGELOG.md
index f06c4195a35..b69d9b9e9a3 100644
--- a/swift/ql/lib/CHANGELOG.md
+++ b/swift/ql/lib/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.3.8
+
+No user-facing changes.
+
## 0.3.7
### Minor Analysis Improvements
diff --git a/swift/ql/lib/change-notes/released/0.3.8.md b/swift/ql/lib/change-notes/released/0.3.8.md
new file mode 100644
index 00000000000..7e9035d11c1
--- /dev/null
+++ b/swift/ql/lib/change-notes/released/0.3.8.md
@@ -0,0 +1,3 @@
+## 0.3.8
+
+No user-facing changes.
diff --git a/swift/ql/lib/codeql-pack.release.yml b/swift/ql/lib/codeql-pack.release.yml
index 939934ffd00..4aa0b63b207 100644
--- a/swift/ql/lib/codeql-pack.release.yml
+++ b/swift/ql/lib/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 0.3.7
+lastReleaseVersion: 0.3.8
diff --git a/swift/ql/lib/qlpack.yml b/swift/ql/lib/qlpack.yml
index bb5078ca42b..8916abe3bec 100644
--- a/swift/ql/lib/qlpack.yml
+++ b/swift/ql/lib/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/swift-all
-version: 0.3.8-dev
+version: 0.3.8
groups: swift
extractor: swift
dbscheme: swift.dbscheme
diff --git a/swift/ql/src/CHANGELOG.md b/swift/ql/src/CHANGELOG.md
index ff380eb0b97..7fe6e54b241 100644
--- a/swift/ql/src/CHANGELOG.md
+++ b/swift/ql/src/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.3.8
+
+No user-facing changes.
+
## 0.3.7
### New Queries
diff --git a/swift/ql/src/change-notes/released/0.3.8.md b/swift/ql/src/change-notes/released/0.3.8.md
new file mode 100644
index 00000000000..7e9035d11c1
--- /dev/null
+++ b/swift/ql/src/change-notes/released/0.3.8.md
@@ -0,0 +1,3 @@
+## 0.3.8
+
+No user-facing changes.
diff --git a/swift/ql/src/codeql-pack.release.yml b/swift/ql/src/codeql-pack.release.yml
index 939934ffd00..4aa0b63b207 100644
--- a/swift/ql/src/codeql-pack.release.yml
+++ b/swift/ql/src/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 0.3.7
+lastReleaseVersion: 0.3.8
diff --git a/swift/ql/src/qlpack.yml b/swift/ql/src/qlpack.yml
index e61def6dd27..4a8d3d68e74 100644
--- a/swift/ql/src/qlpack.yml
+++ b/swift/ql/src/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/swift-queries
-version: 0.3.8-dev
+version: 0.3.8
groups:
- swift
- queries
From 44fe34a37d32c1eb9403cb09c151bb9bcd3aec1b Mon Sep 17 00:00:00 2001
From: erik-krogh
Date: Tue, 6 Feb 2024 09:20:27 +0100
Subject: [PATCH 061/378] use the correct string type in the tainted-path
examples
---
csharp/ql/src/Security Features/CWE-022/examples/TaintedPath.cs | 2 +-
.../Security Features/CWE-022/examples/TaintedPathGoodFolder.cs | 2 +-
.../CWE-022/examples/TaintedPathGoodNormalize.cs | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/csharp/ql/src/Security Features/CWE-022/examples/TaintedPath.cs b/csharp/ql/src/Security Features/CWE-022/examples/TaintedPath.cs
index c185267a038..4539aed8b88 100644
--- a/csharp/ql/src/Security Features/CWE-022/examples/TaintedPath.cs
+++ b/csharp/ql/src/Security Features/CWE-022/examples/TaintedPath.cs
@@ -6,7 +6,7 @@ public class TaintedPathHandler : IHttpHandler
{
public void ProcessRequest(HttpContext ctx)
{
- String filename = ctx.Request.QueryString["path"];
+ string filename = ctx.Request.QueryString["path"];
// BAD: This could read any file on the filesystem.
ctx.Response.Write(File.ReadAllText(filename));
}
diff --git a/csharp/ql/src/Security Features/CWE-022/examples/TaintedPathGoodFolder.cs b/csharp/ql/src/Security Features/CWE-022/examples/TaintedPathGoodFolder.cs
index 33443abb717..6a3991ac7ad 100644
--- a/csharp/ql/src/Security Features/CWE-022/examples/TaintedPathGoodFolder.cs
+++ b/csharp/ql/src/Security Features/CWE-022/examples/TaintedPathGoodFolder.cs
@@ -6,7 +6,7 @@ public class TaintedPathHandler : IHttpHandler
{
public void ProcessRequest(HttpContext ctx)
{
- String filename = ctx.Request.QueryString["path"];
+ string filename = ctx.Request.QueryString["path"];
string publicFolder = Path.GetFullPath("/home/" + user + "/public");
string filePath = Path.GetFullPath(Path.Combine(publicFolder, filename));
diff --git a/csharp/ql/src/Security Features/CWE-022/examples/TaintedPathGoodNormalize.cs b/csharp/ql/src/Security Features/CWE-022/examples/TaintedPathGoodNormalize.cs
index 939ceffff23..0e31e8b68c9 100644
--- a/csharp/ql/src/Security Features/CWE-022/examples/TaintedPathGoodNormalize.cs
+++ b/csharp/ql/src/Security Features/CWE-022/examples/TaintedPathGoodNormalize.cs
@@ -6,7 +6,7 @@ public class TaintedPathHandler : IHttpHandler
{
public void ProcessRequest(HttpContext ctx)
{
- String filename = ctx.Request.QueryString["path"];
+ string filename = ctx.Request.QueryString["path"];
// GOOD: ensure that the filename has no path separators or parent directory references
if (filename.Contains("..") || filename.Contains("/") || filename.Contains("\\"))
{
From 4e176236e77b1fa4fd19d742cde7567e3bb2037e Mon Sep 17 00:00:00 2001
From: erik-krogh
Date: Tue, 6 Feb 2024 09:21:35 +0100
Subject: [PATCH 062/378] add a definition of user
---
.../Security Features/CWE-022/examples/TaintedPathGoodFolder.cs | 1 +
1 file changed, 1 insertion(+)
diff --git a/csharp/ql/src/Security Features/CWE-022/examples/TaintedPathGoodFolder.cs b/csharp/ql/src/Security Features/CWE-022/examples/TaintedPathGoodFolder.cs
index 6a3991ac7ad..19af394b1c7 100644
--- a/csharp/ql/src/Security Features/CWE-022/examples/TaintedPathGoodFolder.cs
+++ b/csharp/ql/src/Security Features/CWE-022/examples/TaintedPathGoodFolder.cs
@@ -8,6 +8,7 @@ public class TaintedPathHandler : IHttpHandler
{
string filename = ctx.Request.QueryString["path"];
+ string user = ctx.User.Identity.Name;
string publicFolder = Path.GetFullPath("/home/" + user + "/public");
string filePath = Path.GetFullPath(Path.Combine(publicFolder, filename));
From 94b7bda3dcbec3beec45d8dc7ef4eb7834fbb50d Mon Sep 17 00:00:00 2001
From: erik-krogh
Date: Tue, 6 Feb 2024 09:36:30 +0100
Subject: [PATCH 063/378] exclude tagged template literals from
`js/superfluous-trailing-arguments`
---
.../ql/src/LanguageFeatures/SpuriousArguments.ql | 3 ++-
.../LanguageFeatures/SpuriousArguments/tst.js | 11 ++++++++++-
2 files changed, 12 insertions(+), 2 deletions(-)
diff --git a/javascript/ql/src/LanguageFeatures/SpuriousArguments.ql b/javascript/ql/src/LanguageFeatures/SpuriousArguments.ql
index de8f248d2d4..fd3914c9023 100644
--- a/javascript/ql/src/LanguageFeatures/SpuriousArguments.ql
+++ b/javascript/ql/src/LanguageFeatures/SpuriousArguments.ql
@@ -46,7 +46,8 @@ class SpuriousArguments extends Expr {
SpuriousArguments() {
this = invk.getArgument(maxArity(invk)).asExpr() and
- not invk.isIncomplete()
+ not invk.isIncomplete() and
+ not invk.getAstNode() instanceof TaggedTemplateExpr
}
/**
diff --git a/javascript/ql/test/query-tests/LanguageFeatures/SpuriousArguments/tst.js b/javascript/ql/test/query-tests/LanguageFeatures/SpuriousArguments/tst.js
index 13877ff1dda..1caa88564a1 100644
--- a/javascript/ql/test/query-tests/LanguageFeatures/SpuriousArguments/tst.js
+++ b/javascript/ql/test/query-tests/LanguageFeatures/SpuriousArguments/tst.js
@@ -129,4 +129,13 @@ function sum2() {
}
// OK
-sum2(1, 2, 3);
\ No newline at end of file
+sum2(1, 2, 3);
+
+const $ = function (x, arr) {
+ console.log(x, arr);
+};
+
+// OK
+async function tagThing(repoUrl, directory) {
+ await $`git clone ${repoUrl} ${directory}`;
+}
From 6c1e3b1ba60aef87f7fdabc2b1e1586989d0a4e8 Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Tue, 6 Feb 2024 11:02:15 +0000
Subject: [PATCH 064/378] Update cpp/ql/src/change-notes/released/0.9.4.md
---
cpp/ql/src/change-notes/released/0.9.4.md | 1 -
1 file changed, 1 deletion(-)
diff --git a/cpp/ql/src/change-notes/released/0.9.4.md b/cpp/ql/src/change-notes/released/0.9.4.md
index 6525a90f9bb..bc6e71d7054 100644
--- a/cpp/ql/src/change-notes/released/0.9.4.md
+++ b/cpp/ql/src/change-notes/released/0.9.4.md
@@ -6,5 +6,4 @@
* The "Incorrect return-value check for a 'scanf'-like function" query (`cpp/incorrectly-checked-scanf`) no longer reports an alert when an explicit check for EOF is added.
* The "Incorrect return-value check for a 'scanf'-like function" query (`cpp/incorrectly-checked-scanf`) now recognizes more EOF checks.
* The "Potentially uninitialized local variable" query (`cpp/uninitialized-local`) no longer reports an alert when the local variable is used as a qualifier to a static member function call.
-* ```
* The diagnostic query `cpp/diagnostics/successfully-extracted-files` now considers any C/C++ file seen during extraction, even one with some errors, to be extracted / scanned. This affects the Code Scanning UI measure of scanned C/C++ files.
From 33cefabe2771d9b28b42a757698a1f18665d140b Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Tue, 6 Feb 2024 11:05:22 +0000
Subject: [PATCH 065/378] Update cpp/ql/src/CHANGELOG.md
---
cpp/ql/src/CHANGELOG.md | 1 -
1 file changed, 1 deletion(-)
diff --git a/cpp/ql/src/CHANGELOG.md b/cpp/ql/src/CHANGELOG.md
index 68bcdbc5b07..44d00c1d8e4 100644
--- a/cpp/ql/src/CHANGELOG.md
+++ b/cpp/ql/src/CHANGELOG.md
@@ -6,7 +6,6 @@
* The "Incorrect return-value check for a 'scanf'-like function" query (`cpp/incorrectly-checked-scanf`) no longer reports an alert when an explicit check for EOF is added.
* The "Incorrect return-value check for a 'scanf'-like function" query (`cpp/incorrectly-checked-scanf`) now recognizes more EOF checks.
* The "Potentially uninitialized local variable" query (`cpp/uninitialized-local`) no longer reports an alert when the local variable is used as a qualifier to a static member function call.
-* ```
* The diagnostic query `cpp/diagnostics/successfully-extracted-files` now considers any C/C++ file seen during extraction, even one with some errors, to be extracted / scanned. This affects the Code Scanning UI measure of scanned C/C++ files.
## 0.9.3
From 705a37706019c2ba531aa613d34ce42757eacd55 Mon Sep 17 00:00:00 2001
From: Max Schaefer
Date: Mon, 5 Feb 2024 19:52:30 +0000
Subject: [PATCH 066/378] Address review comments.
---
java/ql/lib/change-notes/2024-01-31-new-models.md | 3 ---
java/ql/lib/ext/android.app.model.yml | 1 -
java/ql/lib/ext/java.net.model.yml | 1 -
java/ql/lib/ext/java.nio.file.model.yml | 1 -
java/ql/lib/ext/javax.xml.parsers.model.yml | 6 ------
java/ql/lib/ext/org.apache.http.impl.client.model.yml | 1 -
6 files changed, 13 deletions(-)
delete mode 100644 java/ql/lib/ext/javax.xml.parsers.model.yml
diff --git a/java/ql/lib/change-notes/2024-01-31-new-models.md b/java/ql/lib/change-notes/2024-01-31-new-models.md
index 195c1dd9954..4fbc1b59571 100644
--- a/java/ql/lib/change-notes/2024-01-31-new-models.md
+++ b/java/ql/lib/change-notes/2024-01-31-new-models.md
@@ -3,7 +3,6 @@ category: minorAnalysis
---
* Added models for the following packages:
- * android.app
* java.io
* java.lang
* java.net
@@ -11,11 +10,9 @@ category: minorAnalysis
* java.nio.file
* java.util.zip
* javax.servlet
- * javax.xml.parsers
* kotlin.io
* org.apache.commons.io
* org.apache.hadoop.fs
* org.apache.hadoop.fs.s3a
- * org.apache.http.impl.client
* org.eclipse.jetty.client
* org.gradle.api.file
diff --git a/java/ql/lib/ext/android.app.model.yml b/java/ql/lib/ext/android.app.model.yml
index f70ea2d238c..28b5171c0d7 100644
--- a/java/ql/lib/ext/android.app.model.yml
+++ b/java/ql/lib/ext/android.app.model.yml
@@ -6,7 +6,6 @@ extensions:
- ["android.app", "Activity", True, "bindService", "", "", "Argument[0]", "intent-redirection", "manual"]
- ["android.app", "Activity", True, "bindServiceAsUser", "", "", "Argument[0]", "intent-redirection", "manual"]
- ["android.app", "Activity", True, "setResult", "(int,Intent)", "", "Argument[1]", "pending-intents", "manual"]
- - ["android.app", "Activity", True, "startActivity", "(Intent)", "", "Argument[0]", "intent-redirection", "ai-manual"]
- ["android.app", "Activity", True, "startActivityAsCaller", "", "", "Argument[0]", "intent-redirection", "manual"]
- ["android.app", "Activity", True, "startActivityForResult", "(Intent,int)", "", "Argument[0]", "intent-redirection", "manual"]
- ["android.app", "Activity", True, "startActivityForResult", "(Intent,int,Bundle)", "", "Argument[0]", "intent-redirection", "manual"]
diff --git a/java/ql/lib/ext/java.net.model.yml b/java/ql/lib/ext/java.net.model.yml
index a6dd7fc5ce8..afdf3320b08 100644
--- a/java/ql/lib/ext/java.net.model.yml
+++ b/java/ql/lib/ext/java.net.model.yml
@@ -44,7 +44,6 @@ extensions:
- ["java.net", "InetSocketAddress", True, "InetSocketAddress", "(String,int)", "", "Argument[0]", "Argument[this]", "taint", "ai-manual"]
- ["java.net", "URI", False, "resolve", "(URI)", "", "Argument[this]", "ReturnValue", "taint", "ai-manual"]
- ["java.net", "URI", False, "URI", "(String,String,String,int,String,String,String)", "", "Argument[5]", "Argument[this].SyntheticField[java.net.URI.query]", "taint", "ai-manual"]
- - ["java.net", "URI", False, "URI", "(String,String,String,int,String,String,String)", "", "Argument[4]", "ReturnValue", "taint", "ai-manual"]
- ["java.net", "URI", False, "URI", "(String,String,String)", "", "Argument[1]", "ReturnValue", "taint", "ai-manual"]
- ["java.net", "URI", False, "URI", "(String)", "", "Argument[0]", "Argument[this]", "taint", "manual"]
- ["java.net", "URI", False, "create", "", "", "Argument[0]", "ReturnValue", "taint", "manual"]
diff --git a/java/ql/lib/ext/java.nio.file.model.yml b/java/ql/lib/ext/java.nio.file.model.yml
index ea32fa75fe3..f41cbf3a3e9 100644
--- a/java/ql/lib/ext/java.nio.file.model.yml
+++ b/java/ql/lib/ext/java.nio.file.model.yml
@@ -82,7 +82,6 @@ extensions:
- ["java.nio.file", "Path", False, "toFile", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- ["java.nio.file", "Path", True, "toString", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- ["java.nio.file", "Path", True, "toUri", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- - ["java.nio.file", "Paths", False, "get", "(String,String[])", "", "Argument[1]", "ReturnValue", "taint", "ai-manual"]
- ["java.nio.file", "Paths", True, "get", "", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["java.nio.file", "Paths", True, "get", "", "", "Argument[1].ArrayElement", "ReturnValue", "taint", "manual"]
# Not supported by current lambda flow
diff --git a/java/ql/lib/ext/javax.xml.parsers.model.yml b/java/ql/lib/ext/javax.xml.parsers.model.yml
deleted file mode 100644
index d39a28f5942..00000000000
--- a/java/ql/lib/ext/javax.xml.parsers.model.yml
+++ /dev/null
@@ -1,6 +0,0 @@
-extensions:
- - addsTo:
- pack: codeql/java-all
- extensible: sinkModel
- data:
- - ["javax.xml.parsers", "DocumentBuilder", True, "parse", "(InputSource)", "", "Argument[0]", "xxe", "ai-manual"]
diff --git a/java/ql/lib/ext/org.apache.http.impl.client.model.yml b/java/ql/lib/ext/org.apache.http.impl.client.model.yml
index 6f407ac3682..be517e5344f 100644
--- a/java/ql/lib/ext/org.apache.http.impl.client.model.yml
+++ b/java/ql/lib/ext/org.apache.http.impl.client.model.yml
@@ -3,5 +3,4 @@ extensions:
pack: codeql/java-all
extensible: sinkModel
data:
- - ["org.apache.http.impl.client", "CloseableHttpClient", True, "execute", "(HttpUriRequest)", "", "Argument[0]", "request-forgery", "ai-manual"]
- ["org.apache.http.impl.client", "RequestWrapper", True, "setURI", "(URI)", "", "Argument[0]", "request-forgery", "hq-manual"]
From b5139078d0befed44d9147f01862bb7d8a454ab9 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
Date: Tue, 6 Feb 2024 19:22:35 +0000
Subject: [PATCH 067/378] Post-release preparation for codeql-cli-2.16.2
---
cpp/ql/lib/qlpack.yml | 2 +-
cpp/ql/src/qlpack.yml | 2 +-
csharp/ql/campaigns/Solorigate/lib/qlpack.yml | 2 +-
csharp/ql/campaigns/Solorigate/src/qlpack.yml | 2 +-
csharp/ql/lib/qlpack.yml | 2 +-
csharp/ql/src/qlpack.yml | 2 +-
go/ql/consistency-queries/qlpack.yml | 2 +-
go/ql/lib/qlpack.yml | 2 +-
go/ql/src/qlpack.yml | 2 +-
java/ql/automodel/src/qlpack.yml | 2 +-
java/ql/lib/qlpack.yml | 2 +-
java/ql/src/qlpack.yml | 2 +-
javascript/ql/lib/qlpack.yml | 2 +-
javascript/ql/src/qlpack.yml | 2 +-
misc/suite-helpers/qlpack.yml | 2 +-
python/ql/lib/qlpack.yml | 2 +-
python/ql/src/qlpack.yml | 2 +-
ruby/ql/lib/qlpack.yml | 2 +-
ruby/ql/src/qlpack.yml | 2 +-
shared/controlflow/qlpack.yml | 2 +-
shared/dataflow/qlpack.yml | 2 +-
shared/mad/qlpack.yml | 2 +-
shared/rangeanalysis/qlpack.yml | 2 +-
shared/regex/qlpack.yml | 2 +-
shared/ssa/qlpack.yml | 2 +-
shared/threat-models/qlpack.yml | 2 +-
shared/tutorial/qlpack.yml | 2 +-
shared/typetracking/qlpack.yml | 2 +-
shared/typos/qlpack.yml | 2 +-
shared/util/qlpack.yml | 2 +-
shared/yaml/qlpack.yml | 2 +-
swift/ql/lib/qlpack.yml | 2 +-
swift/ql/src/qlpack.yml | 2 +-
33 files changed, 33 insertions(+), 33 deletions(-)
diff --git a/cpp/ql/lib/qlpack.yml b/cpp/ql/lib/qlpack.yml
index b1b4172e977..7615b6bac2f 100644
--- a/cpp/ql/lib/qlpack.yml
+++ b/cpp/ql/lib/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/cpp-all
-version: 0.12.5
+version: 0.12.6-dev
groups: cpp
dbscheme: semmlecode.cpp.dbscheme
extractor: cpp
diff --git a/cpp/ql/src/qlpack.yml b/cpp/ql/src/qlpack.yml
index 0da41987b3e..9151201a137 100644
--- a/cpp/ql/src/qlpack.yml
+++ b/cpp/ql/src/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/cpp-queries
-version: 0.9.4
+version: 0.9.5-dev
groups:
- cpp
- queries
diff --git a/csharp/ql/campaigns/Solorigate/lib/qlpack.yml b/csharp/ql/campaigns/Solorigate/lib/qlpack.yml
index 77b1c8b5154..8466748a25b 100644
--- a/csharp/ql/campaigns/Solorigate/lib/qlpack.yml
+++ b/csharp/ql/campaigns/Solorigate/lib/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/csharp-solorigate-all
-version: 1.7.8
+version: 1.7.9-dev
groups:
- csharp
- solorigate
diff --git a/csharp/ql/campaigns/Solorigate/src/qlpack.yml b/csharp/ql/campaigns/Solorigate/src/qlpack.yml
index 9851e27c691..ff72db938e0 100644
--- a/csharp/ql/campaigns/Solorigate/src/qlpack.yml
+++ b/csharp/ql/campaigns/Solorigate/src/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/csharp-solorigate-queries
-version: 1.7.8
+version: 1.7.9-dev
groups:
- csharp
- solorigate
diff --git a/csharp/ql/lib/qlpack.yml b/csharp/ql/lib/qlpack.yml
index 2b137281da6..2e576e11b11 100644
--- a/csharp/ql/lib/qlpack.yml
+++ b/csharp/ql/lib/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/csharp-all
-version: 0.8.8
+version: 0.8.9-dev
groups: csharp
dbscheme: semmlecode.csharp.dbscheme
extractor: csharp
diff --git a/csharp/ql/src/qlpack.yml b/csharp/ql/src/qlpack.yml
index a16c72edd72..018c3e09ae3 100644
--- a/csharp/ql/src/qlpack.yml
+++ b/csharp/ql/src/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/csharp-queries
-version: 0.8.8
+version: 0.8.9-dev
groups:
- csharp
- queries
diff --git a/go/ql/consistency-queries/qlpack.yml b/go/ql/consistency-queries/qlpack.yml
index c7522dd8e35..651e694d964 100644
--- a/go/ql/consistency-queries/qlpack.yml
+++ b/go/ql/consistency-queries/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql-go-consistency-queries
-version: 0.0.7
+version: 0.0.8-dev
groups:
- go
- queries
diff --git a/go/ql/lib/qlpack.yml b/go/ql/lib/qlpack.yml
index 5f317377d45..920594fe6ec 100644
--- a/go/ql/lib/qlpack.yml
+++ b/go/ql/lib/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/go-all
-version: 0.7.8
+version: 0.7.9-dev
groups: go
dbscheme: go.dbscheme
extractor: go
diff --git a/go/ql/src/qlpack.yml b/go/ql/src/qlpack.yml
index 81654540219..fb73fa0eb96 100644
--- a/go/ql/src/qlpack.yml
+++ b/go/ql/src/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/go-queries
-version: 0.7.8
+version: 0.7.9-dev
groups:
- go
- queries
diff --git a/java/ql/automodel/src/qlpack.yml b/java/ql/automodel/src/qlpack.yml
index 3334223e9e4..1f68ac3f6ba 100644
--- a/java/ql/automodel/src/qlpack.yml
+++ b/java/ql/automodel/src/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/java-automodel-queries
-version: 0.0.14
+version: 0.0.15-dev
groups:
- java
- automodel
diff --git a/java/ql/lib/qlpack.yml b/java/ql/lib/qlpack.yml
index 6e4e1269d9c..cadcc1c9be6 100644
--- a/java/ql/lib/qlpack.yml
+++ b/java/ql/lib/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/java-all
-version: 0.8.8
+version: 0.8.9-dev
groups: java
dbscheme: config/semmlecode.dbscheme
extractor: java
diff --git a/java/ql/src/qlpack.yml b/java/ql/src/qlpack.yml
index 73e8a062ffe..cad99f4d9c4 100644
--- a/java/ql/src/qlpack.yml
+++ b/java/ql/src/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/java-queries
-version: 0.8.8
+version: 0.8.9-dev
groups:
- java
- queries
diff --git a/javascript/ql/lib/qlpack.yml b/javascript/ql/lib/qlpack.yml
index fa544548ea7..2be9a1ed2bd 100644
--- a/javascript/ql/lib/qlpack.yml
+++ b/javascript/ql/lib/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/javascript-all
-version: 0.8.8
+version: 0.8.9-dev
groups: javascript
dbscheme: semmlecode.javascript.dbscheme
extractor: javascript
diff --git a/javascript/ql/src/qlpack.yml b/javascript/ql/src/qlpack.yml
index 1ebbfc58787..545be6f2c61 100644
--- a/javascript/ql/src/qlpack.yml
+++ b/javascript/ql/src/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/javascript-queries
-version: 0.8.8
+version: 0.8.9-dev
groups:
- javascript
- queries
diff --git a/misc/suite-helpers/qlpack.yml b/misc/suite-helpers/qlpack.yml
index 4db5dfcf454..6b20374ae33 100644
--- a/misc/suite-helpers/qlpack.yml
+++ b/misc/suite-helpers/qlpack.yml
@@ -1,4 +1,4 @@
name: codeql/suite-helpers
-version: 0.7.8
+version: 0.7.9-dev
groups: shared
warnOnImplicitThis: true
diff --git a/python/ql/lib/qlpack.yml b/python/ql/lib/qlpack.yml
index a2c343cca3f..94f82195d5b 100644
--- a/python/ql/lib/qlpack.yml
+++ b/python/ql/lib/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/python-all
-version: 0.11.8
+version: 0.11.9-dev
groups: python
dbscheme: semmlecode.python.dbscheme
extractor: python
diff --git a/python/ql/src/qlpack.yml b/python/ql/src/qlpack.yml
index 538e5ad799c..c5335da22f3 100644
--- a/python/ql/src/qlpack.yml
+++ b/python/ql/src/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/python-queries
-version: 0.9.8
+version: 0.9.9-dev
groups:
- python
- queries
diff --git a/ruby/ql/lib/qlpack.yml b/ruby/ql/lib/qlpack.yml
index 7eb6222e101..6c55331de90 100644
--- a/ruby/ql/lib/qlpack.yml
+++ b/ruby/ql/lib/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/ruby-all
-version: 0.8.8
+version: 0.8.9-dev
groups: ruby
extractor: ruby
dbscheme: ruby.dbscheme
diff --git a/ruby/ql/src/qlpack.yml b/ruby/ql/src/qlpack.yml
index 7c1995c00e5..3637a80df7f 100644
--- a/ruby/ql/src/qlpack.yml
+++ b/ruby/ql/src/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/ruby-queries
-version: 0.8.8
+version: 0.8.9-dev
groups:
- ruby
- queries
diff --git a/shared/controlflow/qlpack.yml b/shared/controlflow/qlpack.yml
index 79d4a386cf1..c7a88e50611 100644
--- a/shared/controlflow/qlpack.yml
+++ b/shared/controlflow/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/controlflow
-version: 0.1.8
+version: 0.1.9-dev
groups: shared
library: true
dependencies:
diff --git a/shared/dataflow/qlpack.yml b/shared/dataflow/qlpack.yml
index ffb4d0754be..c14ef815d58 100644
--- a/shared/dataflow/qlpack.yml
+++ b/shared/dataflow/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/dataflow
-version: 0.1.8
+version: 0.1.9-dev
groups: shared
library: true
dependencies:
diff --git a/shared/mad/qlpack.yml b/shared/mad/qlpack.yml
index c4eade3b256..0b3830f888d 100644
--- a/shared/mad/qlpack.yml
+++ b/shared/mad/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/mad
-version: 0.2.8
+version: 0.2.9-dev
groups: shared
library: true
dependencies: null
diff --git a/shared/rangeanalysis/qlpack.yml b/shared/rangeanalysis/qlpack.yml
index faa059f069a..0f5272dd8cf 100644
--- a/shared/rangeanalysis/qlpack.yml
+++ b/shared/rangeanalysis/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/rangeanalysis
-version: 0.0.7
+version: 0.0.8-dev
groups: shared
library: true
dependencies:
diff --git a/shared/regex/qlpack.yml b/shared/regex/qlpack.yml
index 57aa69e9629..eca67311c9c 100644
--- a/shared/regex/qlpack.yml
+++ b/shared/regex/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/regex
-version: 0.2.8
+version: 0.2.9-dev
groups: shared
library: true
dependencies:
diff --git a/shared/ssa/qlpack.yml b/shared/ssa/qlpack.yml
index f47e195b548..b5d30380815 100644
--- a/shared/ssa/qlpack.yml
+++ b/shared/ssa/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/ssa
-version: 0.2.8
+version: 0.2.9-dev
groups: shared
library: true
dependencies:
diff --git a/shared/threat-models/qlpack.yml b/shared/threat-models/qlpack.yml
index b056dd0d720..eb345ecca9a 100644
--- a/shared/threat-models/qlpack.yml
+++ b/shared/threat-models/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/threat-models
-version: 0.0.7
+version: 0.0.8-dev
library: true
groups: shared
dataExtensions:
diff --git a/shared/tutorial/qlpack.yml b/shared/tutorial/qlpack.yml
index 23525cbfc60..e3080bb33b5 100644
--- a/shared/tutorial/qlpack.yml
+++ b/shared/tutorial/qlpack.yml
@@ -1,7 +1,7 @@
name: codeql/tutorial
description: Library for the CodeQL detective tutorials, helping new users learn to
write CodeQL queries.
-version: 0.2.8
+version: 0.2.9-dev
groups: shared
library: true
warnOnImplicitThis: true
diff --git a/shared/typetracking/qlpack.yml b/shared/typetracking/qlpack.yml
index 09757c9de82..adf375fd0c3 100644
--- a/shared/typetracking/qlpack.yml
+++ b/shared/typetracking/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/typetracking
-version: 0.2.8
+version: 0.2.9-dev
groups: shared
library: true
dependencies:
diff --git a/shared/typos/qlpack.yml b/shared/typos/qlpack.yml
index 4466e61ee0b..927514b2fe4 100644
--- a/shared/typos/qlpack.yml
+++ b/shared/typos/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/typos
-version: 0.2.8
+version: 0.2.9-dev
groups: shared
library: true
warnOnImplicitThis: true
diff --git a/shared/util/qlpack.yml b/shared/util/qlpack.yml
index ae11a5bf58b..72537a48107 100644
--- a/shared/util/qlpack.yml
+++ b/shared/util/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/util
-version: 0.2.8
+version: 0.2.9-dev
groups: shared
library: true
dependencies: null
diff --git a/shared/yaml/qlpack.yml b/shared/yaml/qlpack.yml
index 4d656f79862..fae3aad1324 100644
--- a/shared/yaml/qlpack.yml
+++ b/shared/yaml/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/yaml
-version: 0.2.8
+version: 0.2.9-dev
groups: shared
library: true
warnOnImplicitThis: true
diff --git a/swift/ql/lib/qlpack.yml b/swift/ql/lib/qlpack.yml
index 8916abe3bec..2c58adec21e 100644
--- a/swift/ql/lib/qlpack.yml
+++ b/swift/ql/lib/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/swift-all
-version: 0.3.8
+version: 0.3.9-dev
groups: swift
extractor: swift
dbscheme: swift.dbscheme
diff --git a/swift/ql/src/qlpack.yml b/swift/ql/src/qlpack.yml
index 4a8d3d68e74..00ff9a6f163 100644
--- a/swift/ql/src/qlpack.yml
+++ b/swift/ql/src/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/swift-queries
-version: 0.3.8
+version: 0.3.9-dev
groups:
- swift
- queries
From 1484a169d743bec1bf3d08b390ddc5c1efad16c5 Mon Sep 17 00:00:00 2001
From: Jonathan Leitschuh
Date: Tue, 6 Feb 2024 15:43:19 -0500
Subject: [PATCH 068/378] Reduce severity of `java/relative-path-command`
Significantly reduces the severity of `java/relative-path-command` from 9.8 to 5.4
https://www.first.org/cvss/calculator/4.0#CVSS:4.0/AV:L/AC:L/AT:P/PR:H/UI:P/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N
---
java/ql/src/Security/CWE/CWE-078/ExecRelative.ql | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/java/ql/src/Security/CWE/CWE-078/ExecRelative.ql b/java/ql/src/Security/CWE/CWE-078/ExecRelative.ql
index 501826c6426..533980a3f0a 100644
--- a/java/ql/src/Security/CWE/CWE-078/ExecRelative.ql
+++ b/java/ql/src/Security/CWE/CWE-078/ExecRelative.ql
@@ -4,7 +4,7 @@
* malicious changes in the PATH environment variable.
* @kind problem
* @problem.severity warning
- * @security-severity 9.8
+ * @security-severity 5.4
* @precision medium
* @id java/relative-path-command
* @tags security
From b8dbb8c866a7f468681ccb6f14e6d02b0842f8ce Mon Sep 17 00:00:00 2001
From: Tamas Vajk
Date: Wed, 7 Feb 2024 10:26:09 +0100
Subject: [PATCH 069/378] C# Add missing Windows Forms implicit usings
---
.../DependencyManager.cs | 13 +++++++
.../FileContent.cs | 30 ++++++++++++++++
.../Semmle.Extraction.Tests/FileContent.cs | 36 +++++++++++++++----
3 files changed, 73 insertions(+), 6 deletions(-)
diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DependencyManager.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DependencyManager.cs
index 13f8cc4e2a5..cd071c307bf 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DependencyManager.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DependencyManager.cs
@@ -116,8 +116,16 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
bool.TryParse(webViewExtractionOption, out var shouldExtractWebViews) &&
shouldExtractWebViews)
{
+ CompilationInfos.Add(("WebView extraction enabled", "1"));
GenerateSourceFilesFromWebViews(allNonBinaryFiles);
}
+ else
+ {
+ CompilationInfos.Add(("WebView extraction enabled", "0"));
+ }
+
+ CompilationInfos.Add(("UseWPF set", fileContent.UseWpf ? "1" : "0"));
+ CompilationInfos.Add(("UseWindowsForms set", fileContent.UseWindowsForms ? "1" : "0"));
GenerateSourceFileFromImplicitUsings();
@@ -434,6 +442,11 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
"Microsoft.Extensions.DependencyInjection", "Microsoft.Extensions.Hosting", "Microsoft.Extensions.Logging" });
}
+ if (fileContent.UseWindowsForms)
+ {
+ usings.UnionWith(new[] { "System.Drawing", "System.Windows.Forms" });
+ }
+
usings.UnionWith(fileContent.CustomImplicitUsings);
logger.LogInfo($"Generating source file for implicit usings. Namespaces: {string.Join(", ", usings.OrderBy(u => u))}");
diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FileContent.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FileContent.cs
index c06eaec270f..03f448e7330 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FileContent.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FileContent.cs
@@ -61,6 +61,28 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
}
}
+ private bool useWpf = false;
+
+ public bool UseWpf
+ {
+ get
+ {
+ initialize.Run();
+ return useWpf;
+ }
+ }
+
+ private bool useWindowsForms = false;
+
+ public bool UseWindowsForms
+ {
+ get
+ {
+ initialize.Run();
+ return useWindowsForms;
+ }
+ }
+
private bool isLegacyProjectStructureUsed = false;
public bool IsLegacyProjectStructureUsed
@@ -173,6 +195,14 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|| line.Contains("enable".AsSpan(), StringComparison.Ordinal)
|| line.Contains("true".AsSpan(), StringComparison.Ordinal);
+ // Determine if WPF is used.
+ useWpf = useWpf
+ || line.Contains("true".AsSpan(), StringComparison.Ordinal);
+
+ // Determine if Windows Forms is used.
+ useWindowsForms = useWindowsForms
+ || line.Contains("true".AsSpan(), StringComparison.Ordinal);
+
// Find all custom implicit usings.
foreach (var valueMatch in CustomImplicitUsingDeclarations().EnumerateMatches(line))
{
diff --git a/csharp/extractor/Semmle.Extraction.Tests/FileContent.cs b/csharp/extractor/Semmle.Extraction.Tests/FileContent.cs
index ba934120c45..6dcee31024a 100644
--- a/csharp/extractor/Semmle.Extraction.Tests/FileContent.cs
+++ b/csharp/extractor/Semmle.Extraction.Tests/FileContent.cs
@@ -84,7 +84,7 @@ namespace Semmle.Extraction.Tests
Assert.Contains("StyleCop.Analyzers".ToLowerInvariant(), allPackages);
}
- private static void ImplicitUsingsTest(string line, bool expected)
+ private static void CsProjSettingsTest(string line, bool expected, Func func)
{
// Setup
var lines = new List()
@@ -94,28 +94,52 @@ namespace Semmle.Extraction.Tests
var fileContent = new TestFileContent(lines);
// Execute
- var useImplicitUsings = fileContent.UseImplicitUsings;
+ var actual = func(fileContent);
// Verify
- Assert.Equal(expected, useImplicitUsings);
+ Assert.Equal(expected, actual);
}
[Fact]
public void TestFileContent_ImplicitUsings0()
{
- ImplicitUsingsTest("false", false);
+ CsProjSettingsTest("false", false, fc => fc.UseImplicitUsings);
}
[Fact]
public void TestFileContent_ImplicitUsings1()
{
- ImplicitUsingsTest("true", true);
+ CsProjSettingsTest("true", true, fc => fc.UseImplicitUsings);
}
[Fact]
public void TestFileContent_ImplicitUsings2()
{
- ImplicitUsingsTest("enable", true);
+ CsProjSettingsTest("enable", true, fc => fc.UseImplicitUsings);
+ }
+
+ [Fact]
+ public void TestFileContent_UseWpf0()
+ {
+ CsProjSettingsTest("false", false, fc => fc.UseWpf);
+ }
+
+ [Fact]
+ public void TestFileContent_UseWpf1()
+ {
+ CsProjSettingsTest("true", true, fc => fc.UseWpf);
+ }
+
+ [Fact]
+ public void TestFileContent_UseWindowsForms0()
+ {
+ CsProjSettingsTest("false", false, fc => fc.UseWindowsForms);
+ }
+
+ [Fact]
+ public void TestFileContent_UseWindowsForms1()
+ {
+ CsProjSettingsTest("true", true, fc => fc.UseWindowsForms);
}
[Fact]
From 082754a3d8dd3a3a8d550bc5e1b150ea83f51717 Mon Sep 17 00:00:00 2001
From: Max Schaefer
Date: Wed, 7 Feb 2024 13:21:59 +0000
Subject: [PATCH 070/378] Remove problematic Kotlin model.
---
java/ql/lib/change-notes/2024-01-31-new-models.md | 1 -
java/ql/lib/ext/kotlin.io.model.yml | 1 -
2 files changed, 2 deletions(-)
diff --git a/java/ql/lib/change-notes/2024-01-31-new-models.md b/java/ql/lib/change-notes/2024-01-31-new-models.md
index 4fbc1b59571..bdb588f3bc3 100644
--- a/java/ql/lib/change-notes/2024-01-31-new-models.md
+++ b/java/ql/lib/change-notes/2024-01-31-new-models.md
@@ -10,7 +10,6 @@ category: minorAnalysis
* java.nio.file
* java.util.zip
* javax.servlet
- * kotlin.io
* org.apache.commons.io
* org.apache.hadoop.fs
* org.apache.hadoop.fs.s3a
diff --git a/java/ql/lib/ext/kotlin.io.model.yml b/java/ql/lib/ext/kotlin.io.model.yml
index c65862f6eac..b748e04a292 100644
--- a/java/ql/lib/ext/kotlin.io.model.yml
+++ b/java/ql/lib/ext/kotlin.io.model.yml
@@ -3,7 +3,6 @@ extensions:
pack: codeql/java-all
extensible: sinkModel
data:
- - ["kotlin.io", "FilesKt", False, "appendText$default", "(File,String,Charset,int,Object)", "", "Argument[0]", "path-injection", "ai-manual"]
- ["kotlin.io", "FilesKt", False, "deleteRecursively", "(File)", "", "Argument[0]", "path-injection", "ai-manual"]
- ["kotlin.io", "FilesKt", False, "inputStream", "(File)", "", "Argument[0]", "path-injection", "ai-manual"]
- ["kotlin.io", "FilesKt", False, "readBytes", "(File)", "", "Argument[0]", "path-injection", "ai-manual"]
From 9ce75dac0e540545d61dbb9c1af9bf1dab9d5971 Mon Sep 17 00:00:00 2001
From: Maiky <76447395+maikypedia@users.noreply.github.com>
Date: Wed, 7 Feb 2024 14:26:56 +0100
Subject: [PATCH 071/378] Update UnsafeUnpackQuery.qll
---
swift/ql/lib/codeql/swift/security/UnsafeUnpackQuery.qll | 1 -
1 file changed, 1 deletion(-)
diff --git a/swift/ql/lib/codeql/swift/security/UnsafeUnpackQuery.qll b/swift/ql/lib/codeql/swift/security/UnsafeUnpackQuery.qll
index dbc0f733b52..59be3a7eb31 100644
--- a/swift/ql/lib/codeql/swift/security/UnsafeUnpackQuery.qll
+++ b/swift/ql/lib/codeql/swift/security/UnsafeUnpackQuery.qll
@@ -5,7 +5,6 @@
*/
import swift
-import codeql.swift.dataflow.DataFlow
import codeql.swift.dataflow.TaintTracking
import codeql.swift.dataflow.FlowSources
import codeql.swift.security.UnsafeUnpackExtensions
From c6fb303d63fa6ba3fef9efcc1a312e0d86ecbe4b Mon Sep 17 00:00:00 2001
From: Maiky <76447395+maikypedia@users.noreply.github.com>
Date: Wed, 7 Feb 2024 14:27:40 +0100
Subject: [PATCH 072/378] Suggested changes
Co-authored-by: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
---
.../src/experimental/Security/CWE-022/UnsafeUnpack.qhelp | 2 +-
.../ql/src/experimental/Security/CWE-022/UnsafeUnpack.ql | 8 ++++----
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/swift/ql/src/experimental/Security/CWE-022/UnsafeUnpack.qhelp b/swift/ql/src/experimental/Security/CWE-022/UnsafeUnpack.qhelp
index df162180c53..2f65296b9a8 100644
--- a/swift/ql/src/experimental/Security/CWE-022/UnsafeUnpack.qhelp
+++ b/swift/ql/src/experimental/Security/CWE-022/UnsafeUnpack.qhelp
@@ -31,7 +31,7 @@ The following examples unpacks a remote zip using `fileManager.unzipItem()` whic
Consider using a safer module, such as: ZIPArchive
-
+
diff --git a/swift/ql/src/experimental/Security/CWE-022/UnsafeUnpack.ql b/swift/ql/src/experimental/Security/CWE-022/UnsafeUnpack.ql
index c50cc6c3b4f..e455a1b2d16 100644
--- a/swift/ql/src/experimental/Security/CWE-022/UnsafeUnpack.ql
+++ b/swift/ql/src/experimental/Security/CWE-022/UnsafeUnpack.ql
@@ -1,11 +1,11 @@
/**
* @name Arbitrary file write during a zip extraction from a user controlled source
- * @description Unpacking user controlled zips without validating if destination path file
- * is within the destination directory can cause files outside
- * the destination directory to be overwritten.
+ * @description Unpacking user controlled zips without validating whether the
+ * destination file path is within the destination directory can cause files
+ * outside the destination directory to be overwritten.
* @kind path-problem
* @problem.severity error
- * @security-severity 9.8
+ * @security-severity 7.5
* @precision high
* @id swift/unsafe-unpacking
* @tags security
From 7fb72ea81f4397063530b519f6f99e2844719703 Mon Sep 17 00:00:00 2001
From: Maiky <76447395+maikypedia@users.noreply.github.com>
Date: Wed, 7 Feb 2024 14:30:16 +0100
Subject: [PATCH 073/378] Redundant import
---
swift/ql/src/experimental/Security/CWE-022/UnsafeUnpack.ql | 1 -
1 file changed, 1 deletion(-)
diff --git a/swift/ql/src/experimental/Security/CWE-022/UnsafeUnpack.ql b/swift/ql/src/experimental/Security/CWE-022/UnsafeUnpack.ql
index e455a1b2d16..be7f4cfd084 100644
--- a/swift/ql/src/experimental/Security/CWE-022/UnsafeUnpack.ql
+++ b/swift/ql/src/experimental/Security/CWE-022/UnsafeUnpack.ql
@@ -14,7 +14,6 @@
*/
import swift
-import codeql.swift.dataflow.DataFlow
import codeql.swift.security.UnsafeUnpackQuery
import UnsafeUnpackFlow::PathGraph
From 7c0f80ff7d4d01908c3a20d30154a61a46cd898b Mon Sep 17 00:00:00 2001
From: Maiky <76447395+maikypedia@users.noreply.github.com>
Date: Wed, 7 Feb 2024 14:32:42 +0100
Subject: [PATCH 074/378] Apply suggestions from code review
Co-authored-by: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
---
.../Security/CWE-022-Unsafe-Unpack/UnsafeUnpack.swift | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/swift/ql/test/query-tests/Security/CWE-022-Unsafe-Unpack/UnsafeUnpack.swift b/swift/ql/test/query-tests/Security/CWE-022-Unsafe-Unpack/UnsafeUnpack.swift
index 2f599b89150..5d7dc6c58b4 100644
--- a/swift/ql/test/query-tests/Security/CWE-022-Unsafe-Unpack/UnsafeUnpack.swift
+++ b/swift/ql/test/query-tests/Security/CWE-022-Unsafe-Unpack/UnsafeUnpack.swift
@@ -52,15 +52,15 @@ extension String {
// --- tests ---
func testCommandInjectionQhelpExamples() {
- guard let remoteURL = URL(string: "https://example.com/") else {
- return
- }
+ guard let remoteURL = URL(string: "https://example.com/") else {
+ return
+ }
let source = URL(fileURLWithPath: "/sourcePath")
let destination = URL(fileURLWithPath: "/destination")
try Data(contentsOf: remoteURL, options: []).write(to: source)
- do {
+ do {
try Zip.unzipFile(source, destination: destination, overwrite: true, password: nil) // BAD
let fileManager = FileManager()
From 8646bffaea94302d4b42050919d18aaab385fa78 Mon Sep 17 00:00:00 2001
From: Koen Vlaswinkel
Date: Wed, 7 Feb 2024 14:35:19 +0100
Subject: [PATCH 075/378] Ruby: Remove `ReturnValue` as access path for
constructors
---
.../utils/modeleditor/FrameworkModeAccessPaths.ql | 4 +++-
ruby/ql/src/utils/modeleditor/ModelEditor.qll | 13 +++++++++----
2 files changed, 12 insertions(+), 5 deletions(-)
diff --git a/ruby/ql/src/utils/modeleditor/FrameworkModeAccessPaths.ql b/ruby/ql/src/utils/modeleditor/FrameworkModeAccessPaths.ql
index d992f1c46f8..87559350048 100644
--- a/ruby/ql/src/utils/modeleditor/FrameworkModeAccessPaths.ql
+++ b/ruby/ql/src/utils/modeleditor/FrameworkModeAccessPaths.ql
@@ -10,6 +10,7 @@ private import ruby
private import codeql.ruby.AST
private import codeql.ruby.ApiGraphs
private import queries.modeling.internal.Util as Util
+private import ModelEditor
predicate simpleParameters(string type, string path, string value, DataFlow::Node node) {
exists(DataFlow::MethodNode methodNode, DataFlow::ParameterNode paramNode |
@@ -58,7 +59,8 @@ predicate blockArguments(string type, string path, string value, DataFlow::Node
predicate returnValue(string type, string path, string value, DataFlow::Node node) {
exists(DataFlow::MethodNode methodNode, DataFlow::Node returnNode |
methodNode.getLocation().getFile() instanceof Util::RelevantFile and
- returnNode = methodNode.getAReturnNode()
+ returnNode = methodNode.getAReturnNode() and
+ not isConstructor(methodNode) // A constructor doesn't have a return value
|
Util::pathToMethod(methodNode, type, path) and
value = "ReturnValue" and
diff --git a/ruby/ql/src/utils/modeleditor/ModelEditor.qll b/ruby/ql/src/utils/modeleditor/ModelEditor.qll
index 625d40fd501..020a5f6177c 100644
--- a/ruby/ql/src/utils/modeleditor/ModelEditor.qll
+++ b/ruby/ql/src/utils/modeleditor/ModelEditor.qll
@@ -32,6 +32,14 @@ string getNamespace(File file) {
)
}
+/**
+ * Holds if this method is a constructor for a module.
+ */
+predicate isConstructor(DataFlow::MethodNode method) {
+ method.getMethodName() = "initialize" and
+ exists(DataFlow::ModuleNode m | m.getOwnInstanceMethod(method.getMethodName()) = method)
+}
+
abstract class Endpoint instanceof DataFlow::Node {
string getNamespace() { result = getNamespace(super.getLocation().getFile()) }
@@ -153,10 +161,7 @@ class MethodEndpoint extends Endpoint instanceof DataFlow::MethodNode {
/**
* Holds if this method is a constructor for a module.
*/
- private predicate isConstructor() {
- super.getMethodName() = "initialize" and
- exists(DataFlow::ModuleNode m | m.getOwnInstanceMethod(super.getMethodName()) = this)
- }
+ private predicate isConstructor() { isConstructor(this) }
}
string methodClassification(Call method) {
From 1a499cf388d695cd70a93eb4dfa4da0c129903a0 Mon Sep 17 00:00:00 2001
From: maikypedia
Date: Wed, 7 Feb 2024 14:38:21 +0100
Subject: [PATCH 076/378] Update `expected`
---
.../UnsafeUnpack.expected | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/swift/ql/test/query-tests/Security/CWE-022-Unsafe-Unpack/UnsafeUnpack.expected b/swift/ql/test/query-tests/Security/CWE-022-Unsafe-Unpack/UnsafeUnpack.expected
index 4e79ee8b503..09fc20545b0 100644
--- a/swift/ql/test/query-tests/Security/CWE-022-Unsafe-Unpack/UnsafeUnpack.expected
+++ b/swift/ql/test/query-tests/Security/CWE-022-Unsafe-Unpack/UnsafeUnpack.expected
@@ -1,13 +1,13 @@
edges
-| UnsafeUnpack.swift:60:9:60:48 | call to Data.init(contentsOf:options:) | UnsafeUnpack.swift:60:60:60:60 | source |
-| UnsafeUnpack.swift:60:60:60:60 | source | UnsafeUnpack.swift:62:27:62:27 | source |
-| UnsafeUnpack.swift:60:60:60:60 | source | UnsafeUnpack.swift:65:39:65:39 | source |
+| UnsafeUnpack.swift:62:9:62:48 | call to Data.init(contentsOf:options:) | UnsafeUnpack.swift:62:60:62:60 | source |
+| UnsafeUnpack.swift:62:60:62:60 | source | UnsafeUnpack.swift:64:27:64:27 | source |
+| UnsafeUnpack.swift:62:60:62:60 | source | UnsafeUnpack.swift:67:39:67:39 | source |
nodes
-| UnsafeUnpack.swift:60:9:60:48 | call to Data.init(contentsOf:options:) | semmle.label | call to Data.init(contentsOf:options:) |
-| UnsafeUnpack.swift:60:60:60:60 | source | semmle.label | source |
-| UnsafeUnpack.swift:62:27:62:27 | source | semmle.label | source |
-| UnsafeUnpack.swift:65:39:65:39 | source | semmle.label | source |
+| UnsafeUnpack.swift:62:9:62:48 | call to Data.init(contentsOf:options:) | semmle.label | call to Data.init(contentsOf:options:) |
+| UnsafeUnpack.swift:62:60:62:60 | source | semmle.label | source |
+| UnsafeUnpack.swift:64:27:64:27 | source | semmle.label | source |
+| UnsafeUnpack.swift:67:39:67:39 | source | semmle.label | source |
subpaths
#select
-| UnsafeUnpack.swift:62:27:62:27 | source | UnsafeUnpack.swift:60:9:60:48 | call to Data.init(contentsOf:options:) | UnsafeUnpack.swift:62:27:62:27 | source | Unsafe unpacking from a malicious zip retrieved from a remote location. |
-| UnsafeUnpack.swift:65:39:65:39 | source | UnsafeUnpack.swift:60:9:60:48 | call to Data.init(contentsOf:options:) | UnsafeUnpack.swift:65:39:65:39 | source | Unsafe unpacking from a malicious zip retrieved from a remote location. |
+| UnsafeUnpack.swift:64:27:64:27 | source | UnsafeUnpack.swift:62:9:62:48 | call to Data.init(contentsOf:options:) | UnsafeUnpack.swift:64:27:64:27 | source | Unsafe unpacking from a malicious zip retrieved from a remote location. |
+| UnsafeUnpack.swift:67:39:67:39 | source | UnsafeUnpack.swift:62:9:62:48 | call to Data.init(contentsOf:options:) | UnsafeUnpack.swift:67:39:67:39 | source | Unsafe unpacking from a malicious zip retrieved from a remote location. |
From 4eeca02da64ce2ccb3bfe39e21d51088b633600d Mon Sep 17 00:00:00 2001
From: Tamas Vajk
Date: Wed, 7 Feb 2024 14:58:19 +0100
Subject: [PATCH 077/378] Change file content string comparisons to be case
invariant
---
.../FileContent.cs | 29 +++++++------------
1 file changed, 10 insertions(+), 19 deletions(-)
diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FileContent.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FileContent.cs
index 03f448e7330..08639561d87 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FileContent.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FileContent.cs
@@ -127,10 +127,10 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
public FileContent(ILogger logger, IEnumerable files) : this(logger, files, new UnsafeFileReader())
{ }
- private static string GetGroup(ReadOnlySpan input, ValueMatch valueMatch, string groupPrefix, bool toLower)
+ private static string GetGroup(ReadOnlySpan input, ValueMatch valueMatch, string groupPrefix)
{
var match = input.Slice(valueMatch.Index, valueMatch.Length);
- var includeIndex = match.IndexOf(groupPrefix, StringComparison.InvariantCultureIgnoreCase);
+ var includeIndex = match.IndexOf(groupPrefix, StringComparison.OrdinalIgnoreCase);
if (includeIndex == -1)
{
return string.Empty;
@@ -141,14 +141,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
var quoteIndex1 = match.IndexOf("\"");
var quoteIndex2 = match.Slice(quoteIndex1 + 1).IndexOf("\"");
- var result = match.Slice(quoteIndex1 + 1, quoteIndex2).ToString();
-
- if (toLower)
- {
- result = result.ToLowerInvariant();
- }
-
- return result;
+ return match.Slice(quoteIndex1 + 1, quoteIndex2).ToString();
}
private static bool IsGroupMatch(ReadOnlySpan line, Regex regex, string groupPrefix, string value)
@@ -156,7 +149,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
foreach (var valueMatch in regex.EnumerateMatches(line))
{
// We can't get the group from the ValueMatch, so doing it manually:
- if (GetGroup(line, valueMatch, groupPrefix, toLower: true) == value.ToLowerInvariant())
+ if (string.Equals(GetGroup(line, valueMatch, groupPrefix), value, StringComparison.OrdinalIgnoreCase))
{
return true;
}
@@ -172,12 +165,11 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
{
foreach (ReadOnlySpan line in unsafeFileReader.ReadLines(file))
{
-
// Find all the packages.
foreach (var valueMatch in PackageReference().EnumerateMatches(line))
{
// We can't get the group from the ValueMatch, so doing it manually:
- var packageName = GetGroup(line, valueMatch, "Include", toLower: true);
+ var packageName = GetGroup(line, valueMatch, "Include").ToLowerInvariant();
if (!string.IsNullOrEmpty(packageName))
{
allPackages.Add(packageName);
@@ -189,24 +181,23 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|| IsGroupMatch(line, ProjectSdk(), "Sdk", "Microsoft.NET.Sdk.Web")
|| IsGroupMatch(line, FrameworkReference(), "Include", "Microsoft.AspNetCore.App");
-
// Determine if implicit usings are used.
useImplicitUsings = useImplicitUsings
- || line.Contains("enable".AsSpan(), StringComparison.Ordinal)
- || line.Contains("true".AsSpan(), StringComparison.Ordinal);
+ || line.Contains("enable".AsSpan(), StringComparison.OrdinalIgnoreCase)
+ || line.Contains("true".AsSpan(), StringComparison.OrdinalIgnoreCase);
// Determine if WPF is used.
useWpf = useWpf
- || line.Contains("true".AsSpan(), StringComparison.Ordinal);
+ || line.Contains("true".AsSpan(), StringComparison.OrdinalIgnoreCase);
// Determine if Windows Forms is used.
useWindowsForms = useWindowsForms
- || line.Contains("true".AsSpan(), StringComparison.Ordinal);
+ || line.Contains("true".AsSpan(), StringComparison.OrdinalIgnoreCase);
// Find all custom implicit usings.
foreach (var valueMatch in CustomImplicitUsingDeclarations().EnumerateMatches(line))
{
- var ns = GetGroup(line, valueMatch, "Include", toLower: false);
+ var ns = GetGroup(line, valueMatch, "Include");
if (!string.IsNullOrEmpty(ns))
{
implicitUsingNamespaces.Add(ns);
From ed052ccc2607607a352f29540a316e71dfa45b71 Mon Sep 17 00:00:00 2001
From: maikypedia
Date: Wed, 7 Feb 2024 15:58:10 +0100
Subject: [PATCH 078/378] Change note
---
swift/ql/src/change-notes/2024-02-07-unsafe-unpacking.md | 4 ++++
1 file changed, 4 insertions(+)
create mode 100644 swift/ql/src/change-notes/2024-02-07-unsafe-unpacking.md
diff --git a/swift/ql/src/change-notes/2024-02-07-unsafe-unpacking.md b/swift/ql/src/change-notes/2024-02-07-unsafe-unpacking.md
new file mode 100644
index 00000000000..e3c6f79bc48
--- /dev/null
+++ b/swift/ql/src/change-notes/2024-02-07-unsafe-unpacking.md
@@ -0,0 +1,4 @@
+---
+category: newQuery
+---
+* Added a new query, `swift/unsafe-unpacking`, that detects unpacking user controlled zips without validating the destination file path is within the destination directory.
\ No newline at end of file
From 1c6108028b024da4ce2d5200e7e3ef4fffda1a55 Mon Sep 17 00:00:00 2001
From: Ian Lynagh
Date: Wed, 7 Feb 2024 15:12:17 +0000
Subject: [PATCH 079/378] Kotlin 2: Accept some location changes for arrays
---
.../library-tests/arrays/arrayAccesses.expected | 8 ++++----
.../library-tests/arrays/assignExprs.expected | 8 ++++----
2 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/java/ql/test-kotlin2/library-tests/arrays/arrayAccesses.expected b/java/ql/test-kotlin2/library-tests/arrays/arrayAccesses.expected
index 986bdeed21f..3dd2feaf353 100644
--- a/java/ql/test-kotlin2/library-tests/arrays/arrayAccesses.expected
+++ b/java/ql/test-kotlin2/library-tests/arrays/arrayAccesses.expected
@@ -16,7 +16,7 @@
| arrayGetsSets.kt:19:11:19:15 | ...[...] | arrayGetsSets.kt:19:3:19:7 | ...=... | char | arrayGetsSets.kt:19:11:19:12 | a8 | arrayGetsSets.kt:19:14:19:14 | 0 |
| arrayGetsSets.kt:20:3:20:7 | ...[...] | arrayGetsSets.kt:20:3:20:7 | ...=... | Object[] | arrayGetsSets.kt:20:3:20:4 | a9 | arrayGetsSets.kt:20:6:20:6 | 0 |
| arrayGetsSets.kt:20:11:20:15 | ...[...] | arrayGetsSets.kt:20:3:20:7 | ...=... | Object | arrayGetsSets.kt:20:11:20:12 | a9 | arrayGetsSets.kt:20:14:20:14 | 0 |
-| arrayGetsSets.kt:32:3:32:7 | ...[...] | arrayGetsSets.kt:32:3:32:7 | ...+=... | int | arrayGetsSets.kt:32:3:32:4 | a1 | arrayGetsSets.kt:32:6:32:6 | 0 |
-| arrayGetsSets.kt:38:3:38:7 | ...[...] | arrayGetsSets.kt:38:3:38:7 | .../=... | long | arrayGetsSets.kt:38:3:38:4 | a4 | arrayGetsSets.kt:38:6:38:6 | 0 |
-| arrayGetsSets.kt:39:3:39:7 | ...[...] | arrayGetsSets.kt:39:3:39:7 | ...-=... | float | arrayGetsSets.kt:39:3:39:4 | a5 | arrayGetsSets.kt:39:6:39:6 | 0 |
-| arrayGetsSets.kt:40:3:40:7 | ...[...] | arrayGetsSets.kt:40:3:40:7 | ...*=... | double | arrayGetsSets.kt:40:3:40:4 | a6 | arrayGetsSets.kt:40:6:40:6 | 0 |
+| arrayGetsSets.kt:32:3:32:12 | ...[...] | arrayGetsSets.kt:32:3:32:12 | ...+=... | int | arrayGetsSets.kt:32:3:32:4 | a1 | arrayGetsSets.kt:32:6:32:6 | 0 |
+| arrayGetsSets.kt:38:3:38:13 | ...[...] | arrayGetsSets.kt:38:3:38:13 | .../=... | long | arrayGetsSets.kt:38:3:38:4 | a4 | arrayGetsSets.kt:38:6:38:6 | 0 |
+| arrayGetsSets.kt:39:3:39:13 | ...[...] | arrayGetsSets.kt:39:3:39:13 | ...-=... | float | arrayGetsSets.kt:39:3:39:4 | a5 | arrayGetsSets.kt:39:6:39:6 | 0 |
+| arrayGetsSets.kt:40:3:40:14 | ...[...] | arrayGetsSets.kt:40:3:40:14 | ...*=... | double | arrayGetsSets.kt:40:3:40:4 | a6 | arrayGetsSets.kt:40:6:40:6 | 0 |
diff --git a/java/ql/test-kotlin2/library-tests/arrays/assignExprs.expected b/java/ql/test-kotlin2/library-tests/arrays/assignExprs.expected
index da09855b1e0..5f8fda311e2 100644
--- a/java/ql/test-kotlin2/library-tests/arrays/assignExprs.expected
+++ b/java/ql/test-kotlin2/library-tests/arrays/assignExprs.expected
@@ -1,4 +1,4 @@
-| arrayGetsSets.kt:32:3:32:7 | ...+=... | += | int[] | arrayGetsSets.kt:32:3:32:7 | ...[...] | int | arrayGetsSets.kt:32:12:32:12 | 1 | int |
-| arrayGetsSets.kt:38:3:38:7 | .../=... | /= | long[] | arrayGetsSets.kt:38:3:38:7 | ...[...] | long | arrayGetsSets.kt:38:12:38:13 | 1 | long |
-| arrayGetsSets.kt:39:3:39:7 | ...-=... | -= | float[] | arrayGetsSets.kt:39:3:39:7 | ...[...] | float | arrayGetsSets.kt:39:12:39:13 | 1.0 | float |
-| arrayGetsSets.kt:40:3:40:7 | ...*=... | *= | double[] | arrayGetsSets.kt:40:3:40:7 | ...[...] | double | arrayGetsSets.kt:40:12:40:14 | 1.0 | double |
+| arrayGetsSets.kt:32:3:32:12 | ...+=... | += | int[] | arrayGetsSets.kt:32:3:32:12 | ...[...] | int | arrayGetsSets.kt:32:12:32:12 | 1 | int |
+| arrayGetsSets.kt:38:3:38:13 | .../=... | /= | long[] | arrayGetsSets.kt:38:3:38:13 | ...[...] | long | arrayGetsSets.kt:38:12:38:13 | 1 | long |
+| arrayGetsSets.kt:39:3:39:13 | ...-=... | -= | float[] | arrayGetsSets.kt:39:3:39:13 | ...[...] | float | arrayGetsSets.kt:39:12:39:13 | 1.0 | float |
+| arrayGetsSets.kt:40:3:40:14 | ...*=... | *= | double[] | arrayGetsSets.kt:40:3:40:14 | ...[...] | double | arrayGetsSets.kt:40:12:40:14 | 1.0 | double |
From 3d1f9a79fb6d0859b519b58dde7ca9914409abf0 Mon Sep 17 00:00:00 2001
From: Ian Lynagh
Date: Wed, 7 Feb 2024 15:17:40 +0000
Subject: [PATCH 080/378] Kotlin 2: Accept location changes in
test-kotlin2/library-tests/data-classes
---
.../library-tests/data-classes/PrintAst.expected | 10 +++++-----
.../library-tests/data-classes/callees.expected | 2 +-
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/java/ql/test-kotlin2/library-tests/data-classes/PrintAst.expected b/java/ql/test-kotlin2/library-tests/data-classes/PrintAst.expected
index f884671c094..e601e1378a6 100644
--- a/java/ql/test-kotlin2/library-tests/data-classes/PrintAst.expected
+++ b/java/ql/test-kotlin2/library-tests/data-classes/PrintAst.expected
@@ -17,11 +17,11 @@ dc.kt:
# 0| 3: [Method] copy
# 0| 3: [TypeAccess] ProtoMapValue
#-----| 4: (Parameters)
-# 1| 0: [Parameter] bytes
-# 1| 0: [TypeAccess] byte[]
-# 1| 1: [Parameter] strs
-# 1| 0: [TypeAccess] String[]
-# 1| 0: [TypeAccess] String
+# 0| 0: [Parameter] bytes
+# 0| 0: [TypeAccess] byte[]
+# 0| 1: [Parameter] strs
+# 0| 0: [TypeAccess] String[]
+# 0| 0: [TypeAccess] String
# 0| 5: [BlockStmt] { ... }
# 0| 0: [ReturnStmt] return ...
# 0| 0: [ClassInstanceExpr] new ProtoMapValue(...)
diff --git a/java/ql/test-kotlin2/library-tests/data-classes/callees.expected b/java/ql/test-kotlin2/library-tests/data-classes/callees.expected
index f16c4ffb435..a0352c3ac72 100644
--- a/java/ql/test-kotlin2/library-tests/data-classes/callees.expected
+++ b/java/ql/test-kotlin2/library-tests/data-classes/callees.expected
@@ -4,4 +4,4 @@
| dc.kt:0:0:0:0 | new ProtoMapValue(...) | ProtoMapValue.ProtoMapValue |
| dc.kt:0:0:0:0 | toString(...) | java.util.Arrays.toString |
| dc.kt:0:0:0:0 | toString(...) | java.util.Arrays.toString |
-| dc.kt:1:1:1:71 | super(...) | java.lang.Object.Object |
+| dc.kt:1:25:1:71 | super(...) | java.lang.Object.Object |
From c731251e61a607024f580519612841ff7fb5f797 Mon Sep 17 00:00:00 2001
From: Ian Lynagh
Date: Wed, 7 Feb 2024 15:32:04 +0000
Subject: [PATCH 081/378] Kotlin 2: Remove an unused diagnostic matcher in
library-tests/dataflow/func
---
.../library-tests/dataflow/func/kotlinx_coroutines_stubs.kt | 2 --
1 file changed, 2 deletions(-)
diff --git a/java/ql/test-kotlin2/library-tests/dataflow/func/kotlinx_coroutines_stubs.kt b/java/ql/test-kotlin2/library-tests/dataflow/func/kotlinx_coroutines_stubs.kt
index 3ef2c70d363..8cb4c31fb25 100644
--- a/java/ql/test-kotlin2/library-tests/dataflow/func/kotlinx_coroutines_stubs.kt
+++ b/java/ql/test-kotlin2/library-tests/dataflow/func/kotlinx_coroutines_stubs.kt
@@ -31,5 +31,3 @@ public fun CoroutineScope.async(
): Deferred {
return null!!
}
-
-// Diagnostic Matches: % Couldn't get owner of KDoc. The comment is extracted without an owner. ...while extracting a file (kotlinx_coroutines_stubs.kt) at %kotlinx_coroutines_stubs.kt:1:1:36:0%
From c314cc8b68a2438701a60593fc8030d487791bab Mon Sep 17 00:00:00 2001
From: Ian Lynagh
Date: Wed, 7 Feb 2024 15:56:10 +0000
Subject: [PATCH 082/378] Kotlin 2: Accept some location changes in
library-tests/exprs/binop.expected
---
java/ql/test-kotlin2/library-tests/exprs/binop.expected | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/java/ql/test-kotlin2/library-tests/exprs/binop.expected b/java/ql/test-kotlin2/library-tests/exprs/binop.expected
index f69701028d5..83dad7f94b5 100644
--- a/java/ql/test-kotlin2/library-tests/exprs/binop.expected
+++ b/java/ql/test-kotlin2/library-tests/exprs/binop.expected
@@ -96,8 +96,8 @@
| exprs.kt:141:12:141:20 | ... + ... | exprs.kt:141:12:141:14 | 123 | exprs.kt:141:18:141:20 | 456 |
| exprs.kt:167:8:167:16 | ... (value not-equals) ... | exprs.kt:167:8:167:8 | r | exprs.kt:167:13:167:16 | null |
| exprs.kt:196:31:196:37 | ... + ... | exprs.kt:196:31:196:32 | getA1(...) | exprs.kt:196:36:196:37 | a2 |
-| exprs.kt:211:20:211:29 | ... + ... | exprs.kt:211:20:211:21 | ...!! | exprs.kt:211:28:211:28 | 5 |
-| exprs.kt:212:19:212:25 | ... + ... | exprs.kt:212:20:212:21 | ...!! | exprs.kt:212:25:212:25 | 5 |
+| exprs.kt:211:19:211:29 | ... + ... | exprs.kt:211:19:211:21 | ...!! | exprs.kt:211:28:211:28 | 5 |
+| exprs.kt:212:19:212:25 | ... + ... | exprs.kt:212:19:212:21 | ...!! | exprs.kt:212:25:212:25 | 5 |
| exprs.kt:230:12:230:47 | ... (value equals) ... | exprs.kt:230:12:230:27 | notNullPrimitive | exprs.kt:230:32:230:47 | notNullPrimitive |
| exprs.kt:231:12:231:48 | ... (value equals) ... | exprs.kt:231:12:231:27 | notNullPrimitive | exprs.kt:231:32:231:48 | nullablePrimitive |
| exprs.kt:232:12:232:49 | ... (value equals) ... | exprs.kt:232:12:232:28 | nullablePrimitive | exprs.kt:232:33:232:49 | nullablePrimitive |
From 8a93133b81dfb5bc2d9ea583573376aa3de84606 Mon Sep 17 00:00:00 2001
From: Ian Lynagh
Date: Wed, 7 Feb 2024 16:21:49 +0000
Subject: [PATCH 083/378] Kotlin 2: Accept loc changes in
library-tests/exprs/unaryOp.expected
---
java/ql/test-kotlin2/library-tests/exprs/unaryOp.expected | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/java/ql/test-kotlin2/library-tests/exprs/unaryOp.expected b/java/ql/test-kotlin2/library-tests/exprs/unaryOp.expected
index 03b5d64a7f4..487226320cc 100644
--- a/java/ql/test-kotlin2/library-tests/exprs/unaryOp.expected
+++ b/java/ql/test-kotlin2/library-tests/exprs/unaryOp.expected
@@ -2,9 +2,9 @@
| exprs.kt:32:15:32:26 | !... | exprs.kt:32:15:32:26 | contains(...) |
| exprs.kt:79:15:79:22 | ~... | exprs.kt:79:15:79:16 | lx |
| exprs.kt:121:14:121:16 | !... | exprs.kt:121:15:121:16 | b1 |
-| exprs.kt:202:19:202:20 | ...!! | exprs.kt:202:18:202:18 | x |
-| exprs.kt:211:20:211:21 | ...!! | exprs.kt:211:19:211:19 | s |
-| exprs.kt:212:20:212:21 | ...!! | exprs.kt:212:19:212:19 | s |
+| exprs.kt:202:18:202:20 | ...!! | exprs.kt:202:18:202:18 | x |
+| exprs.kt:211:19:211:21 | ...!! | exprs.kt:211:19:211:19 | s |
+| exprs.kt:212:19:212:21 | ...!! | exprs.kt:212:19:212:19 | s |
| exprs.kt:286:5:286:6 | -... | exprs.kt:286:6:286:6 | i |
| exprs.kt:287:5:287:6 | +... | exprs.kt:287:6:287:6 | i |
| exprs.kt:288:5:288:6 | -... | exprs.kt:288:6:288:6 | d |
From ef8e6c8805d0fbf3f52e4b24ae1f9c052724840a Mon Sep 17 00:00:00 2001
From: Ian Lynagh
Date: Wed, 7 Feb 2024 16:40:40 +0000
Subject: [PATCH 084/378] Kotlin 2: Accept loc changes in
library-tests/exprs/funcExprs.expected
---
.../library-tests/exprs/funcExprs.expected | 160 +++++++++---------
1 file changed, 80 insertions(+), 80 deletions(-)
diff --git a/java/ql/test-kotlin2/library-tests/exprs/funcExprs.expected b/java/ql/test-kotlin2/library-tests/exprs/funcExprs.expected
index b79725a80e3..1328047196c 100644
--- a/java/ql/test-kotlin2/library-tests/exprs/funcExprs.expected
+++ b/java/ql/test-kotlin2/library-tests/exprs/funcExprs.expected
@@ -75,118 +75,118 @@ lambda_modifiers
| samConversion.kt:46:32:46:44 | ...->... | samConversion.kt:46:32:46:44 | invoke | final, override, public |
| samConversion.kt:58:30:58:45 | ...->... | samConversion.kt:58:30:58:45 | invoke | final, override, public, suspend |
anon_class_member_modifiers
-| delegatedProperties.kt:6:24:9:9 | new KProperty0(...) { ... } | delegatedProperties.kt:6:24:9:9 | get | override, public |
-| delegatedProperties.kt:6:24:9:9 | new KProperty0(...) { ... } | delegatedProperties.kt:6:24:9:9 | invoke | override, public |
+| delegatedProperties.kt:6:27:9:9 | new KProperty0(...) { ... } | delegatedProperties.kt:6:27:9:9 | get | override, public |
+| delegatedProperties.kt:6:27:9:9 | new KProperty0(...) { ... } | delegatedProperties.kt:6:27:9:9 | invoke | override, public |
| delegatedProperties.kt:6:32:9:9 | new Function0(...) { ... } | delegatedProperties.kt:6:32:9:9 | invoke | final, override, public |
-| delegatedProperties.kt:19:31:19:51 | new KMutableProperty0(...) { ... } | delegatedProperties.kt:19:31:19:51 | get | override, public |
-| delegatedProperties.kt:19:31:19:51 | new KMutableProperty0(...) { ... } | delegatedProperties.kt:19:31:19:51 | get | override, public |
-| delegatedProperties.kt:19:31:19:51 | new KMutableProperty0(...) { ... } | delegatedProperties.kt:19:31:19:51 | invoke | override, public |
-| delegatedProperties.kt:19:31:19:51 | new KMutableProperty0(...) { ... } | delegatedProperties.kt:19:31:19:51 | invoke | override, public |
-| delegatedProperties.kt:19:31:19:51 | new KMutableProperty0(...) { ... } | delegatedProperties.kt:19:31:19:51 | set | override, public |
-| delegatedProperties.kt:19:31:19:51 | new KMutableProperty0(...) { ... } | delegatedProperties.kt:19:31:19:51 | set | override, public |
-| delegatedProperties.kt:23:26:23:31 | new KProperty0(...) { ... } | delegatedProperties.kt:23:26:23:31 | get | override, public |
-| delegatedProperties.kt:23:26:23:31 | new KProperty0(...) { ... } | delegatedProperties.kt:23:26:23:31 | invoke | override, public |
+| delegatedProperties.kt:19:34:19:51 | new KMutableProperty0(...) { ... } | delegatedProperties.kt:19:34:19:51 | get | override, public |
+| delegatedProperties.kt:19:34:19:51 | new KMutableProperty0(...) { ... } | delegatedProperties.kt:19:34:19:51 | get | override, public |
+| delegatedProperties.kt:19:34:19:51 | new KMutableProperty0(...) { ... } | delegatedProperties.kt:19:34:19:51 | invoke | override, public |
+| delegatedProperties.kt:19:34:19:51 | new KMutableProperty0(...) { ... } | delegatedProperties.kt:19:34:19:51 | invoke | override, public |
+| delegatedProperties.kt:19:34:19:51 | new KMutableProperty0(...) { ... } | delegatedProperties.kt:19:34:19:51 | set | override, public |
+| delegatedProperties.kt:19:34:19:51 | new KMutableProperty0(...) { ... } | delegatedProperties.kt:19:34:19:51 | set | override, public |
+| delegatedProperties.kt:23:29:23:31 | new KProperty0(...) { ... } | delegatedProperties.kt:23:29:23:31 | get | override, public |
+| delegatedProperties.kt:23:29:23:31 | new KProperty0(...) { ... } | delegatedProperties.kt:23:29:23:31 | invoke | override, public |
| delegatedProperties.kt:25:64:31:9 | new ReadWriteProperty