diff --git a/.github/workflows/mad_modelDiff.yml b/.github/workflows/mad_modelDiff.yml index 04e5f486866..1d75fc1d654 100644 --- a/.github/workflows/mad_modelDiff.yml +++ b/.github/workflows/mad_modelDiff.yml @@ -11,7 +11,7 @@ on: branches: - main paths: - - "java/ql/src/utils/model-generator/**/*.*" + # - "java/ql/src/utils/model-generator/**/*.*" - ".github/workflows/mad_modelDiff.yml" permissions: @@ -61,8 +61,8 @@ jobs: DATABASE=$2 cd codeql-$QL_VARIANT SHORTNAME=`basename $DATABASE` - python java/ql/src/utils/model-generator/GenerateFlowModel.py --with-summaries --with-sinks $DATABASE $MODELS/${SHORTNAME}.qll - mv $MODELS/${SHORTNAME}.qll $MODELS/${SHORTNAME}Generated_${QL_VARIANT}.qll + python java/ql/src/utils/model-generator/GenerateFlowModel.py --with-summaries --with-sinks $DATABASE $MODELS/${SHORTNAME}.model.yml + mv $MODELS/${SHORTNAME}.model.yml $MODELS/${SHORTNAME}Generated_${QL_VARIANT}.model.yml cd .. } @@ -85,16 +85,16 @@ jobs: set -x MODELS=`pwd`/tmp-models ls -1 tmp-models/ - for m in $MODELS/*_main.qll ; do + for m in $MODELS/*_main.model.yml ; do t="${m/main/"pr"}" basename=`basename $m` - name="diff_${basename/_main.qll/""}" + name="diff_${basename/_main.model.yml/""}" (diff -w -u $m $t | diff2html -i stdin -F $MODELS/$name.html) || true done - uses: actions/upload-artifact@v3 with: name: models - path: tmp-models/*.qll + path: tmp-models/*.model.yml retention-days: 20 - uses: actions/upload-artifact@v3 with: diff --git a/.github/workflows/mad_regenerate-models.yml b/.github/workflows/mad_regenerate-models.yml index 0abc8936911..d92e3652d5c 100644 --- a/.github/workflows/mad_regenerate-models.yml +++ b/.github/workflows/mad_regenerate-models.yml @@ -53,7 +53,7 @@ jobs: java/ql/src/utils/model-generator/RegenerateModels.py "${SLUG}" dbs/${SHORTNAME} - name: Stage changes run: | - find java -name "*.qll" -print0 | xargs -0 git add + find java -name "*.model.yml" -print0 | xargs -0 git add git status git diff --cached > models.patch - uses: actions/upload-artifact@v3 diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll index f681e90aa21..3eacc21f356 100644 --- a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll +++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll @@ -244,4 +244,20 @@ module Consistency { not callable = viableCallable(call) and not any(ConsistencyConfiguration c).viableImplInCallContextTooLargeExclude(call, ctx, callable) } + + query predicate uniqueParameterNodeAtPosition( + DataFlowCallable c, ParameterPosition pos, Node p, string msg + ) { + isParameterNode(p, c, pos) and + not exists(unique(Node p0 | isParameterNode(p0, c, pos))) and + msg = "Parameters with overlapping positions." + } + + query predicate uniqueParameterNodePosition( + DataFlowCallable c, ParameterPosition pos, Node p, string msg + ) { + isParameterNode(p, c, pos) and + not exists(unique(ParameterPosition pos0 | isParameterNode(p, c, pos0))) and + msg = "Parameter node with multiple positions." + } } diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll index f681e90aa21..3eacc21f356 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplConsistency.qll @@ -244,4 +244,20 @@ module Consistency { not callable = viableCallable(call) and not any(ConsistencyConfiguration c).viableImplInCallContextTooLargeExclude(call, ctx, callable) } + + query predicate uniqueParameterNodeAtPosition( + DataFlowCallable c, ParameterPosition pos, Node p, string msg + ) { + isParameterNode(p, c, pos) and + not exists(unique(Node p0 | isParameterNode(p0, c, pos))) and + msg = "Parameters with overlapping positions." + } + + query predicate uniqueParameterNodePosition( + DataFlowCallable c, ParameterPosition pos, Node p, string msg + ) { + isParameterNode(p, c, pos) and + not exists(unique(ParameterPosition pos0 | isParameterNode(p, c, pos0))) and + msg = "Parameter node with multiple positions." + } } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll index f681e90aa21..3eacc21f356 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll @@ -244,4 +244,20 @@ module Consistency { not callable = viableCallable(call) and not any(ConsistencyConfiguration c).viableImplInCallContextTooLargeExclude(call, ctx, callable) } + + query predicate uniqueParameterNodeAtPosition( + DataFlowCallable c, ParameterPosition pos, Node p, string msg + ) { + isParameterNode(p, c, pos) and + not exists(unique(Node p0 | isParameterNode(p0, c, pos))) and + msg = "Parameters with overlapping positions." + } + + query predicate uniqueParameterNodePosition( + DataFlowCallable c, ParameterPosition pos, Node p, string msg + ) { + isParameterNode(p, c, pos) and + not exists(unique(ParameterPosition pos0 | isParameterNode(p, c, pos0))) and + msg = "Parameter node with multiple positions." + } } diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-consistency.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-consistency.expected index 8c15a1cbcb9..948a1ed7cf9 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-consistency.expected +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-consistency.expected @@ -93,3 +93,5 @@ postWithInFlow | test.cpp:499:4:499:4 | p [inner post update] | PostUpdateNode should not be the target of local flow. | | test.cpp:505:35:505:35 | x [inner post update] | PostUpdateNode should not be the target of local flow. | viableImplInCallContextTooLarge +uniqueParameterNodeAtPosition +uniqueParameterNodePosition diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected index ea2f4ae0d29..90e2d6f31d5 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected @@ -637,3 +637,5 @@ postWithInFlow | true_upon_entry.cpp:101:18:101:18 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. | | true_upon_entry.cpp:102:5:102:5 | x [post update] | PostUpdateNode should not be the target of local flow. | viableImplInCallContextTooLarge +uniqueParameterNodeAtPosition +uniqueParameterNodePosition diff --git a/cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected b/cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected index 8a9e15049fc..e8d3ee839dc 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected @@ -158,3 +158,5 @@ postWithInFlow | struct_init.c:24:11:24:12 | ab [inner post update] | PostUpdateNode should not be the target of local flow. | | struct_init.c:36:17:36:24 | nestedAB [inner post update] | PostUpdateNode should not be the target of local flow. | viableImplInCallContextTooLarge +uniqueParameterNodeAtPosition +uniqueParameterNodePosition diff --git a/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected b/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected index c1b3d0c66b7..7841e662585 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected @@ -1326,3 +1326,5 @@ postWithInFlow | struct_init.c:46:16:46:24 | pointerAB [post update] | PostUpdateNode should not be the target of local flow. | | struct_init.c:46:16:46:24 | pointerAB [post update] | PostUpdateNode should not be the target of local flow. | viableImplInCallContextTooLarge +uniqueParameterNodeAtPosition +uniqueParameterNodePosition diff --git a/cpp/ql/test/library-tests/syntax-zoo/dataflow-consistency.expected b/cpp/ql/test/library-tests/syntax-zoo/dataflow-consistency.expected index 1980b113311..3c0812213c3 100644 --- a/cpp/ql/test/library-tests/syntax-zoo/dataflow-consistency.expected +++ b/cpp/ql/test/library-tests/syntax-zoo/dataflow-consistency.expected @@ -127,3 +127,5 @@ postWithInFlow | static_init_templates.cpp:21:2:21:4 | val [post update] | PostUpdateNode should not be the target of local flow. | | try_catch.cpp:7:8:7:8 | call to exception | PostUpdateNode should not be the target of local flow. | viableImplInCallContextTooLarge +uniqueParameterNodeAtPosition +uniqueParameterNodePosition diff --git a/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected b/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected index 26abe41c33f..e8b6e1048c6 100644 --- a/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected +++ b/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected @@ -2713,3 +2713,7 @@ postWithInFlow | whilestmt.c:40:7:40:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. | | whilestmt.c:42:7:42:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. | viableImplInCallContextTooLarge +uniqueParameterNodeAtPosition +| ir.cpp:724:6:724:13 | TryCatch | 0 | ir.cpp:735:22:735:22 | *s | Parameters with overlapping positions. | +| ir.cpp:724:6:724:13 | TryCatch | 0 | ir.cpp:738:24:738:24 | *e | Parameters with overlapping positions. | +uniqueParameterNodePosition diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll index f681e90aa21..3eacc21f356 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplConsistency.qll @@ -244,4 +244,20 @@ module Consistency { not callable = viableCallable(call) and not any(ConsistencyConfiguration c).viableImplInCallContextTooLargeExclude(call, ctx, callable) } + + query predicate uniqueParameterNodeAtPosition( + DataFlowCallable c, ParameterPosition pos, Node p, string msg + ) { + isParameterNode(p, c, pos) and + not exists(unique(Node p0 | isParameterNode(p0, c, pos))) and + msg = "Parameters with overlapping positions." + } + + query predicate uniqueParameterNodePosition( + DataFlowCallable c, ParameterPosition pos, Node p, string msg + ) { + isParameterNode(p, c, pos) and + not exists(unique(ParameterPosition pos0 | isParameterNode(p, c, pos0))) and + msg = "Parameter node with multiple positions." + } } diff --git a/csharp/ql/src/utils/model-generator/GenerateFlowModelExtensions.py b/csharp/ql/src/utils/model-generator/GenerateFlowModelExtensions.py deleted file mode 100755 index 0e93d34c8cf..00000000000 --- a/csharp/ql/src/utils/model-generator/GenerateFlowModelExtensions.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/python3 - -import sys -import os.path -import subprocess - -# Add Model as Data script directory to sys.path. -gitroot = subprocess.check_output(["git", "rev-parse", "--show-toplevel"]).decode("utf-8").strip() -madpath = os.path.join(gitroot, "misc/scripts/models-as-data/") -sys.path.append(madpath) - -import generate_flow_model_extensions as model - -language = "csharp" -model.Generator.make(language).run() \ No newline at end of file diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplConsistency.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplConsistency.qll index f681e90aa21..3eacc21f356 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplConsistency.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplConsistency.qll @@ -244,4 +244,20 @@ module Consistency { not callable = viableCallable(call) and not any(ConsistencyConfiguration c).viableImplInCallContextTooLargeExclude(call, ctx, callable) } + + query predicate uniqueParameterNodeAtPosition( + DataFlowCallable c, ParameterPosition pos, Node p, string msg + ) { + isParameterNode(p, c, pos) and + not exists(unique(Node p0 | isParameterNode(p0, c, pos))) and + msg = "Parameters with overlapping positions." + } + + query predicate uniqueParameterNodePosition( + DataFlowCallable c, ParameterPosition pos, Node p, string msg + ) { + isParameterNode(p, c, pos) and + not exists(unique(ParameterPosition pos0 | isParameterNode(p, c, pos0))) and + msg = "Parameter node with multiple positions." + } } diff --git a/java/ql/src/utils/model-generator/GenerateFlowModelExtensions.py b/java/ql/src/utils/model-generator/GenerateFlowModelExtensions.py deleted file mode 100755 index 3dd1003bbdd..00000000000 --- a/java/ql/src/utils/model-generator/GenerateFlowModelExtensions.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/python3 - -import sys -import os.path -import subprocess - -# Add Model as Data script directory to sys.path. -gitroot = subprocess.check_output(["git", "rev-parse", "--show-toplevel"]).decode("utf-8").strip() -madpath = os.path.join(gitroot, "misc/scripts/models-as-data/") -sys.path.append(madpath) - -import generate_flow_model_extensions as model - -language = "java" -model.Generator.make(language).run() \ No newline at end of file diff --git a/java/ql/src/utils/model-generator/RegenerateModels.py b/java/ql/src/utils/model-generator/RegenerateModels.py index 55e9651b4ca..f89873339f5 100755 --- a/java/ql/src/utils/model-generator/RegenerateModels.py +++ b/java/ql/src/utils/model-generator/RegenerateModels.py @@ -5,19 +5,17 @@ from pathlib import Path import json import os -import requests import shutil import subprocess import tempfile import sys -defaultModelPath = "java/ql/lib/semmle/code/java/frameworks" lgtmSlugToModelFile = { # "apache/commons-beanutils": "apache/BeanUtilsGenerated.qll", # "apache/commons-codec": "apache/CodecGenerated.qll", # "apache/commons-lang": "apache/Lang3Generated.qll", - "apache/commons-io": "apache/IOGenerated.qll", + "apache/commons-io": "org.apache.commons.io", } @@ -36,13 +34,12 @@ def regenerateModel(lgtmSlug, extractedDb): print("ERROR: slug " + lgtmSlug + " is not mapped to a model file in script " + sys.argv[0]) sys.exit(1) - modelFile = defaultModelPath + "/" + lgtmSlugToModelFile[lgtmSlug] + modelFile = lgtmSlugToModelFile[lgtmSlug] codeQlRoot = findGitRoot() - targetModel = codeQlRoot + "/" + modelFile subprocess.check_call([codeQlRoot + "/java/ql/src/utils/model-generator/GenerateFlowModel.py", - "--with-summaries", "--with-sinks", - extractedDb, targetModel]) - print("Regenerated " + targetModel) + "--with-summaries", "--with-sinks", "--with-negative-summaries", + extractedDb, modelFile]) + print("Regenerated " + modelFile) shutil.rmtree(tmpDir) diff --git a/misc/scripts/models-as-data/generate_flow_model.py b/misc/scripts/models-as-data/generate_flow_model.py index b2e8e1a2e52..cc99e627383 100644 --- a/misc/scripts/models-as-data/generate_flow_model.py +++ b/misc/scripts/models-as-data/generate_flow_model.py @@ -1,5 +1,6 @@ #!/usr/bin/python3 +import helpers import json import os import os.path @@ -8,6 +9,21 @@ import subprocess import sys import tempfile +def quote_if_needed(row): + if row != "true" and row != "false": + return "\"" + row + "\"" + # subtypes column + return row + +def parseData(data): + rows = "" + for (row) in data: + d = row[0].split(';') + d = map(quote_if_needed, d) + rows += " - [" + ', '.join(d) + ']\n' + + return rows + class Generator: def __init__ (self, language): self.language = language @@ -17,32 +33,32 @@ class Generator: self.generateNegativeSummaries = False self.generateTypeBasedSummaries = False self.dryRun = False + self.dirname = "model-generator" def printHelp(self): print(f"""Usage: -python3 GenerateFlowModel.py [] [--with-sinks] [--with-sources] [--with-summaries] [--with-typebased-summaries] [--dry-run] +python3 GenerateFlowModel.py [] [--with-sinks] [--with-sources] [--with-summaries] [--with-typebased-summaries] [--dry-run] This generates summary, source and sink models for the code in the database. -The files will be placed in `{self.language}/ql/lib/semmle/code/{self.language}/frameworks/` where -outputQll is the name (and path) of the output QLL file. Usually, models are grouped by their +The files will be placed in `{self.language}/ql/lib/ext/generated/.model.yml` where +outputYml is the name (and path) of the output YAML file. Usually, models are grouped by their respective frameworks. -If negative summaries are produced a file prefixed with `Negative` will be generated and stored in the same folder. Which models are generated is controlled by the flags: --with-sinks --with-sources --with-summaries --with-negative-summaries - --with-typebased-summaries -If none of these flags are specified, all models are generated. + --with-typebased-summaries (Experimental) +If none of these flags are specified, all models are generated except for the type based models. --dry-run: Only run the queries, but don't write to file. Example invocations: -$ python3 GenerateFlowModel.py /tmp/dbs/my_library_db "mylibrary/Framework.qll" -$ python3 GenerateFlowModel.py /tmp/dbs/my_library_db "mylibrary/Framework.qll" "Friendly Name of Framework" -$ python3 GenerateFlowModel.py /tmp/dbs/my_library_db "mylibrary/FrameworkSinks.qll" --with-sinks +$ python3 GenerateFlowModel.py /tmp/dbs/my_library_db mylibrary +$ python3 GenerateFlowModel.py /tmp/dbs/my_library_db mylibrary "Friendly Name of Framework" +$ python3 GenerateFlowModel.py /tmp/dbs/my_library_db --with-sinks Requirements: `codeql` should both appear on your path. """) @@ -50,22 +66,19 @@ Requirements: `codeql` should both appear on your path. def setenvironment(self, target, database, friendlyName): self.codeQlRoot = subprocess.check_output(["git", "rev-parse", "--show-toplevel"]).decode("utf-8").strip() - if not target.endswith(".qll"): - target += ".qll" + if not target.endswith(".model.yml"): + target += ".model.yml" filename = os.path.basename(target) - dirname = os.path.dirname(target) if friendlyName is not None: self.friendlyname = friendlyName else: - self.friendlyname = filename[:-4] - self.shortname = filename[:-4] + self.friendlyname = filename[:-10] + self.shortname = filename[:-10] self.database = database self.generatedFrameworks = os.path.join( - self.codeQlRoot, f"{self.language}/ql/lib/semmle/code/{self.language}/frameworks/") - self.frameworkTarget = os.path.join(self.generatedFrameworks, dirname, filename) - self.negativeFrameworkTarget = os.path.join(self.generatedFrameworks, dirname, "Negative" + filename) - self.typeBasedFrameworkTarget = os.path.join(self.generatedFrameworks, dirname, "TypeBased" + filename) - + self.codeQlRoot, f"{self.language}/ql/lib/ext/generated/") + self.frameworkTarget = os.path.join(self.generatedFrameworks, filename) + self.typeBasedFrameworkTarget = os.path.join(self.generatedFrameworks, "TypeBased" + filename) self.workDir = tempfile.mkdtemp() os.makedirs(self.generatedFrameworks, exist_ok=True) @@ -114,166 +127,93 @@ Requirements: `codeql` should both appear on your path. generator.setenvironment(sys.argv[2], sys.argv[1], friendlyName) return generator + - - def runQuery(self, infoMessage, query): - print("########## Querying " + infoMessage + "...") - queryFile = os.path.join(self.codeQlRoot, f"{self.language}/ql/src/utils/model-generator", query) + def runQuery(self, query): + print("########## Querying " + query + "...") + queryFile = os.path.join(self.codeQlRoot, f"{self.language}/ql/src/utils/{self.dirname}", query) resultBqrs = os.path.join(self.workDir, "out.bqrs") - cmd = ['codeql', 'query', 'run', queryFile, '--database', - self.database, '--output', resultBqrs, '--threads', '8'] - ret = subprocess.call(cmd) - if ret != 0: - print("Failed to generate " + infoMessage + - ". Failed command was: " + shlex.join(cmd)) - sys.exit(1) - return self.readRows(resultBqrs) + helpers.run_cmd(['codeql', 'query', 'run', queryFile, '--database', + self.database, '--output', resultBqrs, '--threads', '8'], "Failed to generate " + query) + + return helpers.readData(self.workDir, resultBqrs) - def readRows(self, bqrsFile): - generatedJson = os.path.join(self.workDir, "out.json") - cmd = ['codeql', 'bqrs', 'decode', bqrsFile, - '--format=json', '--output', generatedJson] - ret = subprocess.call(cmd) - if ret != 0: - print("Failed to decode BQRS. Failed command was: " + shlex.join(cmd)) - sys.exit(1) - - with open(generatedJson) as f: - results = json.load(f) - - try: - results['#select']['tuples'] - except KeyError: - print('Unexpected JSON output - no tuples found') - exit(1) - - rows = "" - for (row) in results['#select']['tuples']: - rows += " \"" + row[0] + "\",\n" - - return rows[:-2] - - - def asCsvModel(self, superclass, kind, rows): - classTemplate = """ -private class {0}{1}Csv extends {2} {{ - override predicate row(string row) {{ - row = - [ -{3} - ] - }} -}} - """ + def asAddsTo(self, rows, predicate): if rows.strip() == "": return "" - return classTemplate.format(self.shortname[0].upper() + self.shortname[1:], kind.capitalize(), superclass, rows) + return helpers.addsToTemplate.format(f"codeql/{self.language}-all", predicate, rows) + + + def getAddsTo(self, query, predicate): + data = self.runQuery(query) + rows = parseData(data) + return self.asAddsTo(rows, predicate) def makeContent(self): if self.generateSummaries: - summaryRows = self.runQuery("summary models", "CaptureSummaryModels.ql") - summaryCsv = self.asCsvModel("SummaryModelCsv", "summary", summaryRows) + summaryAddsTo = self.getAddsTo("CaptureSummaryModels.ql", helpers.summaryModelPredicate) else: - summaryCsv = "" + summaryAddsTo = "" if self.generateSinks: - sinkRows = self.runQuery("sink models", "CaptureSinkModels.ql") - sinkCsv = self.asCsvModel("SinkModelCsv", "sinks", sinkRows) + sinkAddsTo = self.getAddsTo("CaptureSinkModels.ql", helpers.sinkModelPredicate) else: - sinkCsv = "" + sinkAddsTo = "" if self.generateSources: - sourceRows = self.runQuery("source models", "CaptureSourceModels.ql") - sourceCsv = self.asCsvModel("SourceModelCsv", "sources", sourceRows) + sourceAddsTo = self.getAddsTo("CaptureSourceModels.ql", helpers.sourceModelPredicate) else: - sourceCsv = "" + sourceAddsTo = "" - return f""" -/** - * THIS FILE IS AN AUTO-GENERATED MODELS AS DATA FILE. DO NOT EDIT. - * Definitions of taint steps in the {self.friendlyname} framework. - */ - -import {self.language} -private import semmle.code.{self.language}.dataflow.ExternalFlow - -{sinkCsv} -{sourceCsv} -{summaryCsv} - - """ - - def makeNegativeContent(self): if self.generateNegativeSummaries: - negativeSummaryRows = self.runQuery("negative summary models", "CaptureNegativeSummaryModels.ql") - negativeSummaryCsv = self.asCsvModel("NegativeSummaryModelCsv", "NegativeSummary", negativeSummaryRows) + negativeSummaryAddsTo = self.getAddsTo("CaptureNegativeSummaryModels.ql", "extNegativeSummaryModel") else: - negativeSummaryCsv = "" - - return f""" -/** - * THIS FILE IS AN AUTO-GENERATED MODELS AS DATA FILE. DO NOT EDIT. - * Definitions of negative summaries in the {self.friendlyname} framework. - */ - -import {self.language} -private import semmle.code.{self.language}.dataflow.ExternalFlow - -{negativeSummaryCsv} + negativeSummaryAddsTo = "" + + return f""" +# THIS FILE IS AN AUTO-GENERATED MODELS AS DATA FILE. DO NOT EDIT. +# Definitions of taint steps in the {self.friendlyname} framework. +extensions: +{sinkAddsTo} +{sourceAddsTo} +{summaryAddsTo} +{negativeSummaryAddsTo} """ def makeTypeBasedContent(self): if self.generateTypeBasedSummaries: - typeBasedSummaryRows = self.runQuery("type based summary models", "CaptureTypeBasedSummaryModels.ql") - typeBasedSummaryCsv = self.asCsvModel("SummaryModelCsv", "TypeBasedSummary", typeBasedSummaryRows) + typeBasedSummaryAddsTo = self.getAddsTo("CaptureTypeBasedSummaryModels.ql", "extSummaryModel") else: - typeBasedSummaryCsv = "" + typeBasedSummaryAddsTo = "" return f""" -/** - * THIS FILE IS AN AUTO-GENERATED MODELS AS DATA FILE. DO NOT EDIT. - * Definitions of type based summaries in the {self.friendlyname} framework. - */ - -import {self.language} -private import semmle.code.{self.language}.dataflow.ExternalFlow - -{typeBasedSummaryCsv} +# THIS FILE IS AN AUTO-GENERATED MODELS AS DATA FILE. DO NOT EDIT. +# Definitions of type based summaries in the {self.friendlyname} framework. +extensions: +{typeBasedSummaryAddsTo} """ def save(self, content, target): - with open(target, "w") as targetQll: - targetQll.write(content) - - cmd = ['codeql', 'query', 'format', '--in-place', target] - ret = subprocess.call(cmd) - if ret != 0: - print("Failed to format query. Failed command was: " + shlex.join(cmd)) - sys.exit(1) - - print("") - print("CSV model written to " + target) + with open(target, "w") as targetYml: + targetYml.write(content) + print("Models as data extensions written to " + target) def run(self): content = self.makeContent() - negativeContent = self.makeNegativeContent() typeBasedContent = self.makeTypeBasedContent() if self.dryRun: - print("CSV Models generated, but not written to file.") + print("Models as data extensions generated, but not written to file.") sys.exit(0) if self.generateSinks or self.generateSinks or self.generateSummaries: self.save(content, self.frameworkTarget) - if self.generateNegativeSummaries: - self.save(negativeContent, self.negativeFrameworkTarget) - if self.generateTypeBasedSummaries: self.save(typeBasedContent, self.typeBasedFrameworkTarget) diff --git a/misc/scripts/models-as-data/generate_flow_model_extensions.py b/misc/scripts/models-as-data/generate_flow_model_extensions.py deleted file mode 100644 index eee328c6f15..00000000000 --- a/misc/scripts/models-as-data/generate_flow_model_extensions.py +++ /dev/null @@ -1,215 +0,0 @@ -#!/usr/bin/python3 - -import helpers -import json -import os -import os.path -import shlex -import subprocess -import sys -import tempfile - -def quote_if_needed(row): - if row != "true" and row != "false": - return "\"" + row + "\"" - # subtypes column - return row - -def parseData(data): - rows = "" - for (row) in data: - d = row[0].split(';') - d = map(quote_if_needed, d) - rows += " - [" + ', '.join(d) + ']\n' - - return rows - -class Generator: - def __init__ (self, language): - self.language = language - self.generateSinks = False - self.generateSources = False - self.generateSummaries = False - self.generateNegativeSummaries = False - self.generateTypeBasedSummaries = False - self.dryRun = False - self.dirname = "model-generator" - - - def printHelp(self): - print(f"""Usage: -python3 GenerateFlowModelExtensions.py [] [--with-sinks] [--with-sources] [--with-summaries] [--with-typebased-summaries] [--dry-run] - -This generates summary, source and sink models for the code in the database. -The files will be placed in `{self.language}/ql/lib/ext/generated/.model.yml` where -outputYml is the name (and path) of the output YAML file. Usually, models are grouped by their -respective frameworks. - -Which models are generated is controlled by the flags: - --with-sinks - --with-sources - --with-summaries - --with-negative-summaries - --with-typebased-summaries (Experimental - only for C#) -If none of these flags are specified, all models are generated except for the type based models. - - --dry-run: Only run the queries, but don't write to file. - -Example invocations: -$ python3 GenerateFlowModelExtensions.py /tmp/dbs/my_library_db mylibrary -$ python3 GenerateFlowModelExtensions.py /tmp/dbs/my_library_db mylibrary "Friendly Name of Framework" -$ python3 GenerateFlowModelExtensions.py /tmp/dbs/my_library_db --with-sinks - -Requirements: `codeql` should both appear on your path. - """) - - - def setenvironment(self, target, database, friendlyName): - self.codeQlRoot = subprocess.check_output(["git", "rev-parse", "--show-toplevel"]).decode("utf-8").strip() - if not target.endswith(".model.yml"): - target += ".model.yml" - filename = os.path.basename(target) - if friendlyName is not None: - self.friendlyname = friendlyName - else: - self.friendlyname = filename[:-10] - self.shortname = filename[:-10] - self.database = database - self.generatedFrameworks = os.path.join( - self.codeQlRoot, f"{self.language}/ql/lib/ext/generated/") - self.frameworkTarget = os.path.join(self.generatedFrameworks, filename) - self.typeBasedFrameworkTarget = os.path.join(self.generatedFrameworks, "TypeBased" + filename) - self.workDir = tempfile.mkdtemp() - os.makedirs(self.generatedFrameworks, exist_ok=True) - - - @staticmethod - def make(language): - generator = Generator(language) - if any(s == "--help" for s in sys.argv): - generator.printHelp() - sys.exit(0) - - if "--with-sinks" in sys.argv: - sys.argv.remove("--with-sinks") - generator.generateSinks = True - - if "--with-sources" in sys.argv: - sys.argv.remove("--with-sources") - generator.generateSources = True - - if "--with-summaries" in sys.argv: - sys.argv.remove("--with-summaries") - generator.generateSummaries = True - - if "--with-negative-summaries" in sys.argv: - sys.argv.remove("--with-negative-summaries") - generator.generateNegativeSummaries = True - - if "--with-typebased-summaries" in sys.argv: - sys.argv.remove("--with-typebased-summaries") - generator.generateTypeBasedSummaries = True - - if "--dry-run" in sys.argv: - sys.argv.remove("--dry-run") - generator.dryRun = True - - if not generator.generateSinks and not generator.generateSources and not generator.generateSummaries and not generator.generateNegativeSummaries and not generator.generateTypeBasedSummaries: - generator.generateSinks = generator.generateSources = generator.generateSummaries = generator.generateNegativeSummaries = True - - if len(sys.argv) < 3 or len(sys.argv) > 4: - generator.printHelp() - sys.exit(1) - - friendlyName = None - if len(sys.argv) == 4: - friendlyName = sys.argv[3] - - generator.setenvironment(sys.argv[2], sys.argv[1], friendlyName) - return generator - - - def runQuery(self, query): - print("########## Querying " + query + "...") - queryFile = os.path.join(self.codeQlRoot, f"{self.language}/ql/src/utils/{self.dirname}", query) - resultBqrs = os.path.join(self.workDir, "out.bqrs") - - helpers.run_cmd(['codeql', 'query', 'run', queryFile, '--database', - self.database, '--output', resultBqrs, '--threads', '8'], "Failed to generate " + query) - - return helpers.readData(self.workDir, resultBqrs) - - - def asAddsTo(self, rows, predicate): - if rows.strip() == "": - return "" - return helpers.addsToTemplate.format(f"codeql/{self.language}-all", predicate, rows) - - - def getAddsTo(self, query, predicate): - data = self.runQuery(query) - rows = parseData(data) - return self.asAddsTo(rows, predicate) - - - def makeContent(self): - if self.generateSummaries: - summaryAddsTo = self.getAddsTo("CaptureSummaryModels.ql", helpers.summaryModelPredicate) - else: - summaryAddsTo = "" - - if self.generateSinks: - sinkAddsTo = self.getAddsTo("CaptureSinkModels.ql", helpers.sinkModelPredicate) - else: - sinkAddsTo = "" - - if self.generateSources: - sourceAddsTo = self.getAddsTo("CaptureSourceModels.ql", helpers.sourceModelPredicate) - else: - sourceAddsTo = "" - - if self.generateNegativeSummaries: - negativeSummaryAddsTo = self.getAddsTo("CaptureNegativeSummaryModels.ql", helpers.negativeSummaryModelPredicate) - else: - negativeSummaryAddsTo = "" - - return f"""# THIS FILE IS AN AUTO-GENERATED MODELS AS DATA FILE. DO NOT EDIT. -# Definitions of models for the {self.friendlyname} framework. - -extensions: -{sinkAddsTo} -{sourceAddsTo} -{summaryAddsTo} -{negativeSummaryAddsTo}""" - - def makeTypeBasedContent(self): - if self.generateTypeBasedSummaries: - typeBasedSummaryAddsTo = self.getAddsTo("CaptureTypeBasedSummaryModels.ql", "extSummaryModel") - else: - typeBasedSummaryAddsTo = "" - - return f"""# THIS FILE IS AN AUTO-GENERATED MODELS AS DATA FILE. DO NOT EDIT. -# Definitions of type based summaries in the {self.friendlyname} framework. - -extensions: -{typeBasedSummaryAddsTo}""" - - def save(self, content, target): - with open(target, "w") as targetYml: - targetYml.write(content) - print("Models as data extensions written to " + target) - - - def run(self): - content = self.makeContent() - typeBasedContent = self.makeTypeBasedContent() - - if self.dryRun: - print("Models as data extensions generated, but not written to file.") - sys.exit(0) - - if self.generateSinks or self.generateSinks or self.generateSummaries: - self.save(content, self.frameworkTarget) - - if self.generateTypeBasedSummaries: - self.save(typeBasedContent, self.typeBasedFrameworkTarget) diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplConsistency.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplConsistency.qll index f681e90aa21..3eacc21f356 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplConsistency.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImplConsistency.qll @@ -244,4 +244,20 @@ module Consistency { not callable = viableCallable(call) and not any(ConsistencyConfiguration c).viableImplInCallContextTooLargeExclude(call, ctx, callable) } + + query predicate uniqueParameterNodeAtPosition( + DataFlowCallable c, ParameterPosition pos, Node p, string msg + ) { + isParameterNode(p, c, pos) and + not exists(unique(Node p0 | isParameterNode(p0, c, pos))) and + msg = "Parameters with overlapping positions." + } + + query predicate uniqueParameterNodePosition( + DataFlowCallable c, ParameterPosition pos, Node p, string msg + ) { + isParameterNode(p, c, pos) and + not exists(unique(ParameterPosition pos0 | isParameterNode(p, c, pos0))) and + msg = "Parameter node with multiple positions." + } } diff --git a/python/ql/test/experimental/dataflow/basic/dataflow-consistency.expected b/python/ql/test/experimental/dataflow/basic/dataflow-consistency.expected index 8f4dbd04742..686ddfdb83d 100644 --- a/python/ql/test/experimental/dataflow/basic/dataflow-consistency.expected +++ b/python/ql/test/experimental/dataflow/basic/dataflow-consistency.expected @@ -19,3 +19,5 @@ reverseRead argHasPostUpdate postWithInFlow viableImplInCallContextTooLarge +uniqueParameterNodeAtPosition +uniqueParameterNodePosition diff --git a/python/ql/test/experimental/dataflow/calls/dataflow-consistency.expected b/python/ql/test/experimental/dataflow/calls/dataflow-consistency.expected index 8f4dbd04742..686ddfdb83d 100644 --- a/python/ql/test/experimental/dataflow/calls/dataflow-consistency.expected +++ b/python/ql/test/experimental/dataflow/calls/dataflow-consistency.expected @@ -19,3 +19,5 @@ reverseRead argHasPostUpdate postWithInFlow viableImplInCallContextTooLarge +uniqueParameterNodeAtPosition +uniqueParameterNodePosition diff --git a/python/ql/test/experimental/dataflow/consistency/dataflow-consistency.expected b/python/ql/test/experimental/dataflow/consistency/dataflow-consistency.expected index 8f4dbd04742..686ddfdb83d 100644 --- a/python/ql/test/experimental/dataflow/consistency/dataflow-consistency.expected +++ b/python/ql/test/experimental/dataflow/consistency/dataflow-consistency.expected @@ -19,3 +19,5 @@ reverseRead argHasPostUpdate postWithInFlow viableImplInCallContextTooLarge +uniqueParameterNodeAtPosition +uniqueParameterNodePosition diff --git a/python/ql/test/experimental/dataflow/coverage/dataflow-consistency.expected b/python/ql/test/experimental/dataflow/coverage/dataflow-consistency.expected index 8f4dbd04742..686ddfdb83d 100644 --- a/python/ql/test/experimental/dataflow/coverage/dataflow-consistency.expected +++ b/python/ql/test/experimental/dataflow/coverage/dataflow-consistency.expected @@ -19,3 +19,5 @@ reverseRead argHasPostUpdate postWithInFlow viableImplInCallContextTooLarge +uniqueParameterNodeAtPosition +uniqueParameterNodePosition diff --git a/python/ql/test/experimental/dataflow/fieldflow/dataflow-consistency.expected b/python/ql/test/experimental/dataflow/fieldflow/dataflow-consistency.expected index 8f4dbd04742..686ddfdb83d 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/dataflow-consistency.expected +++ b/python/ql/test/experimental/dataflow/fieldflow/dataflow-consistency.expected @@ -19,3 +19,5 @@ reverseRead argHasPostUpdate postWithInFlow viableImplInCallContextTooLarge +uniqueParameterNodeAtPosition +uniqueParameterNodePosition diff --git a/python/ql/test/experimental/dataflow/global-flow/dataflow-consistency.expected b/python/ql/test/experimental/dataflow/global-flow/dataflow-consistency.expected index 8f4dbd04742..686ddfdb83d 100644 --- a/python/ql/test/experimental/dataflow/global-flow/dataflow-consistency.expected +++ b/python/ql/test/experimental/dataflow/global-flow/dataflow-consistency.expected @@ -19,3 +19,5 @@ reverseRead argHasPostUpdate postWithInFlow viableImplInCallContextTooLarge +uniqueParameterNodeAtPosition +uniqueParameterNodePosition diff --git a/python/ql/test/experimental/dataflow/match/dataflow-consistency.expected b/python/ql/test/experimental/dataflow/match/dataflow-consistency.expected index 8f4dbd04742..686ddfdb83d 100644 --- a/python/ql/test/experimental/dataflow/match/dataflow-consistency.expected +++ b/python/ql/test/experimental/dataflow/match/dataflow-consistency.expected @@ -19,3 +19,5 @@ reverseRead argHasPostUpdate postWithInFlow viableImplInCallContextTooLarge +uniqueParameterNodeAtPosition +uniqueParameterNodePosition diff --git a/python/ql/test/experimental/dataflow/pep_328/dataflow-consistency.expected b/python/ql/test/experimental/dataflow/pep_328/dataflow-consistency.expected index 8f4dbd04742..686ddfdb83d 100644 --- a/python/ql/test/experimental/dataflow/pep_328/dataflow-consistency.expected +++ b/python/ql/test/experimental/dataflow/pep_328/dataflow-consistency.expected @@ -19,3 +19,5 @@ reverseRead argHasPostUpdate postWithInFlow viableImplInCallContextTooLarge +uniqueParameterNodeAtPosition +uniqueParameterNodePosition diff --git a/python/ql/test/experimental/dataflow/regression/dataflow-consistency.expected b/python/ql/test/experimental/dataflow/regression/dataflow-consistency.expected index 8f4dbd04742..686ddfdb83d 100644 --- a/python/ql/test/experimental/dataflow/regression/dataflow-consistency.expected +++ b/python/ql/test/experimental/dataflow/regression/dataflow-consistency.expected @@ -19,3 +19,5 @@ reverseRead argHasPostUpdate postWithInFlow viableImplInCallContextTooLarge +uniqueParameterNodeAtPosition +uniqueParameterNodePosition diff --git a/python/ql/test/experimental/dataflow/strange-essaflow/dataflow-consistency.expected b/python/ql/test/experimental/dataflow/strange-essaflow/dataflow-consistency.expected index 8f4dbd04742..686ddfdb83d 100644 --- a/python/ql/test/experimental/dataflow/strange-essaflow/dataflow-consistency.expected +++ b/python/ql/test/experimental/dataflow/strange-essaflow/dataflow-consistency.expected @@ -19,3 +19,5 @@ reverseRead argHasPostUpdate postWithInFlow viableImplInCallContextTooLarge +uniqueParameterNodeAtPosition +uniqueParameterNodePosition diff --git a/python/ql/test/experimental/dataflow/tainttracking/basic/dataflow-consistency.expected b/python/ql/test/experimental/dataflow/tainttracking/basic/dataflow-consistency.expected index 8f4dbd04742..686ddfdb83d 100644 --- a/python/ql/test/experimental/dataflow/tainttracking/basic/dataflow-consistency.expected +++ b/python/ql/test/experimental/dataflow/tainttracking/basic/dataflow-consistency.expected @@ -19,3 +19,5 @@ reverseRead argHasPostUpdate postWithInFlow viableImplInCallContextTooLarge +uniqueParameterNodeAtPosition +uniqueParameterNodePosition diff --git a/python/ql/test/experimental/dataflow/tainttracking/commonSanitizer/dataflow-consistency.expected b/python/ql/test/experimental/dataflow/tainttracking/commonSanitizer/dataflow-consistency.expected index 8f4dbd04742..686ddfdb83d 100644 --- a/python/ql/test/experimental/dataflow/tainttracking/commonSanitizer/dataflow-consistency.expected +++ b/python/ql/test/experimental/dataflow/tainttracking/commonSanitizer/dataflow-consistency.expected @@ -19,3 +19,5 @@ reverseRead argHasPostUpdate postWithInFlow viableImplInCallContextTooLarge +uniqueParameterNodeAtPosition +uniqueParameterNodePosition diff --git a/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/dataflow-consistency.expected b/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/dataflow-consistency.expected index 8f4dbd04742..686ddfdb83d 100644 --- a/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/dataflow-consistency.expected +++ b/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/dataflow-consistency.expected @@ -19,3 +19,5 @@ reverseRead argHasPostUpdate postWithInFlow viableImplInCallContextTooLarge +uniqueParameterNodeAtPosition +uniqueParameterNodePosition diff --git a/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep-py3/dataflow-consistency.expected b/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep-py3/dataflow-consistency.expected index 8f4dbd04742..686ddfdb83d 100644 --- a/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep-py3/dataflow-consistency.expected +++ b/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep-py3/dataflow-consistency.expected @@ -19,3 +19,5 @@ reverseRead argHasPostUpdate postWithInFlow viableImplInCallContextTooLarge +uniqueParameterNodeAtPosition +uniqueParameterNodePosition diff --git a/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/dataflow-consistency.expected b/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/dataflow-consistency.expected index 8f4dbd04742..686ddfdb83d 100644 --- a/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/dataflow-consistency.expected +++ b/python/ql/test/experimental/dataflow/tainttracking/defaultAdditionalTaintStep/dataflow-consistency.expected @@ -19,3 +19,5 @@ reverseRead argHasPostUpdate postWithInFlow viableImplInCallContextTooLarge +uniqueParameterNodeAtPosition +uniqueParameterNodePosition diff --git a/python/ql/test/experimental/dataflow/tainttracking/unwanted-global-flow/dataflow-consistency.expected b/python/ql/test/experimental/dataflow/tainttracking/unwanted-global-flow/dataflow-consistency.expected index 8f4dbd04742..686ddfdb83d 100644 --- a/python/ql/test/experimental/dataflow/tainttracking/unwanted-global-flow/dataflow-consistency.expected +++ b/python/ql/test/experimental/dataflow/tainttracking/unwanted-global-flow/dataflow-consistency.expected @@ -19,3 +19,5 @@ reverseRead argHasPostUpdate postWithInFlow viableImplInCallContextTooLarge +uniqueParameterNodeAtPosition +uniqueParameterNodePosition diff --git a/python/ql/test/experimental/dataflow/typetracking/dataflow-consistency.expected b/python/ql/test/experimental/dataflow/typetracking/dataflow-consistency.expected index 8f4dbd04742..686ddfdb83d 100644 --- a/python/ql/test/experimental/dataflow/typetracking/dataflow-consistency.expected +++ b/python/ql/test/experimental/dataflow/typetracking/dataflow-consistency.expected @@ -19,3 +19,5 @@ reverseRead argHasPostUpdate postWithInFlow viableImplInCallContextTooLarge +uniqueParameterNodeAtPosition +uniqueParameterNodePosition diff --git a/python/ql/test/experimental/dataflow/variable-capture/dataflow-consistency.expected b/python/ql/test/experimental/dataflow/variable-capture/dataflow-consistency.expected index 8f4dbd04742..686ddfdb83d 100644 --- a/python/ql/test/experimental/dataflow/variable-capture/dataflow-consistency.expected +++ b/python/ql/test/experimental/dataflow/variable-capture/dataflow-consistency.expected @@ -19,3 +19,5 @@ reverseRead argHasPostUpdate postWithInFlow viableImplInCallContextTooLarge +uniqueParameterNodeAtPosition +uniqueParameterNodePosition diff --git a/python/ql/test/library-tests/ApiGraphs/py3/dataflow-consistency.expected b/python/ql/test/library-tests/ApiGraphs/py3/dataflow-consistency.expected index 8f4dbd04742..686ddfdb83d 100644 --- a/python/ql/test/library-tests/ApiGraphs/py3/dataflow-consistency.expected +++ b/python/ql/test/library-tests/ApiGraphs/py3/dataflow-consistency.expected @@ -19,3 +19,5 @@ reverseRead argHasPostUpdate postWithInFlow viableImplInCallContextTooLarge +uniqueParameterNodeAtPosition +uniqueParameterNodePosition diff --git a/python/ql/test/library-tests/frameworks/django-orm/dataflow-consistency.expected b/python/ql/test/library-tests/frameworks/django-orm/dataflow-consistency.expected index 06a8a168262..78188947e79 100644 --- a/python/ql/test/library-tests/frameworks/django-orm/dataflow-consistency.expected +++ b/python/ql/test/library-tests/frameworks/django-orm/dataflow-consistency.expected @@ -67,3 +67,5 @@ reverseRead argHasPostUpdate postWithInFlow viableImplInCallContextTooLarge +uniqueParameterNodeAtPosition +uniqueParameterNodePosition diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplConsistency.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplConsistency.qll index f681e90aa21..3eacc21f356 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplConsistency.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplConsistency.qll @@ -244,4 +244,20 @@ module Consistency { not callable = viableCallable(call) and not any(ConsistencyConfiguration c).viableImplInCallContextTooLargeExclude(call, ctx, callable) } + + query predicate uniqueParameterNodeAtPosition( + DataFlowCallable c, ParameterPosition pos, Node p, string msg + ) { + isParameterNode(p, c, pos) and + not exists(unique(Node p0 | isParameterNode(p0, c, pos))) and + msg = "Parameters with overlapping positions." + } + + query predicate uniqueParameterNodePosition( + DataFlowCallable c, ParameterPosition pos, Node p, string msg + ) { + isParameterNode(p, c, pos) and + not exists(unique(ParameterPosition pos0 | isParameterNode(p, c, pos0))) and + msg = "Parameter node with multiple positions." + } } diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImplConsistency.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImplConsistency.qll index f681e90aa21..3eacc21f356 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImplConsistency.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImplConsistency.qll @@ -244,4 +244,20 @@ module Consistency { not callable = viableCallable(call) and not any(ConsistencyConfiguration c).viableImplInCallContextTooLargeExclude(call, ctx, callable) } + + query predicate uniqueParameterNodeAtPosition( + DataFlowCallable c, ParameterPosition pos, Node p, string msg + ) { + isParameterNode(p, c, pos) and + not exists(unique(Node p0 | isParameterNode(p0, c, pos))) and + msg = "Parameters with overlapping positions." + } + + query predicate uniqueParameterNodePosition( + DataFlowCallable c, ParameterPosition pos, Node p, string msg + ) { + isParameterNode(p, c, pos) and + not exists(unique(ParameterPosition pos0 | isParameterNode(p, c, pos0))) and + msg = "Parameter node with multiple positions." + } }