diff --git a/.github/actions/cache-query-compilation/action.yml b/.github/actions/cache-query-compilation/action.yml index c071aa204d9..9f7569e5f0c 100644 --- a/.github/actions/cache-query-compilation/action.yml +++ b/.github/actions/cache-query-compilation/action.yml @@ -26,9 +26,10 @@ runs: echo "merge_base=$MERGE_BASE" >> $GITHUB_ENV - name: Read CodeQL query compilation - PR if: ${{ github.event_name == 'pull_request' }} - uses: actions/cache@v3 + uses: erik-krogh/actions-cache@a88d0603fe5fb5606db9f002dfcadeb32b5f84c6 with: path: '**/.cache' + read-only: true key: codeql-compile-${{ inputs.key }}-pr-${{ github.sha }} # deliberately not using the `compile-compile-main` keys here. restore-keys: | codeql-compile-${{ inputs.key }}-${{ github.base_ref }}-${{ env.merge_base }} @@ -36,7 +37,7 @@ runs: codeql-compile-${{ inputs.key }}-main- - name: Fill CodeQL query compilation cache - main if: ${{ github.event_name != 'pull_request' }} - uses: actions/cache@v3 + uses: erik-krogh/actions-cache@a88d0603fe5fb5606db9f002dfcadeb32b5f84c6 with: path: '**/.cache' key: codeql-compile-${{ inputs.key }}-${{ github.ref_name }}-${{ github.sha }} # just fill on main diff --git a/.github/workflows/csharp-qltest.yml b/.github/workflows/csharp-qltest.yml new file mode 100644 index 00000000000..78b0ef6a87b --- /dev/null +++ b/.github/workflows/csharp-qltest.yml @@ -0,0 +1,72 @@ +name: "C#: Run QL Tests" + +on: + push: + paths: + - "csharp/**" + - "shared/**" + - .github/actions/fetch-codeql/action.yml + - codeql-workspace.yml + branches: + - main + - "rc/*" + pull_request: + paths: + - "csharp/**" + - "shared/**" + - .github/workflows/csharp-qltest.yml + - .github/actions/fetch-codeql/action.yml + - codeql-workspace.yml + branches: + - main + - "rc/*" + +defaults: + run: + working-directory: csharp + +jobs: + qlupgrade: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: ./.github/actions/fetch-codeql + - name: Check DB upgrade scripts + run: | + echo >empty.trap + codeql dataset import -S ql/lib/upgrades/initial/semmlecode.csharp.dbscheme testdb empty.trap + codeql dataset upgrade testdb --additional-packs ql/lib + diff -q testdb/semmlecode.csharp.dbscheme ql/lib/semmlecode.csharp.dbscheme + - name: Check DB downgrade scripts + run: | + echo >empty.trap + rm -rf testdb; codeql dataset import -S ql/lib/semmlecode.csharp.dbscheme testdb empty.trap + codeql resolve upgrades --format=lines --allow-downgrades --additional-packs downgrades \ + --dbscheme=ql/lib/semmlecode.csharp.dbscheme --target-dbscheme=downgrades/initial/semmlecode.csharp.dbscheme | + xargs codeql execute upgrades testdb + diff -q testdb/semmlecode.csharp.dbscheme downgrades/initial/semmlecode.csharp.dbscheme + qltest: + runs-on: ubuntu-latest-xl + strategy: + fail-fast: false + matrix: + slice: ["1/2", "2/2"] + steps: + - uses: actions/checkout@v3 + - uses: ./.github/actions/fetch-codeql + - uses: ./csharp/actions/create-extractor-pack + - name: Cache compilation cache + id: query-cache + uses: ./.github/actions/cache-query-compilation + with: + key: csharp-qltest-${{ matrix.slice }} + - name: Run QL tests + run: | + CODEQL_PATH=$(gh codeql version --format=json | jq -r .unpackedLocation) + # The legacy ASP extractor is not in this repo, so take the one from the nightly build + mv "$CODEQL_PATH/csharp/tools/extractor-asp.jar" "${{ github.workspace }}/csharp/extractor-pack/tools" + # Safe guard against using the bundled extractor + rm -rf "$CODEQL_PATH/csharp" + codeql test run --threads=0 --ram 52000 --slice ${{ matrix.slice }} --search-path "${{ github.workspace }}/csharp/extractor-pack" --check-databases --check-undefined-labels --check-repeated-labels --check-redefined-labels --consistency-queries ql/consistency-queries ql/test --compilation-cache "${{ steps.query-cache.outputs.cache-dir }}" + env: + GITHUB_TOKEN: ${{ github.token }} diff --git a/.github/workflows/ruby-build.yml b/.github/workflows/ruby-build.yml index 784a7db3dc9..cbe7f32fb17 100644 --- a/.github/workflows/ruby-build.yml +++ b/.github/workflows/ruby-build.yml @@ -98,6 +98,7 @@ jobs: key: ruby-build - name: Build Query Pack run: | + rm -rf target/packs codeql pack create ../shared/ssa --output target/packs codeql pack create ../misc/suite-helpers --output target/packs codeql pack create ../shared/regex --output target/packs diff --git a/.gitignore b/.gitignore index 7b8532b00d2..c81e23fc7f8 100644 --- a/.gitignore +++ b/.gitignore @@ -27,8 +27,6 @@ # It's useful (though not required) to be able to unpack codeql in the ql checkout itself /codeql/ -csharp/extractor/Semmle.Extraction.CSharp.Driver/Properties/launchSettings.json - # Avoid committing cached package components .codeql diff --git a/CODEOWNERS b/CODEOWNERS index 86f38eeee22..55ee2f974f7 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -5,7 +5,7 @@ /javascript/ @github/codeql-javascript /python/ @github/codeql-python /ruby/ @github/codeql-ruby -/swift/ @github/codeql-c +/swift/ @github/codeql-swift /java/kotlin-extractor/ @github/codeql-kotlin /java/kotlin-explorer/ @github/codeql-kotlin @@ -45,4 +45,4 @@ WORKSPACE.bazel @github/codeql-ci-reviewers /.github/workflows/js-ml-tests.yml @github/codeql-ml-powered-queries-reviewers /.github/workflows/ql-for-ql-* @github/codeql-ql-for-ql-reviewers /.github/workflows/ruby-* @github/codeql-ruby -/.github/workflows/swift.yml @github/codeql-c +/.github/workflows/swift.yml @github/codeql-swift diff --git a/csharp/.gitignore b/csharp/.gitignore index 0701c11fe1d..a030c9444fe 100644 --- a/csharp/.gitignore +++ b/csharp/.gitignore @@ -11,4 +11,7 @@ csharp.log *.tlog .vs *.user -.vscode/launch.json \ No newline at end of file +.vscode/launch.json + +extractor/Semmle.Extraction.CSharp.Driver/Properties/launchSettings.json +extractor-pack \ No newline at end of file diff --git a/csharp/actions/create-extractor-pack/action.yml b/csharp/actions/create-extractor-pack/action.yml new file mode 100644 index 00000000000..43b0ec9c6fe --- /dev/null +++ b/csharp/actions/create-extractor-pack/action.yml @@ -0,0 +1,13 @@ +name: Build C# CodeQL pack +description: Builds the C# CodeQL pack +runs: + using: composite + steps: + - name: Setup dotnet + uses: actions/setup-dotnet@v3 + with: + dotnet-version: 6.0.202 + - name: Build Extractor + shell: bash + run: scripts/create-extractor-pack.sh + working-directory: csharp diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/Completion.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/Completion.qll index bda14e0b4ae..4003e8cfac2 100644 --- a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/Completion.qll +++ b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/Completion.qll @@ -103,7 +103,6 @@ abstract class Completion extends TCompletion { * otherwise it is a normal non-Boolean completion. */ predicate isValidFor(ControlFlowElement cfe) { - cfe instanceof NonReturningCall and this = cfe.(NonReturningCall).getACompletion() or this = TThrowCompletion(cfe.(TriedControlFlowElement).getAThrownException()) diff --git a/csharp/ql/src/Bad Practices/Magic Constants/MagicConstants.qll b/csharp/ql/src/Bad Practices/Magic Constants/MagicConstants.qll index b65cdcd1961..73b82c14700 100644 --- a/csharp/ql/src/Bad Practices/Magic Constants/MagicConstants.qll +++ b/csharp/ql/src/Bad Practices/Magic Constants/MagicConstants.qll @@ -10,26 +10,26 @@ private predicate trivialPositiveIntValue(string s) { s = [ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", - "17", "18", "19", "20", "16", "32", "64", "128", "256", "512", "1024", "2048", "4096", - "16384", "32768", "65536", "1048576", "2147483648", "4294967296", "15", "31", "63", "127", - "255", "511", "1023", "2047", "4095", "16383", "32767", "65535", "1048577", "2147483647", - "4294967295", "0x00000001", "0x00000002", "0x00000004", "0x00000008", "0x00000010", - "0x00000020", "0x00000040", "0x00000080", "0x00000100", "0x00000200", "0x00000400", - "0x00000800", "0x00001000", "0x00002000", "0x00004000", "0x00008000", "0x00010000", - "0x00020000", "0x00040000", "0x00080000", "0x00100000", "0x00200000", "0x00400000", - "0x00800000", "0x01000000", "0x02000000", "0x04000000", "0x08000000", "0x10000000", - "0x20000000", "0x40000000", "0x80000000", "0x00000001", "0x00000003", "0x00000007", - "0x0000000f", "0x0000001f", "0x0000003f", "0x0000007f", "0x000000ff", "0x000001ff", - "0x000003ff", "0x000007ff", "0x00000fff", "0x00001fff", "0x00003fff", "0x00007fff", - "0x0000ffff", "0x0001ffff", "0x0003ffff", "0x0007ffff", "0x000fffff", "0x001fffff", - "0x003fffff", "0x007fffff", "0x00ffffff", "0x01ffffff", "0x03ffffff", "0x07ffffff", - "0x0fffffff", "0x1fffffff", "0x3fffffff", "0x7fffffff", "0xffffffff", "0x0001", "0x0002", - "0x0004", "0x0008", "0x0010", "0x0020", "0x0040", "0x0080", "0x0100", "0x0200", "0x0400", - "0x0800", "0x1000", "0x2000", "0x4000", "0x8000", "0x0001", "0x0003", "0x0007", "0x000f", - "0x001f", "0x003f", "0x007f", "0x00ff", "0x01ff", "0x03ff", "0x07ff", "0x0fff", "0x1fff", - "0x3fff", "0x7fff", "0xffff", "0x01", "0x02", "0x04", "0x08", "0x10", "0x20", "0x40", "0x80", - "0x01", "0x03", "0x07", "0x0f", "0x1f", "0x3f", "0x7f", "0xff", "0x00", "10", "100", "1000", - "10000", "100000", "1000000", "10000000", "100000000", "1000000000" + "17", "18", "19", "20", "32", "64", "128", "256", "512", "1024", "2048", "4096", "16384", + "32768", "65536", "1048576", "2147483648", "4294967296", "31", "63", "127", "255", "511", + "1023", "2047", "4095", "16383", "32767", "65535", "1048577", "2147483647", "4294967295", + "0x00000001", "0x00000002", "0x00000004", "0x00000008", "0x00000010", "0x00000020", + "0x00000040", "0x00000080", "0x00000100", "0x00000200", "0x00000400", "0x00000800", + "0x00001000", "0x00002000", "0x00004000", "0x00008000", "0x00010000", "0x00020000", + "0x00040000", "0x00080000", "0x00100000", "0x00200000", "0x00400000", "0x00800000", + "0x01000000", "0x02000000", "0x04000000", "0x08000000", "0x10000000", "0x20000000", + "0x40000000", "0x80000000", "0x00000003", "0x00000007", "0x0000000f", "0x0000001f", + "0x0000003f", "0x0000007f", "0x000000ff", "0x000001ff", "0x000003ff", "0x000007ff", + "0x00000fff", "0x00001fff", "0x00003fff", "0x00007fff", "0x0000ffff", "0x0001ffff", + "0x0003ffff", "0x0007ffff", "0x000fffff", "0x001fffff", "0x003fffff", "0x007fffff", + "0x00ffffff", "0x01ffffff", "0x03ffffff", "0x07ffffff", "0x0fffffff", "0x1fffffff", + "0x3fffffff", "0x7fffffff", "0xffffffff", "0x0001", "0x0002", "0x0004", "0x0008", "0x0010", + "0x0020", "0x0040", "0x0080", "0x0100", "0x0200", "0x0400", "0x0800", "0x1000", "0x2000", + "0x4000", "0x8000", "0x0003", "0x0007", "0x000f", "0x001f", "0x003f", "0x007f", "0x00ff", + "0x01ff", "0x03ff", "0x07ff", "0x0fff", "0x1fff", "0x3fff", "0x7fff", "0xffff", "0x02", + "0x04", "0x08", "0x10", "0x20", "0x40", "0x80", "0x01", "0x03", "0x07", "0x0f", "0x1f", + "0x3f", "0x7f", "0xff", "0x00", "100", "1000", "10000", "100000", "1000000", "10000000", + "100000000", "1000000000" ] } diff --git a/csharp/ql/test/library-tests/assemblies/assemblies.ql b/csharp/ql/test/library-tests/assemblies/assemblies.ql index 98db2a0f973..9c072f0d51e 100644 --- a/csharp/ql/test/library-tests/assemblies/assemblies.ql +++ b/csharp/ql/test/library-tests/assemblies/assemblies.ql @@ -34,7 +34,6 @@ where f.hasName("f") and g.hasName("g") and a.getDeclaringType() = class1 and - a.getDeclaringType() = class1 and b.getDeclaringType() = class1 and c.getDeclaringType() = class1 and not exists(c.getParameter(0).getType().(KnownType)) and diff --git a/csharp/scripts/create-extractor-pack.sh b/csharp/scripts/create-extractor-pack.sh new file mode 100755 index 00000000000..dbbe8219a02 --- /dev/null +++ b/csharp/scripts/create-extractor-pack.sh @@ -0,0 +1,27 @@ +#!/bin/bash +set -eux + +if [[ "$OSTYPE" == "linux-gnu"* ]]; then + platform="linux64" + dotnet_platform="linux-x64" +elif [[ "$OSTYPE" == "darwin"* ]]; then + platform="osx64" + dotnet_platform="osx-x64" +else + echo "Unknown OS" + exit 1 +fi + +rm -rf extractor-pack +mkdir -p extractor-pack +mkdir -p extractor-pack/tools/${platform} + +function dotnet_publish { + dotnet publish --self-contained --configuration Release --runtime ${dotnet_platform} -p:RuntimeFrameworkVersion=6.0.4 $1 --output extractor-pack/tools/${platform} +} + +dotnet_publish extractor/Semmle.Extraction.CSharp.Standalone +dotnet_publish extractor/Semmle.Extraction.CSharp.Driver +dotnet_publish autobuilder/Semmle.Autobuild.CSharp + +cp -r codeql-extractor.yml tools/* downgrades tools ql/lib/semmlecode.csharp.dbscheme ql/lib/semmlecode.csharp.dbscheme.stats extractor-pack/ diff --git a/go/ql/lib/semmle/go/security/OpenUrlRedirectCustomizations.qll b/go/ql/lib/semmle/go/security/OpenUrlRedirectCustomizations.qll index 80b9bb4a126..2d7f6948115 100644 --- a/go/ql/lib/semmle/go/security/OpenUrlRedirectCustomizations.qll +++ b/go/ql/lib/semmle/go/security/OpenUrlRedirectCustomizations.qll @@ -128,7 +128,7 @@ private class SafeUrlSink extends SafeUrlFlow::Sink { private class UnsafeFieldReadSanitizer extends SafeUrlFlow::SanitizerEdge { UnsafeFieldReadSanitizer() { exists(DataFlow::FieldReadNode frn, string name | - name = ["User", "RawQuery", "Fragment", "User"] and + name = ["User", "RawQuery", "Fragment"] and frn.getField().hasQualifiedName("net/url", "URL") | this = frn.getBase() diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/ContainerFlow.qll b/java/ql/lib/semmle/code/java/dataflow/internal/ContainerFlow.qll index 8f9a40ed8d0..aebe509816f 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/ContainerFlow.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/ContainerFlow.qll @@ -250,7 +250,6 @@ private class ContainerFlowSummaries extends SummaryModelCsv { "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual", "java.util;Scanner;true;findInLine;;;Argument[-1];ReturnValue;taint;manual", "java.util;Scanner;true;findWithinHorizon;;;Argument[-1];ReturnValue;taint;manual", - "java.util;Scanner;true;findWithinHorizon;;;Argument[-1];ReturnValue;taint;manual", "java.util;Scanner;true;next;;;Argument[-1];ReturnValue;taint;manual", "java.util;Scanner;true;nextBigDecimal;;;Argument[-1];ReturnValue;taint;manual", "java.util;Scanner;true;nextBigInteger;;;Argument[-1];ReturnValue;taint;manual", diff --git a/java/ql/lib/semmle/code/java/frameworks/android/ContentProviders.qll b/java/ql/lib/semmle/code/java/frameworks/android/ContentProviders.qll index 1e10a01d451..bf47e98b8fb 100644 --- a/java/ql/lib/semmle/code/java/frameworks/android/ContentProviders.qll +++ b/java/ql/lib/semmle/code/java/frameworks/android/ContentProviders.qll @@ -56,7 +56,6 @@ private class SummaryModels extends SummaryModelCsv { "android.content;ContentValues;false;putAll;;;Argument[0].MapValue;Argument[-1].MapValue;value;manual", "android.content;ContentResolver;true;acquireContentProviderClient;;;Argument[0];ReturnValue;taint;manual", "android.content;ContentResolver;true;acquireUnstableContentProviderClient;;;Argument[0];ReturnValue;taint;manual", - "android.content;ContentResolver;true;acquireUnstableContentProviderClient;;;Argument[0];ReturnValue;taint;manual", "android.content;ContentResolver;true;applyBatch;;;Argument[1];ReturnValue;taint;manual", "android.content;ContentResolver;true;call;;;Argument[0];ReturnValue;taint;manual", "android.content;ContentResolver;true;canonicalize;;;Argument[0];ReturnValue;taint;manual", diff --git a/java/ql/lib/semmle/code/java/frameworks/guava/Collections.qll b/java/ql/lib/semmle/code/java/frameworks/guava/Collections.qll index d662e7ee7cd..feb27d22ec0 100644 --- a/java/ql/lib/semmle/code/java/frameworks/guava/Collections.qll +++ b/java/ql/lib/semmle/code/java/frameworks/guava/Collections.qll @@ -503,7 +503,6 @@ private class GuavaCollectCsv extends SummaryModelCsv { "com.google.common.collect;Sets;false;filter;(SortedSet,Predicate);;Argument[0].Element;ReturnValue.Element;value;manual", "com.google.common.collect;Sets;false;intersection;(Set,Set);;Argument[0..1].Element;ReturnValue.Element;value;manual", "com.google.common.collect;Sets;false;newConcurrentHashSet;(Iterable);;Argument[0].Element;ReturnValue.Element;value;manual", - "com.google.common.collect;Sets;false;newConcurrentHashSet;(Iterable);;Argument[0].Element;ReturnValue.Element;value;manual", "com.google.common.collect;Sets;false;newCopyOnWriteArraySet;(Iterable);;Argument[0].Element;ReturnValue.Element;value;manual", "com.google.common.collect;Sets;false;newHashSet;(Iterable);;Argument[0].Element;ReturnValue.Element;value;manual", "com.google.common.collect;Sets;false;newHashSet;(Iterator);;Argument[0].Element;ReturnValue.Element;value;manual", diff --git a/java/ql/lib/semmle/code/java/frameworks/guava/IO.qll b/java/ql/lib/semmle/code/java/frameworks/guava/IO.qll index 6137a4e47f3..59fc0113e10 100644 --- a/java/ql/lib/semmle/code/java/frameworks/guava/IO.qll +++ b/java/ql/lib/semmle/code/java/frameworks/guava/IO.qll @@ -93,7 +93,6 @@ private class GuavaIoSinkCsv extends SinkModelCsv { "com.google.common.io;Resources;false;asByteSource;(URL);;Argument[0];url-open-stream;manual", "com.google.common.io;Resources;false;asCharSource;(URL,Charset);;Argument[0];url-open-stream;manual", "com.google.common.io;Resources;false;copy;(URL,OutputStream);;Argument[0];url-open-stream;manual", - "com.google.common.io;Resources;false;asByteSource;(URL);;Argument[0];url-open-stream;manual", "com.google.common.io;Resources;false;readLines;;;Argument[0];url-open-stream;manual", "com.google.common.io;Resources;false;toByteArray;(URL);;Argument[0];url-open-stream;manual", "com.google.common.io;Resources;false;toString;(URL,Charset);;Argument[0];url-open-stream;manual" diff --git a/java/ql/lib/semmle/code/java/security/ImplicitPendingIntents.qll b/java/ql/lib/semmle/code/java/security/ImplicitPendingIntents.qll index 3441cfaef18..bbfafc2d9c4 100644 --- a/java/ql/lib/semmle/code/java/security/ImplicitPendingIntents.qll +++ b/java/ql/lib/semmle/code/java/security/ImplicitPendingIntents.qll @@ -85,9 +85,11 @@ private class MutablePendingIntentFlowStep extends ImplicitPendingIntentAddition // unless it is at least sometimes explicitly marked immutable and never marked mutable. // Note: for API level < 31, PendingIntents were mutable by default, whereas since then // they are immutable by default. - not TaintTracking::localExprTaint(any(ImmutablePendingIntentFlag flag).getAnAccess(), flagArg) + not bitwiseLocalTaintStep*(DataFlow::exprNode(any(ImmutablePendingIntentFlag flag) + .getAnAccess()), DataFlow::exprNode(flagArg)) or - TaintTracking::localExprTaint(any(MutablePendingIntentFlag flag).getAnAccess(), flagArg) + bitwiseLocalTaintStep*(DataFlow::exprNode(any(MutablePendingIntentFlag flag).getAnAccess()), + DataFlow::exprNode(flagArg)) ) } } @@ -124,3 +126,12 @@ private class PendingIntentSentSinkModels extends SinkModelCsv { ] } } + +/** + * Holds if taint can flow from `source` to `sink` in one local step, + * including bitwise operations. + */ +private predicate bitwiseLocalTaintStep(DataFlow::Node source, DataFlow::Node sink) { + TaintTracking::localTaintStep(source, sink) or + source.asExpr() = sink.asExpr().(BitwiseExpr).(BinaryExpr).getAnOperand() +} diff --git a/java/ql/src/Violations of Best Practice/Magic Constants/MagicConstants.qll b/java/ql/src/Violations of Best Practice/Magic Constants/MagicConstants.qll index 11dbd5cd8d3..5fc7e9069cd 100644 --- a/java/ql/src/Violations of Best Practice/Magic Constants/MagicConstants.qll +++ b/java/ql/src/Violations of Best Practice/Magic Constants/MagicConstants.qll @@ -8,26 +8,26 @@ private predicate trivialPositiveIntValue(string s) { s = [ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", - "17", "18", "19", "20", "16", "32", "64", "128", "256", "512", "1024", "2048", "4096", - "16384", "32768", "65536", "1048576", "2147483648", "4294967296", "15", "31", "63", "127", - "255", "511", "1023", "2047", "4095", "16383", "32767", "65535", "1048577", "2147483647", - "4294967295", "0x00000001", "0x00000002", "0x00000004", "0x00000008", "0x00000010", - "0x00000020", "0x00000040", "0x00000080", "0x00000100", "0x00000200", "0x00000400", - "0x00000800", "0x00001000", "0x00002000", "0x00004000", "0x00008000", "0x00010000", - "0x00020000", "0x00040000", "0x00080000", "0x00100000", "0x00200000", "0x00400000", - "0x00800000", "0x01000000", "0x02000000", "0x04000000", "0x08000000", "0x10000000", - "0x20000000", "0x40000000", "0x80000000", "0x00000001", "0x00000003", "0x00000007", - "0x0000000f", "0x0000001f", "0x0000003f", "0x0000007f", "0x000000ff", "0x000001ff", - "0x000003ff", "0x000007ff", "0x00000fff", "0x00001fff", "0x00003fff", "0x00007fff", - "0x0000ffff", "0x0001ffff", "0x0003ffff", "0x0007ffff", "0x000fffff", "0x001fffff", - "0x003fffff", "0x007fffff", "0x00ffffff", "0x01ffffff", "0x03ffffff", "0x07ffffff", - "0x0fffffff", "0x1fffffff", "0x3fffffff", "0x7fffffff", "0xffffffff", "0x0001", "0x0002", - "0x0004", "0x0008", "0x0010", "0x0020", "0x0040", "0x0080", "0x0100", "0x0200", "0x0400", - "0x0800", "0x1000", "0x2000", "0x4000", "0x8000", "0x0001", "0x0003", "0x0007", "0x000f", - "0x001f", "0x003f", "0x007f", "0x00ff", "0x01ff", "0x03ff", "0x07ff", "0x0fff", "0x1fff", - "0x3fff", "0x7fff", "0xffff", "0x01", "0x02", "0x04", "0x08", "0x10", "0x20", "0x40", "0x80", - "0x01", "0x03", "0x07", "0x0f", "0x1f", "0x3f", "0x7f", "0xff", "0x00", "10", "100", "1000", - "10000", "100000", "1000000", "10000000", "100000000", "1000000000" + "17", "18", "19", "20", "32", "64", "128", "256", "512", "1024", "2048", "4096", "16384", + "32768", "65536", "1048576", "2147483648", "4294967296", "31", "63", "127", "255", "511", + "1023", "2047", "4095", "16383", "32767", "65535", "1048577", "2147483647", "4294967295", + "0x00000001", "0x00000002", "0x00000004", "0x00000008", "0x00000010", "0x00000020", + "0x00000040", "0x00000080", "0x00000100", "0x00000200", "0x00000400", "0x00000800", + "0x00001000", "0x00002000", "0x00004000", "0x00008000", "0x00010000", "0x00020000", + "0x00040000", "0x00080000", "0x00100000", "0x00200000", "0x00400000", "0x00800000", + "0x01000000", "0x02000000", "0x04000000", "0x08000000", "0x10000000", "0x20000000", + "0x40000000", "0x80000000", "0x00000003", "0x00000007", "0x0000000f", "0x0000001f", + "0x0000003f", "0x0000007f", "0x000000ff", "0x000001ff", "0x000003ff", "0x000007ff", + "0x00000fff", "0x00001fff", "0x00003fff", "0x00007fff", "0x0000ffff", "0x0001ffff", + "0x0003ffff", "0x0007ffff", "0x000fffff", "0x001fffff", "0x003fffff", "0x007fffff", + "0x00ffffff", "0x01ffffff", "0x03ffffff", "0x07ffffff", "0x0fffffff", "0x1fffffff", + "0x3fffffff", "0x7fffffff", "0xffffffff", "0x0001", "0x0002", "0x0004", "0x0008", "0x0010", + "0x0020", "0x0040", "0x0080", "0x0100", "0x0200", "0x0400", "0x0800", "0x1000", "0x2000", + "0x4000", "0x8000", "0x0003", "0x0007", "0x000f", "0x001f", "0x003f", "0x007f", "0x00ff", + "0x01ff", "0x03ff", "0x07ff", "0x0fff", "0x1fff", "0x3fff", "0x7fff", "0xffff", "0x01", + "0x02", "0x04", "0x08", "0x10", "0x20", "0x40", "0x80", "0x03", "0x07", "0x0f", "0x1f", + "0x3f", "0x7f", "0xff", "0x00", "100", "1000", "10000", "100000", "1000000", "10000000", + "100000000", "1000000000" ] } diff --git a/java/ql/src/change-notes/2022-11-22-bitwise-implicit-intent-flags.md b/java/ql/src/change-notes/2022-11-22-bitwise-implicit-intent-flags.md new file mode 100644 index 00000000000..20612e86325 --- /dev/null +++ b/java/ql/src/change-notes/2022-11-22-bitwise-implicit-intent-flags.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Fixed an issue in the query `java/android/implicit-pendingintents` by which an implicit Pending Intent marked as immutable was not correctly recognized as such. diff --git a/java/ql/test/query-tests/security/CWE-927/ImplicitPendingIntentsTest.java b/java/ql/test/query-tests/security/CWE-927/ImplicitPendingIntentsTest.java index 9c8f098d467..746c9ca83dc 100644 --- a/java/ql/test/query-tests/security/CWE-927/ImplicitPendingIntentsTest.java +++ b/java/ql/test/query-tests/security/CWE-927/ImplicitPendingIntentsTest.java @@ -156,7 +156,7 @@ public class ImplicitPendingIntentsTest { PendingIntent pi = PendingIntent.getActivity(ctx, 0, baseIntent, flag); // Sanitizer Intent fwdIntent = new Intent(); fwdIntent.putExtra("fwdIntent", pi); - ctx.startActivity(fwdIntent); // $ SPURIOUS: $ hasImplicitPendingIntent + ctx.startActivity(fwdIntent); // Safe } } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/NoSQL.qll b/javascript/ql/lib/semmle/javascript/frameworks/NoSQL.qll index 11f454eadd5..568ae3c93a1 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/NoSQL.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/NoSQL.qll @@ -251,7 +251,7 @@ private module Redis { "set", "publish", "append", "bitfield", "decrby", "getset", "hincrby", "hincrbyfloat", "hset", "hsetnx", "incrby", "incrbyfloat", "linsert", "lpush", "lpushx", "lset", "ltrim", "rename", "renamenx", "rpushx", "setbit", "setex", "smove", "zincrby", "zinterstore", - "hdel", "lpush", "pfadd", "rpush", "sadd", "sdiffstore", "srem" + "hdel", "pfadd", "rpush", "sadd", "sdiffstore", "srem" ] and argIndex = 0 or diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/LoopBoundInjectionCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/LoopBoundInjectionCustomizations.qll index 2142d468b90..1c92ff66c5b 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/LoopBoundInjectionCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/LoopBoundInjectionCustomizations.qll @@ -122,10 +122,10 @@ module LoopBoundInjection { "flattenDeep", "flattenDepth", "initial", "intersection", "intersectionBy", "intersectionWith", "join", "remove", "reverse", "slice", "sortedUniq", "sortedUniqBy", "tail", "union", "unionBy", "unionWith", "uniqBy", "unzip", "unzipWith", "without", "zip", - "zipObject", "zipObjectDeep", "zipWith", "countBy", "each", "forEach", "eachRight", - "forEachRight", "filter", "find", "findLast", "flatMap", "flatMapDeep", "flatMapDepth", - "forEach", "forEachRight", "groupBy", "invokeMap", "keyBy", "map", "orderBy", "partition", - "reduce", "reduceRight", "reject", "sortBy" + "zipObject", "zipObjectDeep", "zipWith", "countBy", "each", "eachRight", "forEachRight", + "filter", "find", "findLast", "flatMap", "flatMapDeep", "flatMapDepth", "forEach", + "groupBy", "invokeMap", "keyBy", "map", "orderBy", "partition", "reduce", "reduceRight", + "reject", "sortBy" ] } diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/UnsafeShellCommandConstructionCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/UnsafeShellCommandConstructionCustomizations.qll index 0b4923de179..ca6920db466 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/UnsafeShellCommandConstructionCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/UnsafeShellCommandConstructionCustomizations.qll @@ -156,14 +156,9 @@ module UnsafeShellCommandConstruction { } /** - * Gets a node that ends up in an array that is ultimately executed as a shell script by `sys`. + * Holds if the arguments array given to `sys` is joined as a string because `shell` is set to true. */ - private DataFlow::SourceNode endsInShellExecutedArray( - DataFlow::TypeBackTracker t, SystemCommandExecution sys - ) { - t.start() and - result = sys.getArgumentList().getALocalSource() and - // the array gets joined to a string when `shell` is set to true. + predicate executesArrayAsShell(SystemCommandExecution sys) { sys.getOptionsArg() .getALocalSource() .getAPropertyWrite("shell") @@ -171,6 +166,17 @@ module UnsafeShellCommandConstruction { .asExpr() .(BooleanLiteral) .getValue() = "true" + } + + /** + * Gets a node that ends up in an array that is ultimately executed as a shell script by `sys`. + */ + private DataFlow::SourceNode endsInShellExecutedArray( + DataFlow::TypeBackTracker t, SystemCommandExecution sys + ) { + t.start() and + result = sys.getArgumentList().getALocalSource() and + executesArrayAsShell(sys) or exists(DataFlow::TypeBackTracker t2 | result = endsInShellExecutedArray(t2, sys).backtrack(t2, t) @@ -193,6 +199,10 @@ module UnsafeShellCommandConstruction { or this = arr.getAMethodCall(["push", "unshift"]).getAnArgument() ) + or + this = sys.getArgumentList() and + not this instanceof DataFlow::ArrayCreationNode and + executesArrayAsShell(sys) } override string getSinkType() { result = "shell argument" } diff --git a/javascript/ql/lib/semmle/javascript/security/regexp/PolynomialReDoSCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/regexp/PolynomialReDoSCustomizations.qll index 87f9437196f..508eaf40e2c 100644 --- a/javascript/ql/lib/semmle/javascript/security/regexp/PolynomialReDoSCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/regexp/PolynomialReDoSCustomizations.qll @@ -90,7 +90,8 @@ module PolynomialReDoS { isCharClassLike(root) ) or - this.(DataFlow::MethodCallNode).getMethodName() = StringOps::substringMethodName() + this.(DataFlow::MethodCallNode).getMethodName() = StringOps::substringMethodName() and + not this.(DataFlow::MethodCallNode).getNumArgument() = 1 // with one argument it just slices off the beginning } } diff --git a/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction/UnsafeShellCommandConstruction.expected b/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction/UnsafeShellCommandConstruction.expected index c97e4dc650e..de11fb884c9 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction/UnsafeShellCommandConstruction.expected +++ b/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction/UnsafeShellCommandConstruction.expected @@ -227,8 +227,14 @@ nodes | lib/lib.js:420:29:420:32 | name | | lib/lib.js:424:24:424:27 | name | | lib/lib.js:424:24:424:27 | name | +| lib/lib.js:425:6:425:13 | arr | +| lib/lib.js:425:12:425:13 | [] | | lib/lib.js:426:11:426:14 | name | | lib/lib.js:426:11:426:14 | name | +| lib/lib.js:427:14:427:16 | arr | +| lib/lib.js:427:14:427:16 | arr | +| lib/lib.js:428:14:428:58 | build(" ... + '-') | +| lib/lib.js:428:14:428:58 | build(" ... + '-') | | lib/lib.js:428:28:428:51 | (name ? ... ' : '') | | lib/lib.js:428:28:428:57 | (name ? ... ) + '-' | | lib/lib.js:428:29:428:50 | name ? ... :' : '' | @@ -306,6 +312,10 @@ nodes | lib/subLib/index.js:7:32:7:35 | name | | lib/subLib/index.js:8:22:8:25 | name | | lib/subLib/index.js:8:22:8:25 | name | +| lib/subLib/index.js:13:44:13:46 | arr | +| lib/subLib/index.js:13:44:13:46 | arr | +| lib/subLib/index.js:14:22:14:24 | arr | +| lib/subLib/index.js:14:22:14:24 | arr | edges | lib/isImported.js:5:49:5:52 | name | lib/isImported.js:6:22:6:25 | name | | lib/isImported.js:5:49:5:52 | name | lib/isImported.js:6:22:6:25 | name | @@ -584,7 +594,13 @@ edges | lib/lib.js:414:40:414:43 | name | lib/lib.js:426:11:426:14 | name | | lib/lib.js:414:40:414:43 | name | lib/lib.js:428:36:428:39 | name | | lib/lib.js:414:40:414:43 | name | lib/lib.js:428:36:428:39 | name | +| lib/lib.js:425:6:425:13 | arr | lib/lib.js:427:14:427:16 | arr | +| lib/lib.js:425:6:425:13 | arr | lib/lib.js:427:14:427:16 | arr | +| lib/lib.js:425:12:425:13 | [] | lib/lib.js:425:6:425:13 | arr | +| lib/lib.js:426:11:426:14 | name | lib/lib.js:425:12:425:13 | [] | | lib/lib.js:428:28:428:51 | (name ? ... ' : '') | lib/lib.js:428:28:428:57 | (name ? ... ) + '-' | +| lib/lib.js:428:28:428:57 | (name ? ... ) + '-' | lib/lib.js:428:14:428:58 | build(" ... + '-') | +| lib/lib.js:428:28:428:57 | (name ? ... ) + '-' | lib/lib.js:428:14:428:58 | build(" ... + '-') | | lib/lib.js:428:28:428:57 | (name ? ... ) + '-' | lib/lib.js:431:23:431:26 | last | | lib/lib.js:428:29:428:50 | name ? ... :' : '' | lib/lib.js:428:28:428:51 | (name ? ... ' : '') | | lib/lib.js:428:36:428:39 | name | lib/lib.js:428:36:428:45 | name + ':' | @@ -672,6 +688,10 @@ edges | lib/subLib/index.js:7:32:7:35 | name | lib/subLib/index.js:8:22:8:25 | name | | lib/subLib/index.js:7:32:7:35 | name | lib/subLib/index.js:8:22:8:25 | name | | lib/subLib/index.js:7:32:7:35 | name | lib/subLib/index.js:8:22:8:25 | name | +| lib/subLib/index.js:13:44:13:46 | arr | lib/subLib/index.js:14:22:14:24 | arr | +| lib/subLib/index.js:13:44:13:46 | arr | lib/subLib/index.js:14:22:14:24 | arr | +| lib/subLib/index.js:13:44:13:46 | arr | lib/subLib/index.js:14:22:14:24 | arr | +| lib/subLib/index.js:13:44:13:46 | arr | lib/subLib/index.js:14:22:14:24 | arr | #select | lib/isImported.js:6:10:6:25 | "rm -rf " + name | lib/isImported.js:5:49:5:52 | name | lib/isImported.js:6:22:6:25 | name | This string concatenation which depends on $@ is later used in a $@. | lib/isImported.js:5:49:5:52 | name | library input | lib/isImported.js:6:2:6:26 | cp.exec ... + name) | shell command | | lib/lib2.js:4:10:4:25 | "rm -rf " + name | lib/lib2.js:3:28:3:31 | name | lib/lib2.js:4:22:4:25 | name | This string concatenation which depends on $@ is later used in a $@. | lib/lib2.js:3:28:3:31 | name | library input | lib/lib2.js:4:2:4:26 | cp.exec ... + name) | shell command | @@ -739,6 +759,8 @@ edges | lib/lib.js:420:29:420:32 | name | lib/lib.js:414:40:414:43 | name | lib/lib.js:420:29:420:32 | name | This shell argument which depends on $@ is later used in a $@. | lib/lib.js:414:40:414:43 | name | library input | lib/lib.js:420:2:420:49 | cp.spaw ... true}) | shell command | | lib/lib.js:424:24:424:27 | name | lib/lib.js:414:40:414:43 | name | lib/lib.js:424:24:424:27 | name | This shell argument which depends on $@ is later used in a $@. | lib/lib.js:414:40:414:43 | name | library input | lib/lib.js:424:2:424:40 | spawn(" ... WN_OPT) | shell command | | lib/lib.js:426:11:426:14 | name | lib/lib.js:414:40:414:43 | name | lib/lib.js:426:11:426:14 | name | This shell argument which depends on $@ is later used in a $@. | lib/lib.js:414:40:414:43 | name | library input | lib/lib.js:427:2:427:28 | spawn(" ... WN_OPT) | shell command | +| lib/lib.js:427:14:427:16 | arr | lib/lib.js:414:40:414:43 | name | lib/lib.js:427:14:427:16 | arr | This shell argument which depends on $@ is later used in a $@. | lib/lib.js:414:40:414:43 | name | library input | lib/lib.js:427:2:427:28 | spawn(" ... WN_OPT) | shell command | +| lib/lib.js:428:14:428:58 | build(" ... + '-') | lib/lib.js:414:40:414:43 | name | lib/lib.js:428:14:428:58 | build(" ... + '-') | This shell argument which depends on $@ is later used in a $@. | lib/lib.js:414:40:414:43 | name | library input | lib/lib.js:428:2:428:70 | spawn(" ... WN_OPT) | shell command | | lib/lib.js:436:19:436:22 | last | lib/lib.js:414:40:414:43 | name | lib/lib.js:436:19:436:22 | last | This shell argument which depends on $@ is later used in a $@. | lib/lib.js:414:40:414:43 | name | library input | lib/lib.js:428:2:428:70 | spawn(" ... WN_OPT) | shell command | | lib/lib.js:442:12:442:27 | "rm -rf " + name | lib/lib.js:441:39:441:42 | name | lib/lib.js:442:24:442:27 | name | This string concatenation which depends on $@ is later used in a $@. | lib/lib.js:441:39:441:42 | name | library input | lib/lib.js:442:2:442:28 | asyncEx ... + name) | shell command | | lib/lib.js:447:13:447:28 | "rm -rf " + name | lib/lib.js:446:20:446:23 | name | lib/lib.js:447:25:447:28 | name | This string concatenation which depends on $@ is later used in a $@. | lib/lib.js:446:20:446:23 | name | library input | lib/lib.js:447:3:447:29 | asyncEx ... + name) | shell command | @@ -760,3 +782,4 @@ edges | lib/subLib/amdSub.js:4:10:4:25 | "rm -rf " + name | lib/subLib/amdSub.js:3:28:3:31 | name | lib/subLib/amdSub.js:4:22:4:25 | name | This string concatenation which depends on $@ is later used in a $@. | lib/subLib/amdSub.js:3:28:3:31 | name | library input | lib/subLib/amdSub.js:4:2:4:26 | cp.exec ... + name) | shell command | | lib/subLib/index.js:4:10:4:25 | "rm -rf " + name | lib/subLib/index.js:3:28:3:31 | name | lib/subLib/index.js:4:22:4:25 | name | This string concatenation which depends on $@ is later used in a $@. | lib/subLib/index.js:3:28:3:31 | name | library input | lib/subLib/index.js:4:2:4:26 | cp.exec ... + name) | shell command | | lib/subLib/index.js:8:10:8:25 | "rm -rf " + name | lib/subLib/index.js:7:32:7:35 | name | lib/subLib/index.js:8:22:8:25 | name | This string concatenation which depends on $@ is later used in a $@. | lib/subLib/index.js:7:32:7:35 | name | library input | lib/subLib/index.js:8:2:8:26 | cp.exec ... + name) | shell command | +| lib/subLib/index.js:14:22:14:24 | arr | lib/subLib/index.js:13:44:13:46 | arr | lib/subLib/index.js:14:22:14:24 | arr | This shell argument which depends on $@ is later used in a $@. | lib/subLib/index.js:13:44:13:46 | arr | library input | lib/subLib/index.js:14:5:14:40 | cp.spaw ... true}) | shell command | diff --git a/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction/lib/subLib/index.js b/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction/lib/subLib/index.js index 9e105338669..6e7d3498723 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction/lib/subLib/index.js +++ b/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction/lib/subLib/index.js @@ -8,4 +8,8 @@ module.exports.foo = function (name) { cp.exec("rm -rf " + name); // NOT OK - this is being called explicitly from child_process-test.js }; -module.exports.amd = require("./amd.js"); \ No newline at end of file +module.exports.amd = require("./amd.js"); + +module.exports.arrToShell = function (cmd, arr) { + cp.spawn("echo", arr, {shell: true}); // NOT OK +} \ No newline at end of file diff --git a/javascript/ql/test/query-tests/Security/CWE-400/ReDoS/PolynomialBackTracking.expected b/javascript/ql/test/query-tests/Security/CWE-400/ReDoS/PolynomialBackTracking.expected index e1ad3adb91c..6ed2af353f5 100644 --- a/javascript/ql/test/query-tests/Security/CWE-400/ReDoS/PolynomialBackTracking.expected +++ b/javascript/ql/test/query-tests/Security/CWE-400/ReDoS/PolynomialBackTracking.expected @@ -33,6 +33,8 @@ | lib/lib.js:8:3:8:4 | f* | Strings with many repetitions of 'f' can start matching anywhere after the start of the preceeding f*g | | lib/lib.js:28:3:28:4 | f* | Strings with many repetitions of 'f' can start matching anywhere after the start of the preceeding f*g | | lib/lib.js:36:3:36:4 | f* | Strings with many repetitions of 'f' can start matching anywhere after the start of the preceeding f*g | +| lib/lib.js:42:29:42:30 | f* | Strings with many repetitions of 'f' can start matching anywhere after the start of the preceeding f*g | +| lib/lib.js:45:29:45:30 | f* | Strings with many repetitions of 'f' can start matching anywhere after the start of the preceeding f*g | | lib/moduleLib/moduleLib.js:2:3:2:4 | a* | Strings with many repetitions of 'a' can start matching anywhere after the start of the preceeding a*b | | lib/otherLib/js/src/index.js:2:3:2:4 | a* | Strings with many repetitions of 'a' can start matching anywhere after the start of the preceeding a*b | | lib/snapdragon.js:7:28:7:29 | a* | Strings starting with 'a' and with many repetitions of 'a' can start matching anywhere after the start of the preceeding aa*$ | diff --git a/javascript/ql/test/query-tests/Security/CWE-400/ReDoS/PolynomialReDoS.expected b/javascript/ql/test/query-tests/Security/CWE-400/ReDoS/PolynomialReDoS.expected index 3df7db24964..04bf2cbad36 100644 --- a/javascript/ql/test/query-tests/Security/CWE-400/ReDoS/PolynomialReDoS.expected +++ b/javascript/ql/test/query-tests/Security/CWE-400/ReDoS/PolynomialReDoS.expected @@ -28,6 +28,15 @@ nodes | lib/lib.js:35:28:35:31 | name | | lib/lib.js:36:13:36:16 | name | | lib/lib.js:36:13:36:16 | name | +| lib/lib.js:41:32:41:35 | name | +| lib/lib.js:41:32:41:35 | name | +| lib/lib.js:42:17:42:20 | name | +| lib/lib.js:42:17:42:20 | name | +| lib/lib.js:44:5:44:25 | name | +| lib/lib.js:44:12:44:15 | name | +| lib/lib.js:44:12:44:25 | name.substr(1) | +| lib/lib.js:45:17:45:20 | name | +| lib/lib.js:45:17:45:20 | name | | lib/moduleLib/moduleLib.js:1:28:1:31 | name | | lib/moduleLib/moduleLib.js:1:28:1:31 | name | | lib/moduleLib/moduleLib.js:2:13:2:16 | name | @@ -249,6 +258,16 @@ edges | lib/lib.js:35:1:37:1 | 'arguments' object of function usedWithArguments | lib/lib.js:35:28:35:31 | name | | lib/lib.js:35:28:35:31 | name | lib/lib.js:36:13:36:16 | name | | lib/lib.js:35:28:35:31 | name | lib/lib.js:36:13:36:16 | name | +| lib/lib.js:41:32:41:35 | name | lib/lib.js:42:17:42:20 | name | +| lib/lib.js:41:32:41:35 | name | lib/lib.js:42:17:42:20 | name | +| lib/lib.js:41:32:41:35 | name | lib/lib.js:42:17:42:20 | name | +| lib/lib.js:41:32:41:35 | name | lib/lib.js:42:17:42:20 | name | +| lib/lib.js:41:32:41:35 | name | lib/lib.js:44:12:44:15 | name | +| lib/lib.js:41:32:41:35 | name | lib/lib.js:44:12:44:15 | name | +| lib/lib.js:44:5:44:25 | name | lib/lib.js:45:17:45:20 | name | +| lib/lib.js:44:5:44:25 | name | lib/lib.js:45:17:45:20 | name | +| lib/lib.js:44:12:44:15 | name | lib/lib.js:44:12:44:25 | name.substr(1) | +| lib/lib.js:44:12:44:25 | name.substr(1) | lib/lib.js:44:5:44:25 | name | | lib/moduleLib/moduleLib.js:1:28:1:31 | name | lib/moduleLib/moduleLib.js:2:13:2:16 | name | | lib/moduleLib/moduleLib.js:1:28:1:31 | name | lib/moduleLib/moduleLib.js:2:13:2:16 | name | | lib/moduleLib/moduleLib.js:1:28:1:31 | name | lib/moduleLib/moduleLib.js:2:13:2:16 | name | @@ -440,6 +459,8 @@ edges | lib/lib.js:4:2:4:18 | regexp.test(name) | lib/lib.js:3:28:3:31 | name | lib/lib.js:4:14:4:17 | name | This $@ that depends on $@ may run slow on strings with many repetitions of 'a'. | lib/lib.js:1:15:1:16 | a* | regular expression | lib/lib.js:3:28:3:31 | name | library input | | lib/lib.js:8:2:8:17 | /f*g/.test(name) | lib/lib.js:7:19:7:22 | name | lib/lib.js:8:13:8:16 | name | This $@ that depends on $@ may run slow on strings with many repetitions of 'f'. | lib/lib.js:8:3:8:4 | f* | regular expression | lib/lib.js:7:19:7:22 | name | library input | | lib/lib.js:36:2:36:17 | /f*g/.test(name) | lib/lib.js:32:32:32:40 | arguments | lib/lib.js:36:13:36:16 | name | This $@ that depends on $@ may run slow on strings with many repetitions of 'f'. | lib/lib.js:36:3:36:4 | f* | regular expression | lib/lib.js:32:32:32:40 | arguments | library input | +| lib/lib.js:42:17:42:33 | name.match(/f*g/) | lib/lib.js:41:32:41:35 | name | lib/lib.js:42:17:42:20 | name | This $@ that depends on $@ may run slow on strings with many repetitions of 'f'. | lib/lib.js:42:29:42:30 | f* | regular expression | lib/lib.js:41:32:41:35 | name | library input | +| lib/lib.js:45:17:45:33 | name.match(/f*g/) | lib/lib.js:41:32:41:35 | name | lib/lib.js:45:17:45:20 | name | This $@ that depends on $@ may run slow on strings with many repetitions of 'f'. | lib/lib.js:45:29:45:30 | f* | regular expression | lib/lib.js:41:32:41:35 | name | library input | | lib/moduleLib/moduleLib.js:2:2:2:17 | /a*b/.test(name) | lib/moduleLib/moduleLib.js:1:28:1:31 | name | lib/moduleLib/moduleLib.js:2:13:2:16 | name | This $@ that depends on $@ may run slow on strings with many repetitions of 'a'. | lib/moduleLib/moduleLib.js:2:3:2:4 | a* | regular expression | lib/moduleLib/moduleLib.js:1:28:1:31 | name | library input | | lib/otherLib/js/src/index.js:2:2:2:17 | /a*b/.test(name) | lib/otherLib/js/src/index.js:1:28:1:31 | name | lib/otherLib/js/src/index.js:2:13:2:16 | name | This $@ that depends on $@ may run slow on strings with many repetitions of 'a'. | lib/otherLib/js/src/index.js:2:3:2:4 | a* | regular expression | lib/otherLib/js/src/index.js:1:28:1:31 | name | library input | | lib/snapdragon.js:7:15:7:32 | this.match(/aa*$/) | lib/snapdragon.js:3:34:3:38 | input | lib/snapdragon.js:7:15:7:18 | this | This $@ that depends on $@ may run slow on strings starting with 'a' and with many repetitions of 'a'. | lib/snapdragon.js:7:28:7:29 | a* | regular expression | lib/snapdragon.js:3:34:3:38 | input | library input | diff --git a/javascript/ql/test/query-tests/Security/CWE-400/ReDoS/lib/lib.js b/javascript/ql/test/query-tests/Security/CWE-400/ReDoS/lib/lib.js index 5c892f328a3..73700dfbc6b 100644 --- a/javascript/ql/test/query-tests/Security/CWE-400/ReDoS/lib/lib.js +++ b/javascript/ql/test/query-tests/Security/CWE-400/ReDoS/lib/lib.js @@ -36,4 +36,11 @@ function usedWithArguments(name) { /f*g/.test(name); // NOT OK } -module.exports.snapdragon = require("./snapdragon") \ No newline at end of file +module.exports.snapdragon = require("./snapdragon") + +module.exports.foo = function (name) { + var data1 = name.match(/f*g/); // NOT OK + + name = name.substr(1); + var data2 = name.match(/f*g/); // NOT OK +} \ No newline at end of file diff --git a/python/ql/lib/change-notes/2022-11-21-module-resolution-rewrite.md b/python/ql/lib/change-notes/2022-11-21-module-resolution-rewrite.md new file mode 100644 index 00000000000..fa04c07e9d5 --- /dev/null +++ b/python/ql/lib/change-notes/2022-11-21-module-resolution-rewrite.md @@ -0,0 +1,5 @@ +--- + category: minorAnalysis +--- + * The data-flow library has been rewritten to no longer rely on the points-to analysis in order to + resolve references to modules. Improvements in the module resolution can lead to more results. diff --git a/python/ql/lib/semmle/python/dataflow/new/SensitiveDataSources.qll b/python/ql/lib/semmle/python/dataflow/new/SensitiveDataSources.qll index 65d334f1c38..926ae46d33e 100644 --- a/python/ql/lib/semmle/python/dataflow/new/SensitiveDataSources.qll +++ b/python/ql/lib/semmle/python/dataflow/new/SensitiveDataSources.qll @@ -8,6 +8,7 @@ private import semmle.python.dataflow.new.DataFlow // Need to import `semmle.python.Frameworks` since frameworks can extend `SensitiveDataSource::Range` private import semmle.python.Frameworks private import semmle.python.security.internal.SensitiveDataHeuristics as SensitiveDataHeuristics +private import semmle.python.ApiGraphs // We export these explicitly, so we don't also export the `HeuristicNames` module. class SensitiveDataClassification = SensitiveDataHeuristics::SensitiveDataClassification; @@ -23,7 +24,17 @@ module SensitiveDataClassification = SensitiveDataHeuristics::SensitiveDataClass class SensitiveDataSource extends DataFlow::Node { SensitiveDataSource::Range range; - SensitiveDataSource() { this = range } + SensitiveDataSource() { + this = range and + // ignore sensitive password sources in getpass.py, that can escape through `getpass.getpass()` return value, + // since `getpass.getpass()` is considered a source itself. + not exists(Module getpass | + getpass.getName() = "getpass" and + this.getScope().getEnclosingModule() = getpass and + // do allow this call if we're analyzing getpass.py as part of CPython though + not exists(getpass.getFile().getRelativePath()) + ) + } /** * Gets the classification of the sensitive data. @@ -312,6 +323,17 @@ private module SensitiveDataModeling { override SensitiveDataClassification getClassification() { result = classification } } + + /** + * A call to `getpass.getpass`, see https://docs.python.org/3.10/library/getpass.html#getpass.getpass + */ + class GetPassCall extends SensitiveDataSource::Range, API::CallNode { + GetPassCall() { this = API::moduleImport("getpass").getMember("getpass").getACall() } + + override SensitiveDataClassification getClassification() { + result = SensitiveDataClassification::password() + } + } } predicate sensitiveDataExtraStepForCalls = SensitiveDataModeling::extraStepForCalls/2; diff --git a/python/ql/src/change-notes/2022-11-22-getpass.md b/python/ql/src/change-notes/2022-11-22-getpass.md new file mode 100644 index 00000000000..d9df302bc63 --- /dev/null +++ b/python/ql/src/change-notes/2022-11-22-getpass.md @@ -0,0 +1,4 @@ +--- + category: minorAnalysis +--- + * Added modeling of `getpass.getpass` as a source of passwords, which will be an additional source for `py/clear-text-logging-sensitive-data`, `py/clear-text-storage-sensitive-data`, and `py/weak-sensitive-data-hashing`. diff --git a/python/ql/test/experimental/dataflow/TestUtil/NormalDataflowTest.qll b/python/ql/test/experimental/dataflow/TestUtil/NormalDataflowTest.qll index b0f0009b30d..f526a1f43ae 100644 --- a/python/ql/test/experimental/dataflow/TestUtil/NormalDataflowTest.qll +++ b/python/ql/test/experimental/dataflow/TestUtil/NormalDataflowTest.qll @@ -16,8 +16,9 @@ class DataFlowTest extends FlowTest { query predicate missingAnnotationOnSink(Location location, string error, string element) { error = "ERROR, you should add `# $ MISSING: flow` annotation" and exists(DataFlow::Node sink | + any(TestConfiguration config).isSink(sink) and + // note: we only care about `SINK` and not `SINK_F`, so we have to reconstruct manually. exists(DataFlow::CallCfgNode call | - // note: we only care about `SINK` and not `SINK_F`, so we have to reconstruct manually. call.getFunction().asCfgNode().(NameNode).getId() = "SINK" and (sink = call.getArg(_) or sink = call.getArgByName(_)) ) and diff --git a/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected b/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected deleted file mode 100644 index f297c4be6e3..00000000000 --- a/python/ql/test/experimental/dataflow/coverage/classesCallGraph.expected +++ /dev/null @@ -1,80 +0,0 @@ -| classes.py:14:17:14:60 | ControlFlowNode for Attribute() | classes.py:14:17:14:60 | ControlFlowNode for Attribute() | -| classes.py:45:16:45:35 | ControlFlowNode for Attribute() | classes.py:45:16:45:35 | ControlFlowNode for Attribute() | -| classes.py:60:17:60:27 | [pre objCreate] ControlFlowNode for With_init() | classes.py:54:18:54:21 | ControlFlowNode for self | -| classes.py:242:9:242:24 | ControlFlowNode for set() | classes.py:242:9:242:24 | ControlFlowNode for set() | -| classes.py:247:9:247:30 | ControlFlowNode for frozenset() | classes.py:247:9:247:30 | ControlFlowNode for frozenset() | -| classes.py:252:9:252:28 | ControlFlowNode for dict() | classes.py:252:9:252:28 | ControlFlowNode for dict() | -| classes.py:559:16:559:17 | ControlFlowNode for Str | classes.py:565:5:565:22 | ControlFlowNode for Subscript | -| classes.py:565:5:565:16 | ControlFlowNode for with_getitem | classes.py:555:21:555:24 | ControlFlowNode for self | -| classes.py:565:18:565:21 | ControlFlowNode for arg2 | classes.py:555:27:555:29 | ControlFlowNode for key | -| classes.py:581:5:581:16 | ControlFlowNode for with_setitem | classes.py:570:21:570:24 | ControlFlowNode for self | -| classes.py:581:18:581:21 | ControlFlowNode for arg2 | classes.py:570:27:570:29 | ControlFlowNode for key | -| classes.py:581:26:581:29 | ControlFlowNode for arg3 | classes.py:570:32:570:36 | ControlFlowNode for value | -| classes.py:595:9:595:20 | ControlFlowNode for with_delitem | classes.py:586:21:586:24 | ControlFlowNode for self | -| classes.py:595:22:595:25 | ControlFlowNode for arg2 | classes.py:586:27:586:29 | ControlFlowNode for key | -| classes.py:618:16:618:28 | ControlFlowNode for Attribute() | classes.py:618:16:618:28 | ControlFlowNode for Attribute() | -| classes.py:659:15:659:18 | ControlFlowNode for self | classes.py:667:5:667:19 | ControlFlowNode for BinaryExpr | -| classes.py:661:16:661:19 | ControlFlowNode for self | classes.py:667:5:667:19 | ControlFlowNode for BinaryExpr | -| classes.py:667:5:667:12 | ControlFlowNode for with_add | classes.py:657:17:657:20 | ControlFlowNode for self | -| classes.py:667:5:667:12 | ControlFlowNode for with_add | classes.py:667:5:667:19 | ControlFlowNode for BinaryExpr | -| classes.py:667:16:667:19 | ControlFlowNode for arg2 | classes.py:657:23:657:27 | ControlFlowNode for other | -| classes.py:674:15:674:18 | ControlFlowNode for self | classes.py:682:5:682:19 | ControlFlowNode for BinaryExpr | -| classes.py:676:16:676:19 | ControlFlowNode for self | classes.py:682:5:682:19 | ControlFlowNode for BinaryExpr | -| classes.py:682:5:682:12 | ControlFlowNode for with_sub | classes.py:672:17:672:20 | ControlFlowNode for self | -| classes.py:682:5:682:12 | ControlFlowNode for with_sub | classes.py:682:5:682:19 | ControlFlowNode for BinaryExpr | -| classes.py:682:16:682:19 | ControlFlowNode for arg2 | classes.py:672:23:672:27 | ControlFlowNode for other | -| classes.py:689:15:689:18 | ControlFlowNode for self | classes.py:697:5:697:19 | ControlFlowNode for BinaryExpr | -| classes.py:691:16:691:19 | ControlFlowNode for self | classes.py:697:5:697:19 | ControlFlowNode for BinaryExpr | -| classes.py:697:5:697:12 | ControlFlowNode for with_mul | classes.py:687:17:687:20 | ControlFlowNode for self | -| classes.py:697:5:697:12 | ControlFlowNode for with_mul | classes.py:697:5:697:19 | ControlFlowNode for BinaryExpr | -| classes.py:697:16:697:19 | ControlFlowNode for arg2 | classes.py:687:23:687:27 | ControlFlowNode for other | -| classes.py:704:15:704:18 | ControlFlowNode for self | classes.py:712:5:712:22 | ControlFlowNode for BinaryExpr | -| classes.py:706:16:706:19 | ControlFlowNode for self | classes.py:712:5:712:22 | ControlFlowNode for BinaryExpr | -| classes.py:712:5:712:15 | ControlFlowNode for with_matmul | classes.py:702:20:702:23 | ControlFlowNode for self | -| classes.py:712:5:712:15 | ControlFlowNode for with_matmul | classes.py:712:5:712:22 | ControlFlowNode for BinaryExpr | -| classes.py:712:19:712:22 | ControlFlowNode for arg2 | classes.py:702:26:702:30 | ControlFlowNode for other | -| classes.py:719:15:719:18 | ControlFlowNode for self | classes.py:727:5:727:23 | ControlFlowNode for BinaryExpr | -| classes.py:721:16:721:19 | ControlFlowNode for self | classes.py:727:5:727:23 | ControlFlowNode for BinaryExpr | -| classes.py:727:5:727:16 | ControlFlowNode for with_truediv | classes.py:717:21:717:24 | ControlFlowNode for self | -| classes.py:727:5:727:16 | ControlFlowNode for with_truediv | classes.py:727:5:727:23 | ControlFlowNode for BinaryExpr | -| classes.py:727:20:727:23 | ControlFlowNode for arg2 | classes.py:717:27:717:31 | ControlFlowNode for other | -| classes.py:734:15:734:18 | ControlFlowNode for self | classes.py:742:5:742:25 | ControlFlowNode for BinaryExpr | -| classes.py:736:16:736:19 | ControlFlowNode for self | classes.py:742:5:742:25 | ControlFlowNode for BinaryExpr | -| classes.py:742:5:742:17 | ControlFlowNode for with_floordiv | classes.py:732:22:732:25 | ControlFlowNode for self | -| classes.py:742:5:742:17 | ControlFlowNode for with_floordiv | classes.py:742:5:742:25 | ControlFlowNode for BinaryExpr | -| classes.py:742:22:742:25 | ControlFlowNode for arg2 | classes.py:732:28:732:32 | ControlFlowNode for other | -| classes.py:749:15:749:18 | ControlFlowNode for self | classes.py:757:5:757:19 | ControlFlowNode for BinaryExpr | -| classes.py:751:16:751:19 | ControlFlowNode for self | classes.py:757:5:757:19 | ControlFlowNode for BinaryExpr | -| classes.py:757:5:757:12 | ControlFlowNode for with_mod | classes.py:747:17:747:20 | ControlFlowNode for self | -| classes.py:757:5:757:12 | ControlFlowNode for with_mod | classes.py:757:5:757:19 | ControlFlowNode for BinaryExpr | -| classes.py:757:16:757:19 | ControlFlowNode for arg2 | classes.py:747:23:747:27 | ControlFlowNode for other | -| classes.py:779:15:779:18 | ControlFlowNode for self | classes.py:793:5:793:20 | ControlFlowNode for BinaryExpr | -| classes.py:781:16:781:19 | ControlFlowNode for self | classes.py:793:5:793:20 | ControlFlowNode for BinaryExpr | -| classes.py:793:5:793:12 | ControlFlowNode for with_pow | classes.py:777:17:777:20 | ControlFlowNode for self | -| classes.py:793:5:793:12 | ControlFlowNode for with_pow | classes.py:793:5:793:20 | ControlFlowNode for BinaryExpr | -| classes.py:793:17:793:20 | ControlFlowNode for arg2 | classes.py:777:23:777:27 | ControlFlowNode for other | -| classes.py:800:15:800:18 | ControlFlowNode for self | classes.py:808:5:808:23 | ControlFlowNode for BinaryExpr | -| classes.py:802:16:802:19 | ControlFlowNode for self | classes.py:808:5:808:23 | ControlFlowNode for BinaryExpr | -| classes.py:808:5:808:15 | ControlFlowNode for with_lshift | classes.py:798:20:798:23 | ControlFlowNode for self | -| classes.py:808:5:808:15 | ControlFlowNode for with_lshift | classes.py:808:5:808:23 | ControlFlowNode for BinaryExpr | -| classes.py:808:20:808:23 | ControlFlowNode for arg2 | classes.py:798:26:798:30 | ControlFlowNode for other | -| classes.py:815:15:815:18 | ControlFlowNode for self | classes.py:823:5:823:23 | ControlFlowNode for BinaryExpr | -| classes.py:817:16:817:19 | ControlFlowNode for self | classes.py:823:5:823:23 | ControlFlowNode for BinaryExpr | -| classes.py:823:5:823:15 | ControlFlowNode for with_rshift | classes.py:813:20:813:23 | ControlFlowNode for self | -| classes.py:823:5:823:15 | ControlFlowNode for with_rshift | classes.py:823:5:823:23 | ControlFlowNode for BinaryExpr | -| classes.py:823:20:823:23 | ControlFlowNode for arg2 | classes.py:813:26:813:30 | ControlFlowNode for other | -| classes.py:830:15:830:18 | ControlFlowNode for self | classes.py:838:5:838:19 | ControlFlowNode for BinaryExpr | -| classes.py:832:16:832:19 | ControlFlowNode for self | classes.py:838:5:838:19 | ControlFlowNode for BinaryExpr | -| classes.py:838:5:838:12 | ControlFlowNode for with_and | classes.py:828:17:828:20 | ControlFlowNode for self | -| classes.py:838:5:838:12 | ControlFlowNode for with_and | classes.py:838:5:838:19 | ControlFlowNode for BinaryExpr | -| classes.py:838:16:838:19 | ControlFlowNode for arg2 | classes.py:828:23:828:27 | ControlFlowNode for other | -| classes.py:845:15:845:18 | ControlFlowNode for self | classes.py:853:5:853:19 | ControlFlowNode for BinaryExpr | -| classes.py:847:16:847:19 | ControlFlowNode for self | classes.py:853:5:853:19 | ControlFlowNode for BinaryExpr | -| classes.py:853:5:853:12 | ControlFlowNode for with_xor | classes.py:843:17:843:20 | ControlFlowNode for self | -| classes.py:853:5:853:12 | ControlFlowNode for with_xor | classes.py:853:5:853:19 | ControlFlowNode for BinaryExpr | -| classes.py:853:16:853:19 | ControlFlowNode for arg2 | classes.py:843:23:843:27 | ControlFlowNode for other | -| classes.py:860:15:860:18 | ControlFlowNode for self | classes.py:868:5:868:18 | ControlFlowNode for BinaryExpr | -| classes.py:862:16:862:19 | ControlFlowNode for self | classes.py:868:5:868:18 | ControlFlowNode for BinaryExpr | -| classes.py:868:5:868:11 | ControlFlowNode for with_or | classes.py:858:16:858:19 | ControlFlowNode for self | -| classes.py:868:5:868:11 | ControlFlowNode for with_or | classes.py:868:5:868:18 | ControlFlowNode for BinaryExpr | -| classes.py:868:15:868:18 | ControlFlowNode for arg2 | classes.py:858:22:858:26 | ControlFlowNode for other | diff --git a/python/ql/test/experimental/dataflow/coverage/classesCallGraph.ql b/python/ql/test/experimental/dataflow/coverage/classesCallGraph.ql deleted file mode 100644 index c1b66d0f323..00000000000 --- a/python/ql/test/experimental/dataflow/coverage/classesCallGraph.ql +++ /dev/null @@ -1,37 +0,0 @@ -import semmle.python.dataflow.new.DataFlow -private import semmle.python.dataflow.new.internal.DataFlowPrivate as DataFlowPrivate - -/** - * A configuration to find the call graph edges. - */ -class CallGraphConfig extends DataFlow::Configuration { - CallGraphConfig() { this = "CallGraphConfig" } - - override predicate isSource(DataFlow::Node node) { - node instanceof DataFlowPrivate::ReturnNode - or - // These sources should allow for the non-standard call syntax - node instanceof DataFlow::ArgumentNode - } - - override predicate isSink(DataFlow::Node node) { - node instanceof DataFlowPrivate::OutNode - or - node instanceof DataFlow::ParameterNode and - // exclude parameters to the SINK-functions - not exists(DataFlowPrivate::DataFlowCallable c | - c.getParameter(_) = node.asCfgNode() and - c.getName().matches("SINK_") - ) - } -} - -from DataFlow::Node source, DataFlow::Node sink -where - source.getLocation().getFile().getBaseName() = "classes.py" and - sink.getLocation().getFile().getBaseName() = "classes.py" and - exists(CallGraphConfig cfg | cfg.hasFlow(source, sink)) -select source, sink -// Ideally, we would just have 1-step paths either from argument to parameter -// or from return to call. This gives a bit more, so should be rewritten. -// We should also consider splitting this into two, one for each direction. diff --git a/python/ql/test/experimental/dataflow/coverage/dataflow.expected b/python/ql/test/experimental/dataflow/coverage/dataflow.expected deleted file mode 100644 index c7a0294d66a..00000000000 --- a/python/ql/test/experimental/dataflow/coverage/dataflow.expected +++ /dev/null @@ -1,970 +0,0 @@ -edges -| datamodel.py:35:7:35:7 | ControlFlowNode for a | datamodel.py:36:10:36:10 | ControlFlowNode for a | -| datamodel.py:38:8:38:13 | ControlFlowNode for SOURCE | datamodel.py:35:7:35:7 | ControlFlowNode for a | -| datamodel.py:38:8:38:13 | ControlFlowNode for SOURCE | datamodel.py:38:6:38:17 | ControlFlowNode for f() | -| datamodel.py:44:22:44:22 | ControlFlowNode for x | datamodel.py:46:16:46:16 | ControlFlowNode for x | -| datamodel.py:49:26:49:26 | ControlFlowNode for x | datamodel.py:50:16:50:16 | ControlFlowNode for x | -| datamodel.py:71:15:71:20 | ControlFlowNode for SOURCE | datamodel.py:44:22:44:22 | ControlFlowNode for x | -| datamodel.py:71:15:71:20 | ControlFlowNode for SOURCE | datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() | -| datamodel.py:72:18:72:23 | ControlFlowNode for SOURCE | datamodel.py:44:22:44:22 | ControlFlowNode for x | -| datamodel.py:72:18:72:23 | ControlFlowNode for SOURCE | datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | -| datamodel.py:80:20:80:25 | ControlFlowNode for SOURCE | datamodel.py:49:26:49:26 | ControlFlowNode for x | -| datamodel.py:80:20:80:25 | ControlFlowNode for SOURCE | datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | -| datamodel.py:81:20:81:25 | ControlFlowNode for SOURCE | datamodel.py:49:26:49:26 | ControlFlowNode for x | -| datamodel.py:81:20:81:25 | ControlFlowNode for SOURCE | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | -| datamodel.py:152:5:152:8 | [post store] ControlFlowNode for self [Attribute b] | datamodel.py:155:14:155:25 | ControlFlowNode for Customized() [Attribute b] | -| datamodel.py:152:14:152:19 | ControlFlowNode for SOURCE | datamodel.py:152:5:152:8 | [post store] ControlFlowNode for self [Attribute b] | -| datamodel.py:155:14:155:25 | ControlFlowNode for Customized() [Attribute b] | datamodel.py:159:6:159:15 | ControlFlowNode for customized [Attribute b] | -| datamodel.py:159:6:159:15 | ControlFlowNode for customized [Attribute b] | datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | -| test.py:42:10:42:26 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:43:9:43:9 | ControlFlowNode for x [Tuple element at index 1] | -| test.py:42:21:42:26 | ControlFlowNode for SOURCE | test.py:42:10:42:26 | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:43:9:43:9 | ControlFlowNode for x [Tuple element at index 1] | test.py:43:9:43:12 | ControlFlowNode for Subscript | -| test.py:43:9:43:12 | ControlFlowNode for Subscript | test.py:44:10:44:10 | ControlFlowNode for y | -| test.py:55:9:55:14 | ControlFlowNode for SOURCE | test.py:56:10:56:10 | ControlFlowNode for x | -| test.py:61:9:61:16 | ControlFlowNode for Str | test.py:62:10:62:10 | ControlFlowNode for x | -| test.py:66:9:66:17 | ControlFlowNode for Str | test.py:67:10:67:10 | ControlFlowNode for x | -| test.py:71:9:71:10 | ControlFlowNode for IntegerLiteral | test.py:72:10:72:10 | ControlFlowNode for x | -| test.py:76:9:76:12 | ControlFlowNode for FloatLiteral | test.py:77:10:77:10 | ControlFlowNode for x | -| test.py:87:10:87:15 | ControlFlowNode for SOURCE | test.py:88:10:88:10 | ControlFlowNode for x | -| test.py:93:9:93:16 | ControlFlowNode for List [List element] | test.py:94:10:94:10 | ControlFlowNode for x [List element] | -| test.py:93:10:93:15 | ControlFlowNode for SOURCE | test.py:93:9:93:16 | ControlFlowNode for List [List element] | -| test.py:94:10:94:10 | ControlFlowNode for x [List element] | test.py:94:10:94:13 | ControlFlowNode for Subscript | -| test.py:103:9:103:37 | ControlFlowNode for ListComp [List element] | test.py:104:10:104:10 | ControlFlowNode for x [List element] | -| test.py:103:10:103:15 | ControlFlowNode for SOURCE | test.py:103:9:103:37 | ControlFlowNode for ListComp [List element] | -| test.py:104:10:104:10 | ControlFlowNode for x [List element] | test.py:104:10:104:13 | ControlFlowNode for Subscript | -| test.py:108:9:108:29 | ControlFlowNode for ListComp [List element] | test.py:109:10:109:10 | ControlFlowNode for x [List element] | -| test.py:108:10:108:10 | ControlFlowNode for y | test.py:108:9:108:29 | ControlFlowNode for ListComp [List element] | -| test.py:108:16:108:16 | SSA variable y | test.py:108:10:108:10 | ControlFlowNode for y | -| test.py:108:21:108:28 | ControlFlowNode for List [List element] | test.py:108:16:108:16 | SSA variable y | -| test.py:108:22:108:27 | ControlFlowNode for SOURCE | test.py:108:21:108:28 | ControlFlowNode for List [List element] | -| test.py:109:10:109:10 | ControlFlowNode for x [List element] | test.py:109:10:109:13 | ControlFlowNode for Subscript | -| test.py:113:9:113:16 | ControlFlowNode for List [List element] | test.py:114:21:114:21 | ControlFlowNode for l [List element] | -| test.py:113:10:113:15 | ControlFlowNode for SOURCE | test.py:113:9:113:16 | ControlFlowNode for List [List element] | -| test.py:114:9:114:22 | ControlFlowNode for ListComp [List element] | test.py:115:10:115:10 | ControlFlowNode for x [List element] | -| test.py:114:10:114:10 | ControlFlowNode for y | test.py:114:9:114:22 | ControlFlowNode for ListComp [List element] | -| test.py:114:16:114:16 | SSA variable y | test.py:114:10:114:10 | ControlFlowNode for y | -| test.py:114:21:114:21 | ControlFlowNode for l [List element] | test.py:114:16:114:16 | SSA variable y | -| test.py:115:10:115:10 | ControlFlowNode for x [List element] | test.py:115:10:115:13 | ControlFlowNode for Subscript | -| test.py:125:9:125:16 | ControlFlowNode for Set [Set element] | test.py:126:10:126:10 | ControlFlowNode for x [Set element] | -| test.py:125:10:125:15 | ControlFlowNode for SOURCE | test.py:125:9:125:16 | ControlFlowNode for Set [Set element] | -| test.py:126:10:126:10 | ControlFlowNode for x [Set element] | test.py:126:10:126:16 | ControlFlowNode for Attribute() | -| test.py:130:9:130:37 | ControlFlowNode for SetComp [Set element] | test.py:131:10:131:10 | ControlFlowNode for x [Set element] | -| test.py:130:10:130:15 | ControlFlowNode for SOURCE | test.py:130:9:130:37 | ControlFlowNode for SetComp [Set element] | -| test.py:131:10:131:10 | ControlFlowNode for x [Set element] | test.py:131:10:131:16 | ControlFlowNode for Attribute() | -| test.py:135:9:135:29 | ControlFlowNode for SetComp [Set element] | test.py:136:10:136:10 | ControlFlowNode for x [Set element] | -| test.py:135:10:135:10 | ControlFlowNode for y | test.py:135:9:135:29 | ControlFlowNode for SetComp [Set element] | -| test.py:135:16:135:16 | SSA variable y | test.py:135:10:135:10 | ControlFlowNode for y | -| test.py:135:21:135:28 | ControlFlowNode for List [List element] | test.py:135:16:135:16 | SSA variable y | -| test.py:135:22:135:27 | ControlFlowNode for SOURCE | test.py:135:21:135:28 | ControlFlowNode for List [List element] | -| test.py:136:10:136:10 | ControlFlowNode for x [Set element] | test.py:136:10:136:16 | ControlFlowNode for Attribute() | -| test.py:140:9:140:16 | ControlFlowNode for Set [Set element] | test.py:141:21:141:21 | ControlFlowNode for l [Set element] | -| test.py:140:10:140:15 | ControlFlowNode for SOURCE | test.py:140:9:140:16 | ControlFlowNode for Set [Set element] | -| test.py:141:9:141:22 | ControlFlowNode for SetComp [Set element] | test.py:142:10:142:10 | ControlFlowNode for x [Set element] | -| test.py:141:10:141:10 | ControlFlowNode for y | test.py:141:9:141:22 | ControlFlowNode for SetComp [Set element] | -| test.py:141:16:141:16 | SSA variable y | test.py:141:10:141:10 | ControlFlowNode for y | -| test.py:141:21:141:21 | ControlFlowNode for l [Set element] | test.py:141:16:141:16 | SSA variable y | -| test.py:142:10:142:10 | ControlFlowNode for x [Set element] | test.py:142:10:142:16 | ControlFlowNode for Attribute() | -| test.py:152:9:152:21 | ControlFlowNode for Dict [Dictionary element at key s] | test.py:153:10:153:10 | ControlFlowNode for x [Dictionary element at key s] | -| test.py:152:15:152:20 | ControlFlowNode for SOURCE | test.py:152:9:152:21 | ControlFlowNode for Dict [Dictionary element at key s] | -| test.py:153:10:153:10 | ControlFlowNode for x [Dictionary element at key s] | test.py:153:10:153:15 | ControlFlowNode for Subscript | -| test.py:157:9:157:21 | ControlFlowNode for Dict [Dictionary element at key s] | test.py:158:10:158:10 | ControlFlowNode for x [Dictionary element at key s] | -| test.py:157:15:157:20 | ControlFlowNode for SOURCE | test.py:157:9:157:21 | ControlFlowNode for Dict [Dictionary element at key s] | -| test.py:158:10:158:10 | ControlFlowNode for x [Dictionary element at key s] | test.py:158:10:158:19 | ControlFlowNode for Attribute() | -| test.py:183:9:183:42 | ControlFlowNode for ListComp [List element] | test.py:184:10:184:10 | ControlFlowNode for x [List element] | -| test.py:183:10:183:10 | ControlFlowNode for y | test.py:183:9:183:42 | ControlFlowNode for ListComp [List element] | -| test.py:183:16:183:16 | SSA variable z [List element] | test.py:183:41:183:41 | ControlFlowNode for z [List element] | -| test.py:183:21:183:30 | ControlFlowNode for List [List element, List element] | test.py:183:16:183:16 | SSA variable z [List element] | -| test.py:183:22:183:29 | ControlFlowNode for List [List element] | test.py:183:21:183:30 | ControlFlowNode for List [List element, List element] | -| test.py:183:23:183:28 | ControlFlowNode for SOURCE | test.py:183:22:183:29 | ControlFlowNode for List [List element] | -| test.py:183:36:183:36 | SSA variable y | test.py:183:10:183:10 | ControlFlowNode for y | -| test.py:183:41:183:41 | ControlFlowNode for z [List element] | test.py:183:36:183:36 | SSA variable y | -| test.py:184:10:184:10 | ControlFlowNode for x [List element] | test.py:184:10:184:13 | ControlFlowNode for Subscript | -| test.py:188:9:188:68 | ControlFlowNode for ListComp [List element] | test.py:189:10:189:10 | ControlFlowNode for x [List element] | -| test.py:188:10:188:10 | ControlFlowNode for y | test.py:188:9:188:68 | ControlFlowNode for ListComp [List element] | -| test.py:188:16:188:16 | SSA variable v [List element, List element, List element] | test.py:188:45:188:45 | ControlFlowNode for v [List element, List element, List element] | -| test.py:188:21:188:34 | ControlFlowNode for List [List element, List element, List element, List element] | test.py:188:16:188:16 | SSA variable v [List element, List element, List element] | -| test.py:188:22:188:33 | ControlFlowNode for List [List element, List element, List element] | test.py:188:21:188:34 | ControlFlowNode for List [List element, List element, List element, List element] | -| test.py:188:23:188:32 | ControlFlowNode for List [List element, List element] | test.py:188:22:188:33 | ControlFlowNode for List [List element, List element, List element] | -| test.py:188:24:188:31 | ControlFlowNode for List [List element] | test.py:188:23:188:32 | ControlFlowNode for List [List element, List element] | -| test.py:188:25:188:30 | ControlFlowNode for SOURCE | test.py:188:24:188:31 | ControlFlowNode for List [List element] | -| test.py:188:40:188:40 | SSA variable u [List element, List element] | test.py:188:56:188:56 | ControlFlowNode for u [List element, List element] | -| test.py:188:45:188:45 | ControlFlowNode for v [List element, List element, List element] | test.py:188:40:188:40 | SSA variable u [List element, List element] | -| test.py:188:51:188:51 | SSA variable z [List element] | test.py:188:67:188:67 | ControlFlowNode for z [List element] | -| test.py:188:56:188:56 | ControlFlowNode for u [List element, List element] | test.py:188:51:188:51 | SSA variable z [List element] | -| test.py:188:62:188:62 | SSA variable y | test.py:188:10:188:10 | ControlFlowNode for y | -| test.py:188:67:188:67 | ControlFlowNode for z [List element] | test.py:188:62:188:62 | SSA variable y | -| test.py:189:10:189:10 | ControlFlowNode for x [List element] | test.py:189:10:189:13 | ControlFlowNode for Subscript | -| test.py:199:9:199:42 | ControlFlowNode for ListComp [List element] | test.py:200:10:200:10 | ControlFlowNode for x [List element] | -| test.py:199:10:199:10 | ControlFlowNode for y | test.py:199:9:199:42 | ControlFlowNode for ListComp [List element] | -| test.py:199:16:199:16 | SSA variable y | test.py:199:10:199:10 | ControlFlowNode for y | -| test.py:199:22:199:22 | ControlFlowNode for z | test.py:199:22:199:40 | ControlFlowNode for GeneratorExp [List element] | -| test.py:199:22:199:40 | ControlFlowNode for GeneratorExp [List element] | test.py:199:16:199:16 | SSA variable y | -| test.py:199:28:199:28 | SSA variable z | test.py:199:22:199:22 | ControlFlowNode for z | -| test.py:199:33:199:40 | ControlFlowNode for List [List element] | test.py:199:28:199:28 | SSA variable z | -| test.py:199:34:199:39 | ControlFlowNode for SOURCE | test.py:199:33:199:40 | ControlFlowNode for List [List element] | -| test.py:200:10:200:10 | ControlFlowNode for x [List element] | test.py:200:10:200:13 | ControlFlowNode for Subscript | -| test.py:205:9:205:47 | ControlFlowNode for ListComp [List element] | test.py:206:10:206:10 | ControlFlowNode for x [List element] | -| test.py:205:10:205:10 | ControlFlowNode for a | test.py:205:9:205:47 | ControlFlowNode for ListComp [List element] | -| test.py:205:17:205:17 | SSA variable a | test.py:205:10:205:10 | ControlFlowNode for a | -| test.py:205:17:205:20 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:205:17:205:17 | SSA variable a | -| test.py:205:17:205:20 | IterableSequence [Tuple element at index 0] | test.py:205:17:205:20 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:205:26:205:46 | ControlFlowNode for List [List element, Tuple element at index 0] | test.py:205:17:205:20 | IterableSequence [Tuple element at index 0] | -| test.py:205:28:205:33 | ControlFlowNode for SOURCE | test.py:205:28:205:44 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:205:28:205:44 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:205:26:205:46 | ControlFlowNode for List [List element, Tuple element at index 0] | -| test.py:206:10:206:10 | ControlFlowNode for x [List element] | test.py:206:10:206:13 | ControlFlowNode for Subscript | -| test.py:210:9:210:51 | ControlFlowNode for ListComp [List element] | test.py:211:10:211:10 | ControlFlowNode for x [List element] | -| test.py:210:10:210:10 | ControlFlowNode for a [List element] | test.py:210:10:210:13 | ControlFlowNode for Subscript | -| test.py:210:10:210:13 | ControlFlowNode for Subscript | test.py:210:9:210:51 | ControlFlowNode for ListComp [List element] | -| test.py:210:20:210:21 | IterableElement | test.py:210:20:210:21 | SSA variable a [List element] | -| test.py:210:20:210:21 | SSA variable a [List element] | test.py:210:10:210:10 | ControlFlowNode for a [List element] | -| test.py:210:20:210:24 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:210:20:210:21 | IterableElement | -| test.py:210:20:210:24 | IterableSequence [Tuple element at index 0] | test.py:210:20:210:24 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:210:30:210:50 | ControlFlowNode for List [List element, Tuple element at index 0] | test.py:210:20:210:24 | IterableSequence [Tuple element at index 0] | -| test.py:210:32:210:37 | ControlFlowNode for SOURCE | test.py:210:32:210:48 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:210:32:210:48 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:210:30:210:50 | ControlFlowNode for List [List element, Tuple element at index 0] | -| test.py:211:10:211:10 | ControlFlowNode for x [List element] | test.py:211:10:211:13 | ControlFlowNode for Subscript | -| test.py:349:11:349:16 | ControlFlowNode for SOURCE | test.py:349:11:349:17 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:349:11:349:17 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:349:10:349:21 | ControlFlowNode for Subscript | -| test.py:353:10:353:17 | ControlFlowNode for List [List element] | test.py:353:10:353:20 | ControlFlowNode for Subscript | -| test.py:353:11:353:16 | ControlFlowNode for SOURCE | test.py:353:10:353:17 | ControlFlowNode for List [List element] | -| test.py:357:10:357:22 | ControlFlowNode for Dict [Dictionary element at key s] | test.py:357:10:357:27 | ControlFlowNode for Subscript | -| test.py:357:16:357:21 | ControlFlowNode for SOURCE | test.py:357:10:357:22 | ControlFlowNode for Dict [Dictionary element at key s] | -| test.py:375:15:375:15 | ControlFlowNode for b | test.py:376:12:376:12 | ControlFlowNode for b | -| test.py:380:28:380:33 | ControlFlowNode for SOURCE | test.py:375:15:375:15 | ControlFlowNode for b | -| test.py:380:28:380:33 | ControlFlowNode for SOURCE | test.py:380:10:380:34 | ControlFlowNode for second() | -| test.py:388:30:388:35 | ControlFlowNode for SOURCE | test.py:375:15:375:15 | ControlFlowNode for b | -| test.py:388:30:388:35 | ControlFlowNode for SOURCE | test.py:388:10:388:36 | ControlFlowNode for second() | -| test.py:396:10:396:43 | KwUnpacked b | test.py:375:15:375:15 | ControlFlowNode for b | -| test.py:396:10:396:43 | KwUnpacked b | test.py:396:10:396:43 | ControlFlowNode for second() | -| test.py:396:30:396:42 | ControlFlowNode for Dict [Dictionary element at key b] | test.py:396:10:396:43 | KwUnpacked b | -| test.py:396:36:396:41 | ControlFlowNode for SOURCE | test.py:396:30:396:42 | ControlFlowNode for Dict [Dictionary element at key b] | -| test.py:399:21:399:21 | ControlFlowNode for b [Tuple element at index 0] | test.py:400:12:400:12 | ControlFlowNode for b [Tuple element at index 0] | -| test.py:400:12:400:12 | ControlFlowNode for b [Tuple element at index 0] | test.py:400:12:400:15 | ControlFlowNode for Subscript | -| test.py:404:10:404:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | test.py:399:21:399:21 | ControlFlowNode for b [Tuple element at index 0] | -| test.py:404:10:404:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | test.py:404:10:404:39 | ControlFlowNode for f_extra_pos() | -| test.py:404:33:404:38 | ControlFlowNode for SOURCE | test.py:404:10:404:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | -| test.py:407:26:407:26 | ControlFlowNode for b [Dictionary element at key b] | test.py:408:12:408:12 | ControlFlowNode for b [Dictionary element at key b] | -| test.py:408:12:408:12 | ControlFlowNode for b [Dictionary element at key b] | test.py:408:12:408:17 | ControlFlowNode for Subscript | -| test.py:412:10:412:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | test.py:407:26:407:26 | ControlFlowNode for b [Dictionary element at key b] | -| test.py:412:10:412:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | test.py:412:10:412:45 | ControlFlowNode for f_extra_keyword() | -| test.py:412:39:412:44 | ControlFlowNode for SOURCE | test.py:412:10:412:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | -| test.py:429:15:429:20 | ControlFlowNode for SOURCE | test.py:429:10:429:20 | ControlFlowNode for BoolExpr | -| test.py:434:16:434:21 | ControlFlowNode for SOURCE | test.py:434:10:434:21 | ControlFlowNode for BoolExpr | -| test.py:445:10:445:15 | ControlFlowNode for SOURCE | test.py:445:10:445:38 | ControlFlowNode for IfExp | -| test.py:453:34:453:39 | ControlFlowNode for SOURCE | test.py:453:10:453:39 | ControlFlowNode for IfExp | -| test.py:474:11:474:11 | ControlFlowNode for x | test.py:475:16:475:16 | ControlFlowNode for x | -| test.py:477:12:477:17 | ControlFlowNode for SOURCE | test.py:474:11:474:11 | ControlFlowNode for x | -| test.py:477:12:477:17 | ControlFlowNode for SOURCE | test.py:477:10:477:18 | ControlFlowNode for f() | -| test.py:481:19:481:19 | ControlFlowNode for b | test.py:482:16:482:16 | ControlFlowNode for b | -| test.py:484:28:484:33 | ControlFlowNode for SOURCE | test.py:481:19:481:19 | ControlFlowNode for b | -| test.py:484:28:484:33 | ControlFlowNode for SOURCE | test.py:484:10:484:34 | ControlFlowNode for second() | -| test.py:495:19:495:19 | ControlFlowNode for b | test.py:496:16:496:16 | ControlFlowNode for b | -| test.py:498:30:498:35 | ControlFlowNode for SOURCE | test.py:495:19:495:19 | ControlFlowNode for b | -| test.py:498:30:498:35 | ControlFlowNode for SOURCE | test.py:498:10:498:36 | ControlFlowNode for second() | -| test.py:509:19:509:19 | ControlFlowNode for b | test.py:510:16:510:16 | ControlFlowNode for b | -| test.py:512:10:512:43 | KwUnpacked b | test.py:509:19:509:19 | ControlFlowNode for b | -| test.py:512:10:512:43 | KwUnpacked b | test.py:512:10:512:43 | ControlFlowNode for second() | -| test.py:512:30:512:42 | ControlFlowNode for Dict [Dictionary element at key b] | test.py:512:10:512:43 | KwUnpacked b | -| test.py:512:36:512:41 | ControlFlowNode for SOURCE | test.py:512:30:512:42 | ControlFlowNode for Dict [Dictionary element at key b] | -| test.py:516:30:516:30 | ControlFlowNode for b [Tuple element at index 0] | test.py:516:33:516:33 | ControlFlowNode for b [Tuple element at index 0] | -| test.py:516:33:516:33 | ControlFlowNode for b [Tuple element at index 0] | test.py:516:33:516:36 | ControlFlowNode for Subscript | -| test.py:517:10:517:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | test.py:516:30:516:30 | ControlFlowNode for b [Tuple element at index 0] | -| test.py:517:10:517:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | test.py:517:10:517:39 | ControlFlowNode for f_extra_pos() | -| test.py:517:33:517:38 | ControlFlowNode for SOURCE | test.py:517:10:517:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | -| test.py:521:35:521:35 | ControlFlowNode for b [Dictionary element at key b] | test.py:521:38:521:38 | ControlFlowNode for b [Dictionary element at key b] | -| test.py:521:38:521:38 | ControlFlowNode for b [Dictionary element at key b] | test.py:521:38:521:43 | ControlFlowNode for Subscript | -| test.py:522:10:522:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | test.py:521:35:521:35 | ControlFlowNode for b [Dictionary element at key b] | -| test.py:522:10:522:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | test.py:522:10:522:45 | ControlFlowNode for f_extra_keyword() | -| test.py:522:39:522:44 | ControlFlowNode for SOURCE | test.py:522:10:522:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | -| test.py:534:9:534:14 | ControlFlowNode for SOURCE | test.py:536:10:536:10 | ControlFlowNode for a | -| test.py:534:9:534:14 | ControlFlowNode for SOURCE | test.py:541:10:541:10 | ControlFlowNode for b | -| test.py:546:10:546:15 | ControlFlowNode for SOURCE | test.py:546:10:546:26 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:546:10:546:26 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:547:5:547:8 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:547:5:547:5 | SSA variable a | test.py:548:10:548:10 | ControlFlowNode for a | -| test.py:547:5:547:8 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:547:5:547:5 | SSA variable a | -| test.py:554:10:554:15 | ControlFlowNode for SOURCE | test.py:554:10:554:36 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:554:10:554:36 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:555:5:555:13 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:554:10:554:36 | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] | test.py:555:5:555:13 | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] | -| test.py:554:19:554:35 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:554:10:554:36 | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] | -| test.py:554:30:554:35 | ControlFlowNode for SOURCE | test.py:554:19:554:35 | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:555:5:555:5 | SSA variable a | test.py:556:10:556:10 | ControlFlowNode for a | -| test.py:555:5:555:13 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:555:5:555:5 | SSA variable a | -| test.py:555:5:555:13 | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] | test.py:555:9:555:12 | IterableSequence [Tuple element at index 1] | -| test.py:555:9:555:12 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:555:12:555:12 | SSA variable c | -| test.py:555:9:555:12 | IterableSequence [Tuple element at index 1] | test.py:555:9:555:12 | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:555:12:555:12 | SSA variable c | test.py:558:10:558:10 | ControlFlowNode for c | -| test.py:563:9:563:33 | ControlFlowNode for List [List element, List element, List element, List element] | test.py:564:5:564:14 | IterableSequence [List element, List element, List element, List element] | -| test.py:563:10:563:21 | ControlFlowNode for List [List element, List element, List element] | test.py:563:9:563:33 | ControlFlowNode for List [List element, List element, List element, List element] | -| test.py:563:11:563:20 | ControlFlowNode for List [List element, List element] | test.py:563:10:563:21 | ControlFlowNode for List [List element, List element, List element] | -| test.py:563:12:563:19 | ControlFlowNode for List [List element] | test.py:563:11:563:20 | ControlFlowNode for List [List element, List element] | -| test.py:563:13:563:18 | ControlFlowNode for SOURCE | test.py:563:12:563:19 | ControlFlowNode for List [List element] | -| test.py:564:5:564:11 | ControlFlowNode for List [Tuple element at index 0, List element, List element] | test.py:564:6:564:10 | IterableSequence [List element, List element] | -| test.py:564:5:564:11 | IterableElement [List element, List element] | test.py:564:5:564:11 | ControlFlowNode for List [Tuple element at index 0, List element, List element] | -| test.py:564:5:564:11 | IterableSequence [List element, List element, List element] | test.py:564:5:564:11 | IterableElement [List element, List element] | -| test.py:564:5:564:14 | ControlFlowNode for Tuple [Tuple element at index 0, List element, List element, List element] | test.py:564:5:564:11 | IterableSequence [List element, List element, List element] | -| test.py:564:5:564:14 | IterableElement [List element, List element, List element] | test.py:564:5:564:14 | ControlFlowNode for Tuple [Tuple element at index 0, List element, List element, List element] | -| test.py:564:5:564:14 | IterableSequence [List element, List element, List element, List element] | test.py:564:5:564:14 | IterableElement [List element, List element, List element] | -| test.py:564:6:564:10 | ControlFlowNode for List [Tuple element at index 0, List element] | test.py:564:7:564:9 | IterableSequence [List element] | -| test.py:564:6:564:10 | IterableElement [List element] | test.py:564:6:564:10 | ControlFlowNode for List [Tuple element at index 0, List element] | -| test.py:564:6:564:10 | IterableSequence [List element, List element] | test.py:564:6:564:10 | IterableElement [List element] | -| test.py:564:7:564:9 | ControlFlowNode for List [Tuple element at index 0] | test.py:564:8:564:8 | SSA variable a | -| test.py:564:7:564:9 | IterableElement | test.py:564:7:564:9 | ControlFlowNode for List [Tuple element at index 0] | -| test.py:564:7:564:9 | IterableSequence [List element] | test.py:564:7:564:9 | IterableElement | -| test.py:564:8:564:8 | SSA variable a | test.py:565:10:565:10 | ControlFlowNode for a | -| test.py:571:10:571:15 | ControlFlowNode for SOURCE | test.py:571:10:571:34 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:571:10:571:34 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:572:5:572:12 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:571:10:571:34 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:572:5:572:12 | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:571:18:571:23 | ControlFlowNode for SOURCE | test.py:571:10:571:34 | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:572:5:572:5 | SSA variable a | test.py:573:10:573:10 | ControlFlowNode for a | -| test.py:572:5:572:12 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:572:5:572:5 | SSA variable a | -| test.py:572:5:572:12 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:572:8:572:9 | IterableElement | -| test.py:572:5:572:12 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:572:12:572:12 | SSA variable c | -| test.py:572:8:572:9 | IterableElement | test.py:572:8:572:9 | SSA variable b [List element] | -| test.py:572:8:572:9 | SSA variable b [List element] | test.py:575:10:575:10 | ControlFlowNode for b [List element] | -| test.py:572:12:572:12 | SSA variable c | test.py:576:12:576:12 | ControlFlowNode for c | -| test.py:575:10:575:10 | ControlFlowNode for b [List element] | test.py:575:10:575:13 | ControlFlowNode for Subscript | -| test.py:581:10:581:15 | ControlFlowNode for SOURCE | test.py:581:10:581:23 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:581:10:581:23 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:582:5:582:12 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:581:10:581:23 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:582:5:582:12 | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:581:18:581:23 | ControlFlowNode for SOURCE | test.py:581:10:581:23 | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:582:5:582:5 | SSA variable a | test.py:583:10:583:10 | ControlFlowNode for a | -| test.py:582:5:582:12 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:582:5:582:5 | SSA variable a | -| test.py:582:5:582:12 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:582:12:582:12 | SSA variable c | -| test.py:582:12:582:12 | SSA variable c | test.py:585:10:585:10 | ControlFlowNode for c | -| test.py:590:10:590:61 | ControlFlowNode for List [List element, List element] | test.py:593:6:593:23 | IterableSequence [List element, List element] | -| test.py:590:10:590:61 | ControlFlowNode for List [List element, List element] | test.py:601:5:601:24 | IterableSequence [List element, List element] | -| test.py:590:10:590:61 | ControlFlowNode for List [List element, List element] | test.py:609:6:609:23 | IterableSequence [List element, List element] | -| test.py:590:11:590:37 | ControlFlowNode for List [List element] | test.py:590:10:590:61 | ControlFlowNode for List [List element, List element] | -| test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:590:11:590:37 | ControlFlowNode for List [List element] | -| test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:590:11:590:37 | ControlFlowNode for List [List element] | -| test.py:590:40:590:47 | ControlFlowNode for List [List element] | test.py:590:10:590:61 | ControlFlowNode for List [List element, List element] | -| test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:590:40:590:47 | ControlFlowNode for List [List element] | -| test.py:593:6:593:23 | ControlFlowNode for Tuple [Tuple element at index 0, List element] | test.py:593:7:593:16 | IterableSequence [List element] | -| test.py:593:6:593:23 | IterableElement [List element] | test.py:593:6:593:23 | ControlFlowNode for Tuple [Tuple element at index 0, List element] | -| test.py:593:6:593:23 | IterableSequence [List element, List element] | test.py:593:6:593:23 | IterableElement [List element] | -| test.py:593:7:593:8 | SSA variable a1 | test.py:594:10:594:11 | ControlFlowNode for a1 | -| test.py:593:7:593:16 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:593:7:593:8 | SSA variable a1 | -| test.py:593:7:593:16 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:593:11:593:12 | SSA variable a2 | -| test.py:593:7:593:16 | ControlFlowNode for Tuple [Tuple element at index 2] | test.py:593:15:593:16 | SSA variable a3 | -| test.py:593:7:593:16 | IterableElement | test.py:593:7:593:16 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:593:7:593:16 | IterableElement | test.py:593:7:593:16 | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:593:7:593:16 | IterableElement | test.py:593:7:593:16 | ControlFlowNode for Tuple [Tuple element at index 2] | -| test.py:593:7:593:16 | IterableSequence [List element] | test.py:593:7:593:16 | IterableElement | -| test.py:593:11:593:12 | SSA variable a2 | test.py:595:12:595:13 | ControlFlowNode for a2 | -| test.py:593:15:593:16 | SSA variable a3 | test.py:596:10:596:11 | ControlFlowNode for a3 | -| test.py:601:5:601:24 | ControlFlowNode for List [Tuple element at index 0, List element] | test.py:601:7:601:16 | IterableSequence [List element] | -| test.py:601:5:601:24 | IterableElement [List element] | test.py:601:5:601:24 | ControlFlowNode for List [Tuple element at index 0, List element] | -| test.py:601:5:601:24 | IterableSequence [List element, List element] | test.py:601:5:601:24 | IterableElement [List element] | -| test.py:601:7:601:8 | SSA variable a1 | test.py:602:10:602:11 | ControlFlowNode for a1 | -| test.py:601:7:601:16 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:601:7:601:8 | SSA variable a1 | -| test.py:601:7:601:16 | ControlFlowNode for Tuple [Tuple element at index 1] | test.py:601:11:601:12 | SSA variable a2 | -| test.py:601:7:601:16 | ControlFlowNode for Tuple [Tuple element at index 2] | test.py:601:15:601:16 | SSA variable a3 | -| test.py:601:7:601:16 | IterableElement | test.py:601:7:601:16 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:601:7:601:16 | IterableElement | test.py:601:7:601:16 | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:601:7:601:16 | IterableElement | test.py:601:7:601:16 | ControlFlowNode for Tuple [Tuple element at index 2] | -| test.py:601:7:601:16 | IterableSequence [List element] | test.py:601:7:601:16 | IterableElement | -| test.py:601:11:601:12 | SSA variable a2 | test.py:603:12:603:13 | ControlFlowNode for a2 | -| test.py:601:15:601:16 | SSA variable a3 | test.py:604:10:604:11 | ControlFlowNode for a3 | -| test.py:609:6:609:17 | ControlFlowNode for List [Tuple element at index 0] | test.py:609:7:609:8 | SSA variable a1 | -| test.py:609:6:609:17 | ControlFlowNode for List [Tuple element at index 1] | test.py:609:11:609:12 | SSA variable a2 | -| test.py:609:6:609:17 | ControlFlowNode for List [Tuple element at index 2] | test.py:609:15:609:16 | SSA variable a3 | -| test.py:609:6:609:17 | IterableElement | test.py:609:6:609:17 | ControlFlowNode for List [Tuple element at index 0] | -| test.py:609:6:609:17 | IterableElement | test.py:609:6:609:17 | ControlFlowNode for List [Tuple element at index 1] | -| test.py:609:6:609:17 | IterableElement | test.py:609:6:609:17 | ControlFlowNode for List [Tuple element at index 2] | -| test.py:609:6:609:17 | IterableSequence [List element] | test.py:609:6:609:17 | IterableElement | -| test.py:609:6:609:23 | ControlFlowNode for Tuple [Tuple element at index 0, List element] | test.py:609:6:609:17 | IterableSequence [List element] | -| test.py:609:6:609:23 | IterableElement [List element] | test.py:609:6:609:23 | ControlFlowNode for Tuple [Tuple element at index 0, List element] | -| test.py:609:6:609:23 | IterableSequence [List element, List element] | test.py:609:6:609:23 | IterableElement [List element] | -| test.py:609:7:609:8 | SSA variable a1 | test.py:610:10:610:11 | ControlFlowNode for a1 | -| test.py:609:11:609:12 | SSA variable a2 | test.py:611:12:611:13 | ControlFlowNode for a2 | -| test.py:609:15:609:16 | SSA variable a3 | test.py:612:10:612:11 | ControlFlowNode for a3 | -| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | test.py:621:5:621:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] | -| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | test.py:630:6:630:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | -| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | test.py:639:5:639:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] | -| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | test.py:648:6:648:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | -| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | test.py:621:5:621:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] | -| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | test.py:630:6:630:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | -| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | test.py:639:5:639:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] | -| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | test.py:648:6:648:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | -| test.py:618:12:618:17 | ControlFlowNode for SOURCE | test.py:618:12:618:36 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:618:12:618:36 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | -| test.py:618:12:618:36 | ControlFlowNode for Tuple [Tuple element at index 2] | test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | -| test.py:618:31:618:36 | ControlFlowNode for SOURCE | test.py:618:12:618:36 | ControlFlowNode for Tuple [Tuple element at index 2] | -| test.py:621:5:621:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] | test.py:621:6:621:14 | IterableSequence [Tuple element at index 0] | -| test.py:621:5:621:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] | test.py:621:6:621:14 | IterableSequence [Tuple element at index 2] | -| test.py:621:6:621:14 | ControlFlowNode for List [Tuple element at index 0] | test.py:621:7:621:8 | SSA variable a1 | -| test.py:621:6:621:14 | ControlFlowNode for List [Tuple element at index 2] | test.py:621:11:621:13 | IterableElement | -| test.py:621:6:621:14 | IterableSequence [Tuple element at index 0] | test.py:621:6:621:14 | ControlFlowNode for List [Tuple element at index 0] | -| test.py:621:6:621:14 | IterableSequence [Tuple element at index 2] | test.py:621:6:621:14 | ControlFlowNode for List [Tuple element at index 2] | -| test.py:621:7:621:8 | SSA variable a1 | test.py:622:10:622:11 | ControlFlowNode for a1 | -| test.py:621:11:621:13 | IterableElement | test.py:621:11:621:13 | SSA variable a2 [List element] | -| test.py:621:11:621:13 | SSA variable a2 [List element] | test.py:624:12:624:13 | ControlFlowNode for a2 [List element] | -| test.py:621:11:621:13 | SSA variable a2 [List element] | test.py:625:10:625:11 | ControlFlowNode for a2 [List element] | -| test.py:624:12:624:13 | ControlFlowNode for a2 [List element] | test.py:624:12:624:16 | ControlFlowNode for Subscript | -| test.py:625:10:625:11 | ControlFlowNode for a2 [List element] | test.py:625:10:625:14 | ControlFlowNode for Subscript | -| test.py:630:6:630:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | test.py:630:7:630:13 | IterableSequence [Tuple element at index 0] | -| test.py:630:6:630:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | test.py:630:7:630:13 | IterableSequence [Tuple element at index 2] | -| test.py:630:7:630:8 | SSA variable a1 | test.py:631:10:631:11 | ControlFlowNode for a1 | -| test.py:630:7:630:13 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:630:7:630:8 | SSA variable a1 | -| test.py:630:7:630:13 | ControlFlowNode for Tuple [Tuple element at index 2] | test.py:630:11:630:13 | IterableElement | -| test.py:630:7:630:13 | IterableSequence [Tuple element at index 0] | test.py:630:7:630:13 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:630:7:630:13 | IterableSequence [Tuple element at index 2] | test.py:630:7:630:13 | ControlFlowNode for Tuple [Tuple element at index 2] | -| test.py:630:11:630:13 | IterableElement | test.py:630:11:630:13 | SSA variable a2 [List element] | -| test.py:630:11:630:13 | SSA variable a2 [List element] | test.py:633:12:633:13 | ControlFlowNode for a2 [List element] | -| test.py:630:11:630:13 | SSA variable a2 [List element] | test.py:634:10:634:11 | ControlFlowNode for a2 [List element] | -| test.py:633:12:633:13 | ControlFlowNode for a2 [List element] | test.py:633:12:633:16 | ControlFlowNode for Subscript | -| test.py:634:10:634:11 | ControlFlowNode for a2 [List element] | test.py:634:10:634:14 | ControlFlowNode for Subscript | -| test.py:639:5:639:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] | test.py:639:7:639:13 | IterableSequence [Tuple element at index 0] | -| test.py:639:5:639:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] | test.py:639:7:639:13 | IterableSequence [Tuple element at index 2] | -| test.py:639:7:639:8 | SSA variable a1 | test.py:640:10:640:11 | ControlFlowNode for a1 | -| test.py:639:7:639:13 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:639:7:639:8 | SSA variable a1 | -| test.py:639:7:639:13 | ControlFlowNode for Tuple [Tuple element at index 2] | test.py:639:11:639:13 | IterableElement | -| test.py:639:7:639:13 | IterableSequence [Tuple element at index 0] | test.py:639:7:639:13 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:639:7:639:13 | IterableSequence [Tuple element at index 2] | test.py:639:7:639:13 | ControlFlowNode for Tuple [Tuple element at index 2] | -| test.py:639:11:639:13 | IterableElement | test.py:639:11:639:13 | SSA variable a2 [List element] | -| test.py:639:11:639:13 | SSA variable a2 [List element] | test.py:642:12:642:13 | ControlFlowNode for a2 [List element] | -| test.py:639:11:639:13 | SSA variable a2 [List element] | test.py:643:10:643:11 | ControlFlowNode for a2 [List element] | -| test.py:642:12:642:13 | ControlFlowNode for a2 [List element] | test.py:642:12:642:16 | ControlFlowNode for Subscript | -| test.py:643:10:643:11 | ControlFlowNode for a2 [List element] | test.py:643:10:643:14 | ControlFlowNode for Subscript | -| test.py:648:6:648:14 | ControlFlowNode for List [Tuple element at index 0] | test.py:648:7:648:8 | SSA variable a1 | -| test.py:648:6:648:14 | ControlFlowNode for List [Tuple element at index 2] | test.py:648:11:648:13 | IterableElement | -| test.py:648:6:648:14 | IterableSequence [Tuple element at index 0] | test.py:648:6:648:14 | ControlFlowNode for List [Tuple element at index 0] | -| test.py:648:6:648:14 | IterableSequence [Tuple element at index 2] | test.py:648:6:648:14 | ControlFlowNode for List [Tuple element at index 2] | -| test.py:648:6:648:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | test.py:648:6:648:14 | IterableSequence [Tuple element at index 0] | -| test.py:648:6:648:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | test.py:648:6:648:14 | IterableSequence [Tuple element at index 2] | -| test.py:648:7:648:8 | SSA variable a1 | test.py:649:10:649:11 | ControlFlowNode for a1 | -| test.py:648:11:648:13 | IterableElement | test.py:648:11:648:13 | SSA variable a2 [List element] | -| test.py:648:11:648:13 | SSA variable a2 [List element] | test.py:651:12:651:13 | ControlFlowNode for a2 [List element] | -| test.py:648:11:648:13 | SSA variable a2 [List element] | test.py:652:10:652:11 | ControlFlowNode for a2 [List element] | -| test.py:651:12:651:13 | ControlFlowNode for a2 [List element] | test.py:651:12:651:16 | ControlFlowNode for Subscript | -| test.py:652:10:652:11 | ControlFlowNode for a2 [List element] | test.py:652:10:652:14 | ControlFlowNode for Subscript | -| test.py:659:19:659:24 | ControlFlowNode for SOURCE | test.py:660:10:660:10 | ControlFlowNode for a | -| test.py:667:10:667:51 | ControlFlowNode for List [List element, Tuple element at index 0] | test.py:668:16:668:17 | ControlFlowNode for tl [List element, Tuple element at index 0] | -| test.py:667:12:667:17 | ControlFlowNode for SOURCE | test.py:667:12:667:28 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:667:12:667:28 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:667:10:667:51 | ControlFlowNode for List [List element, Tuple element at index 0] | -| test.py:667:33:667:38 | ControlFlowNode for SOURCE | test.py:667:33:667:49 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:667:33:667:49 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:667:10:667:51 | ControlFlowNode for List [List element, Tuple element at index 0] | -| test.py:668:9:668:9 | SSA variable x | test.py:669:14:669:14 | ControlFlowNode for x | -| test.py:668:9:668:11 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:668:9:668:9 | SSA variable x | -| test.py:668:9:668:11 | IterableSequence [Tuple element at index 0] | test.py:668:9:668:11 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:668:16:668:17 | ControlFlowNode for tl [List element, Tuple element at index 0] | test.py:668:9:668:11 | IterableSequence [Tuple element at index 0] | -| test.py:675:10:675:51 | ControlFlowNode for List [List element, Tuple element at index 0] | test.py:676:17:676:18 | ControlFlowNode for tl [List element, Tuple element at index 0] | -| test.py:675:12:675:17 | ControlFlowNode for SOURCE | test.py:675:12:675:28 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:675:12:675:28 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:675:10:675:51 | ControlFlowNode for List [List element, Tuple element at index 0] | -| test.py:675:33:675:38 | ControlFlowNode for SOURCE | test.py:675:33:675:49 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:675:33:675:49 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:675:10:675:51 | ControlFlowNode for List [List element, Tuple element at index 0] | -| test.py:676:9:676:10 | IterableElement | test.py:676:9:676:10 | SSA variable x [List element] | -| test.py:676:9:676:10 | SSA variable x [List element] | test.py:678:14:678:14 | ControlFlowNode for x [List element] | -| test.py:676:9:676:12 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:676:9:676:10 | IterableElement | -| test.py:676:9:676:12 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:676:12:676:12 | SSA variable y | -| test.py:676:9:676:12 | IterableSequence [Tuple element at index 0] | test.py:676:9:676:12 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:676:12:676:12 | SSA variable y | test.py:679:16:679:16 | ControlFlowNode for y | -| test.py:676:17:676:18 | ControlFlowNode for tl [List element, Tuple element at index 0] | test.py:676:9:676:12 | IterableSequence [Tuple element at index 0] | -| test.py:678:14:678:14 | ControlFlowNode for x [List element] | test.py:678:14:678:17 | ControlFlowNode for Subscript | -| test.py:684:10:684:51 | ControlFlowNode for List [List element, Tuple element at index 0] | test.py:685:19:685:20 | ControlFlowNode for tl [List element, Tuple element at index 0] | -| test.py:684:12:684:17 | ControlFlowNode for SOURCE | test.py:684:12:684:28 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:684:12:684:28 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:684:10:684:51 | ControlFlowNode for List [List element, Tuple element at index 0] | -| test.py:684:33:684:38 | ControlFlowNode for SOURCE | test.py:684:33:684:49 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:684:33:684:49 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:684:10:684:51 | ControlFlowNode for List [List element, Tuple element at index 0] | -| test.py:685:9:685:9 | SSA variable x | test.py:686:14:686:14 | ControlFlowNode for x | -| test.py:685:9:685:14 | ControlFlowNode for Tuple [Tuple element at index 0] | test.py:685:9:685:9 | SSA variable x | -| test.py:685:9:685:14 | IterableSequence [Tuple element at index 0] | test.py:685:9:685:14 | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:685:19:685:20 | ControlFlowNode for tl [List element, Tuple element at index 0] | test.py:685:9:685:14 | IterableSequence [Tuple element at index 0] | -| test.py:690:39:690:42 | ControlFlowNode for args [Tuple element at index 0] | test.py:691:14:691:17 | ControlFlowNode for args [Tuple element at index 0] | -| test.py:690:39:690:42 | ControlFlowNode for args [Tuple element at index 1] | test.py:691:14:691:17 | ControlFlowNode for args [Tuple element at index 1] | -| test.py:691:7:691:9 | SSA variable arg | test.py:692:10:692:12 | ControlFlowNode for arg | -| test.py:691:14:691:17 | ControlFlowNode for args [Tuple element at index 0] | test.py:691:7:691:9 | SSA variable arg | -| test.py:691:14:691:17 | ControlFlowNode for args [Tuple element at index 1] | test.py:691:7:691:9 | SSA variable arg | -| test.py:697:7:697:12 | ControlFlowNode for SOURCE | test.py:698:51:698:51 | ControlFlowNode for s | -| test.py:698:3:698:52 | PosOverflowNode for iterate_star_args() [Tuple element at index 0] | test.py:690:39:690:42 | ControlFlowNode for args [Tuple element at index 0] | -| test.py:698:3:698:52 | PosOverflowNode for iterate_star_args() [Tuple element at index 1] | test.py:690:39:690:42 | ControlFlowNode for args [Tuple element at index 1] | -| test.py:698:43:698:48 | ControlFlowNode for SOURCE | test.py:698:3:698:52 | PosOverflowNode for iterate_star_args() [Tuple element at index 0] | -| test.py:698:51:698:51 | ControlFlowNode for s | test.py:698:3:698:52 | PosOverflowNode for iterate_star_args() [Tuple element at index 1] | -| test.py:769:16:769:21 | ControlFlowNode for SOURCE | test.py:772:10:772:36 | ControlFlowNode for return_from_inner_scope() | -| test.py:807:35:807:35 | ControlFlowNode for x | test.py:808:10:808:10 | ControlFlowNode for x | -| test.py:807:37:807:42 | ControlFlowNode for SOURCE | test.py:807:35:807:35 | ControlFlowNode for x | -| test.py:807:48:807:48 | ControlFlowNode for y | test.py:809:10:809:10 | ControlFlowNode for y | -| test.py:807:50:807:55 | ControlFlowNode for SOURCE | test.py:807:48:807:48 | ControlFlowNode for y | -| test.py:807:61:807:61 | ControlFlowNode for z | test.py:810:10:810:10 | ControlFlowNode for z | -| test.py:807:63:807:68 | ControlFlowNode for SOURCE | test.py:807:61:807:61 | ControlFlowNode for z | -nodes -| datamodel.py:35:7:35:7 | ControlFlowNode for a | semmle.label | ControlFlowNode for a | -| datamodel.py:36:10:36:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a | -| datamodel.py:38:6:38:17 | ControlFlowNode for f() | semmle.label | ControlFlowNode for f() | -| datamodel.py:38:8:38:13 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| datamodel.py:44:22:44:22 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| datamodel.py:46:16:46:16 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| datamodel.py:49:26:49:26 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| datamodel.py:50:16:50:16 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| datamodel.py:71:15:71:20 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| datamodel.py:72:18:72:23 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| datamodel.py:80:20:80:25 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| datamodel.py:81:20:81:25 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| datamodel.py:152:5:152:8 | [post store] ControlFlowNode for self [Attribute b] | semmle.label | [post store] ControlFlowNode for self [Attribute b] | -| datamodel.py:152:14:152:19 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| datamodel.py:155:14:155:25 | ControlFlowNode for Customized() [Attribute b] | semmle.label | ControlFlowNode for Customized() [Attribute b] | -| datamodel.py:159:6:159:15 | ControlFlowNode for customized [Attribute b] | semmle.label | ControlFlowNode for customized [Attribute b] | -| datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | -| test.py:42:10:42:26 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:42:21:42:26 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:43:9:43:9 | ControlFlowNode for x [Tuple element at index 1] | semmle.label | ControlFlowNode for x [Tuple element at index 1] | -| test.py:43:9:43:12 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:44:10:44:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | -| test.py:55:9:55:14 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:56:10:56:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:61:9:61:16 | ControlFlowNode for Str | semmle.label | ControlFlowNode for Str | -| test.py:62:10:62:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:66:9:66:17 | ControlFlowNode for Str | semmle.label | ControlFlowNode for Str | -| test.py:67:10:67:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:71:9:71:10 | ControlFlowNode for IntegerLiteral | semmle.label | ControlFlowNode for IntegerLiteral | -| test.py:72:10:72:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:76:9:76:12 | ControlFlowNode for FloatLiteral | semmle.label | ControlFlowNode for FloatLiteral | -| test.py:77:10:77:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:87:10:87:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:88:10:88:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:93:9:93:16 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] | -| test.py:93:10:93:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:94:10:94:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] | -| test.py:94:10:94:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:103:9:103:37 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] | -| test.py:103:10:103:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:104:10:104:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] | -| test.py:104:10:104:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:108:9:108:29 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] | -| test.py:108:10:108:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | -| test.py:108:16:108:16 | SSA variable y | semmle.label | SSA variable y | -| test.py:108:21:108:28 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] | -| test.py:108:22:108:27 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:109:10:109:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] | -| test.py:109:10:109:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:113:9:113:16 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] | -| test.py:113:10:113:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:114:9:114:22 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] | -| test.py:114:10:114:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | -| test.py:114:16:114:16 | SSA variable y | semmle.label | SSA variable y | -| test.py:114:21:114:21 | ControlFlowNode for l [List element] | semmle.label | ControlFlowNode for l [List element] | -| test.py:115:10:115:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] | -| test.py:115:10:115:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:125:9:125:16 | ControlFlowNode for Set [Set element] | semmle.label | ControlFlowNode for Set [Set element] | -| test.py:125:10:125:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:126:10:126:10 | ControlFlowNode for x [Set element] | semmle.label | ControlFlowNode for x [Set element] | -| test.py:126:10:126:16 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| test.py:130:9:130:37 | ControlFlowNode for SetComp [Set element] | semmle.label | ControlFlowNode for SetComp [Set element] | -| test.py:130:10:130:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:131:10:131:10 | ControlFlowNode for x [Set element] | semmle.label | ControlFlowNode for x [Set element] | -| test.py:131:10:131:16 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| test.py:135:9:135:29 | ControlFlowNode for SetComp [Set element] | semmle.label | ControlFlowNode for SetComp [Set element] | -| test.py:135:10:135:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | -| test.py:135:16:135:16 | SSA variable y | semmle.label | SSA variable y | -| test.py:135:21:135:28 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] | -| test.py:135:22:135:27 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:136:10:136:10 | ControlFlowNode for x [Set element] | semmle.label | ControlFlowNode for x [Set element] | -| test.py:136:10:136:16 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| test.py:140:9:140:16 | ControlFlowNode for Set [Set element] | semmle.label | ControlFlowNode for Set [Set element] | -| test.py:140:10:140:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:141:9:141:22 | ControlFlowNode for SetComp [Set element] | semmle.label | ControlFlowNode for SetComp [Set element] | -| test.py:141:10:141:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | -| test.py:141:16:141:16 | SSA variable y | semmle.label | SSA variable y | -| test.py:141:21:141:21 | ControlFlowNode for l [Set element] | semmle.label | ControlFlowNode for l [Set element] | -| test.py:142:10:142:10 | ControlFlowNode for x [Set element] | semmle.label | ControlFlowNode for x [Set element] | -| test.py:142:10:142:16 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| test.py:152:9:152:21 | ControlFlowNode for Dict [Dictionary element at key s] | semmle.label | ControlFlowNode for Dict [Dictionary element at key s] | -| test.py:152:15:152:20 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:153:10:153:10 | ControlFlowNode for x [Dictionary element at key s] | semmle.label | ControlFlowNode for x [Dictionary element at key s] | -| test.py:153:10:153:15 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:157:9:157:21 | ControlFlowNode for Dict [Dictionary element at key s] | semmle.label | ControlFlowNode for Dict [Dictionary element at key s] | -| test.py:157:15:157:20 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:158:10:158:10 | ControlFlowNode for x [Dictionary element at key s] | semmle.label | ControlFlowNode for x [Dictionary element at key s] | -| test.py:158:10:158:19 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| test.py:183:9:183:42 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] | -| test.py:183:10:183:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | -| test.py:183:16:183:16 | SSA variable z [List element] | semmle.label | SSA variable z [List element] | -| test.py:183:21:183:30 | ControlFlowNode for List [List element, List element] | semmle.label | ControlFlowNode for List [List element, List element] | -| test.py:183:22:183:29 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] | -| test.py:183:23:183:28 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:183:36:183:36 | SSA variable y | semmle.label | SSA variable y | -| test.py:183:41:183:41 | ControlFlowNode for z [List element] | semmle.label | ControlFlowNode for z [List element] | -| test.py:184:10:184:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] | -| test.py:184:10:184:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:188:9:188:68 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] | -| test.py:188:10:188:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | -| test.py:188:16:188:16 | SSA variable v [List element, List element, List element] | semmle.label | SSA variable v [List element, List element, List element] | -| test.py:188:21:188:34 | ControlFlowNode for List [List element, List element, List element, List element] | semmle.label | ControlFlowNode for List [List element, List element, List element, List element] | -| test.py:188:22:188:33 | ControlFlowNode for List [List element, List element, List element] | semmle.label | ControlFlowNode for List [List element, List element, List element] | -| test.py:188:23:188:32 | ControlFlowNode for List [List element, List element] | semmle.label | ControlFlowNode for List [List element, List element] | -| test.py:188:24:188:31 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] | -| test.py:188:25:188:30 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:188:40:188:40 | SSA variable u [List element, List element] | semmle.label | SSA variable u [List element, List element] | -| test.py:188:45:188:45 | ControlFlowNode for v [List element, List element, List element] | semmle.label | ControlFlowNode for v [List element, List element, List element] | -| test.py:188:51:188:51 | SSA variable z [List element] | semmle.label | SSA variable z [List element] | -| test.py:188:56:188:56 | ControlFlowNode for u [List element, List element] | semmle.label | ControlFlowNode for u [List element, List element] | -| test.py:188:62:188:62 | SSA variable y | semmle.label | SSA variable y | -| test.py:188:67:188:67 | ControlFlowNode for z [List element] | semmle.label | ControlFlowNode for z [List element] | -| test.py:189:10:189:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] | -| test.py:189:10:189:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:199:9:199:42 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] | -| test.py:199:10:199:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | -| test.py:199:16:199:16 | SSA variable y | semmle.label | SSA variable y | -| test.py:199:22:199:22 | ControlFlowNode for z | semmle.label | ControlFlowNode for z | -| test.py:199:22:199:40 | ControlFlowNode for GeneratorExp [List element] | semmle.label | ControlFlowNode for GeneratorExp [List element] | -| test.py:199:28:199:28 | SSA variable z | semmle.label | SSA variable z | -| test.py:199:33:199:40 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] | -| test.py:199:34:199:39 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:200:10:200:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] | -| test.py:200:10:200:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:205:9:205:47 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] | -| test.py:205:10:205:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a | -| test.py:205:17:205:17 | SSA variable a | semmle.label | SSA variable a | -| test.py:205:17:205:20 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:205:17:205:20 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] | -| test.py:205:26:205:46 | ControlFlowNode for List [List element, Tuple element at index 0] | semmle.label | ControlFlowNode for List [List element, Tuple element at index 0] | -| test.py:205:28:205:33 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:205:28:205:44 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:206:10:206:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] | -| test.py:206:10:206:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:210:9:210:51 | ControlFlowNode for ListComp [List element] | semmle.label | ControlFlowNode for ListComp [List element] | -| test.py:210:10:210:10 | ControlFlowNode for a [List element] | semmle.label | ControlFlowNode for a [List element] | -| test.py:210:10:210:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:210:20:210:21 | IterableElement | semmle.label | IterableElement | -| test.py:210:20:210:21 | SSA variable a [List element] | semmle.label | SSA variable a [List element] | -| test.py:210:20:210:24 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:210:20:210:24 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] | -| test.py:210:30:210:50 | ControlFlowNode for List [List element, Tuple element at index 0] | semmle.label | ControlFlowNode for List [List element, Tuple element at index 0] | -| test.py:210:32:210:37 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:210:32:210:48 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:211:10:211:10 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] | -| test.py:211:10:211:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:349:10:349:21 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:349:11:349:16 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:349:11:349:17 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:353:10:353:17 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] | -| test.py:353:10:353:20 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:353:11:353:16 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:357:10:357:22 | ControlFlowNode for Dict [Dictionary element at key s] | semmle.label | ControlFlowNode for Dict [Dictionary element at key s] | -| test.py:357:10:357:27 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:357:16:357:21 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:375:15:375:15 | ControlFlowNode for b | semmle.label | ControlFlowNode for b | -| test.py:376:12:376:12 | ControlFlowNode for b | semmle.label | ControlFlowNode for b | -| test.py:380:10:380:34 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() | -| test.py:380:28:380:33 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:388:10:388:36 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() | -| test.py:388:30:388:35 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:396:10:396:43 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() | -| test.py:396:10:396:43 | KwUnpacked b | semmle.label | KwUnpacked b | -| test.py:396:30:396:42 | ControlFlowNode for Dict [Dictionary element at key b] | semmle.label | ControlFlowNode for Dict [Dictionary element at key b] | -| test.py:396:36:396:41 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:399:21:399:21 | ControlFlowNode for b [Tuple element at index 0] | semmle.label | ControlFlowNode for b [Tuple element at index 0] | -| test.py:400:12:400:12 | ControlFlowNode for b [Tuple element at index 0] | semmle.label | ControlFlowNode for b [Tuple element at index 0] | -| test.py:400:12:400:15 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:404:10:404:39 | ControlFlowNode for f_extra_pos() | semmle.label | ControlFlowNode for f_extra_pos() | -| test.py:404:10:404:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | semmle.label | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | -| test.py:404:33:404:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:407:26:407:26 | ControlFlowNode for b [Dictionary element at key b] | semmle.label | ControlFlowNode for b [Dictionary element at key b] | -| test.py:408:12:408:12 | ControlFlowNode for b [Dictionary element at key b] | semmle.label | ControlFlowNode for b [Dictionary element at key b] | -| test.py:408:12:408:17 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:412:10:412:45 | ControlFlowNode for f_extra_keyword() | semmle.label | ControlFlowNode for f_extra_keyword() | -| test.py:412:10:412:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | semmle.label | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | -| test.py:412:39:412:44 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:429:10:429:20 | ControlFlowNode for BoolExpr | semmle.label | ControlFlowNode for BoolExpr | -| test.py:429:15:429:20 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:434:10:434:21 | ControlFlowNode for BoolExpr | semmle.label | ControlFlowNode for BoolExpr | -| test.py:434:16:434:21 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:445:10:445:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:445:10:445:38 | ControlFlowNode for IfExp | semmle.label | ControlFlowNode for IfExp | -| test.py:453:10:453:39 | ControlFlowNode for IfExp | semmle.label | ControlFlowNode for IfExp | -| test.py:453:34:453:39 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:474:11:474:11 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:475:16:475:16 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:477:10:477:18 | ControlFlowNode for f() | semmle.label | ControlFlowNode for f() | -| test.py:477:12:477:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:481:19:481:19 | ControlFlowNode for b | semmle.label | ControlFlowNode for b | -| test.py:482:16:482:16 | ControlFlowNode for b | semmle.label | ControlFlowNode for b | -| test.py:484:10:484:34 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() | -| test.py:484:28:484:33 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:495:19:495:19 | ControlFlowNode for b | semmle.label | ControlFlowNode for b | -| test.py:496:16:496:16 | ControlFlowNode for b | semmle.label | ControlFlowNode for b | -| test.py:498:10:498:36 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() | -| test.py:498:30:498:35 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:509:19:509:19 | ControlFlowNode for b | semmle.label | ControlFlowNode for b | -| test.py:510:16:510:16 | ControlFlowNode for b | semmle.label | ControlFlowNode for b | -| test.py:512:10:512:43 | ControlFlowNode for second() | semmle.label | ControlFlowNode for second() | -| test.py:512:10:512:43 | KwUnpacked b | semmle.label | KwUnpacked b | -| test.py:512:30:512:42 | ControlFlowNode for Dict [Dictionary element at key b] | semmle.label | ControlFlowNode for Dict [Dictionary element at key b] | -| test.py:512:36:512:41 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:516:30:516:30 | ControlFlowNode for b [Tuple element at index 0] | semmle.label | ControlFlowNode for b [Tuple element at index 0] | -| test.py:516:33:516:33 | ControlFlowNode for b [Tuple element at index 0] | semmle.label | ControlFlowNode for b [Tuple element at index 0] | -| test.py:516:33:516:36 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:517:10:517:39 | ControlFlowNode for f_extra_pos() | semmle.label | ControlFlowNode for f_extra_pos() | -| test.py:517:10:517:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | semmle.label | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | -| test.py:517:33:517:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:521:35:521:35 | ControlFlowNode for b [Dictionary element at key b] | semmle.label | ControlFlowNode for b [Dictionary element at key b] | -| test.py:521:38:521:38 | ControlFlowNode for b [Dictionary element at key b] | semmle.label | ControlFlowNode for b [Dictionary element at key b] | -| test.py:521:38:521:43 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:522:10:522:45 | ControlFlowNode for f_extra_keyword() | semmle.label | ControlFlowNode for f_extra_keyword() | -| test.py:522:10:522:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | semmle.label | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | -| test.py:522:39:522:44 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:534:9:534:14 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:536:10:536:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a | -| test.py:541:10:541:10 | ControlFlowNode for b | semmle.label | ControlFlowNode for b | -| test.py:546:10:546:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:546:10:546:26 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:547:5:547:5 | SSA variable a | semmle.label | SSA variable a | -| test.py:547:5:547:8 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:548:10:548:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a | -| test.py:554:10:554:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:554:10:554:36 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:554:10:554:36 | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] | -| test.py:554:19:554:35 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:554:30:554:35 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:555:5:555:5 | SSA variable a | semmle.label | SSA variable a | -| test.py:555:5:555:13 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:555:5:555:13 | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1, Tuple element at index 1] | -| test.py:555:9:555:12 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:555:9:555:12 | IterableSequence [Tuple element at index 1] | semmle.label | IterableSequence [Tuple element at index 1] | -| test.py:555:12:555:12 | SSA variable c | semmle.label | SSA variable c | -| test.py:556:10:556:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a | -| test.py:558:10:558:10 | ControlFlowNode for c | semmle.label | ControlFlowNode for c | -| test.py:563:9:563:33 | ControlFlowNode for List [List element, List element, List element, List element] | semmle.label | ControlFlowNode for List [List element, List element, List element, List element] | -| test.py:563:10:563:21 | ControlFlowNode for List [List element, List element, List element] | semmle.label | ControlFlowNode for List [List element, List element, List element] | -| test.py:563:11:563:20 | ControlFlowNode for List [List element, List element] | semmle.label | ControlFlowNode for List [List element, List element] | -| test.py:563:12:563:19 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] | -| test.py:563:13:563:18 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:564:5:564:11 | ControlFlowNode for List [Tuple element at index 0, List element, List element] | semmle.label | ControlFlowNode for List [Tuple element at index 0, List element, List element] | -| test.py:564:5:564:11 | IterableElement [List element, List element] | semmle.label | IterableElement [List element, List element] | -| test.py:564:5:564:11 | IterableSequence [List element, List element, List element] | semmle.label | IterableSequence [List element, List element, List element] | -| test.py:564:5:564:14 | ControlFlowNode for Tuple [Tuple element at index 0, List element, List element, List element] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, List element, List element, List element] | -| test.py:564:5:564:14 | IterableElement [List element, List element, List element] | semmle.label | IterableElement [List element, List element, List element] | -| test.py:564:5:564:14 | IterableSequence [List element, List element, List element, List element] | semmle.label | IterableSequence [List element, List element, List element, List element] | -| test.py:564:6:564:10 | ControlFlowNode for List [Tuple element at index 0, List element] | semmle.label | ControlFlowNode for List [Tuple element at index 0, List element] | -| test.py:564:6:564:10 | IterableElement [List element] | semmle.label | IterableElement [List element] | -| test.py:564:6:564:10 | IterableSequence [List element, List element] | semmle.label | IterableSequence [List element, List element] | -| test.py:564:7:564:9 | ControlFlowNode for List [Tuple element at index 0] | semmle.label | ControlFlowNode for List [Tuple element at index 0] | -| test.py:564:7:564:9 | IterableElement | semmle.label | IterableElement | -| test.py:564:7:564:9 | IterableSequence [List element] | semmle.label | IterableSequence [List element] | -| test.py:564:8:564:8 | SSA variable a | semmle.label | SSA variable a | -| test.py:565:10:565:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a | -| test.py:571:10:571:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:571:10:571:34 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:571:10:571:34 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:571:18:571:23 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:572:5:572:5 | SSA variable a | semmle.label | SSA variable a | -| test.py:572:5:572:12 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:572:5:572:12 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:572:8:572:9 | IterableElement | semmle.label | IterableElement | -| test.py:572:8:572:9 | SSA variable b [List element] | semmle.label | SSA variable b [List element] | -| test.py:572:12:572:12 | SSA variable c | semmle.label | SSA variable c | -| test.py:573:10:573:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a | -| test.py:575:10:575:10 | ControlFlowNode for b [List element] | semmle.label | ControlFlowNode for b [List element] | -| test.py:575:10:575:13 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:576:12:576:12 | ControlFlowNode for c | semmle.label | ControlFlowNode for c | -| test.py:581:10:581:15 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:581:10:581:23 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:581:10:581:23 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:581:18:581:23 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:582:5:582:5 | SSA variable a | semmle.label | SSA variable a | -| test.py:582:5:582:12 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:582:5:582:12 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:582:12:582:12 | SSA variable c | semmle.label | SSA variable c | -| test.py:583:10:583:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a | -| test.py:585:10:585:10 | ControlFlowNode for c | semmle.label | ControlFlowNode for c | -| test.py:590:10:590:61 | ControlFlowNode for List [List element, List element] | semmle.label | ControlFlowNode for List [List element, List element] | -| test.py:590:11:590:37 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] | -| test.py:590:12:590:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:590:31:590:36 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:590:40:590:47 | ControlFlowNode for List [List element] | semmle.label | ControlFlowNode for List [List element] | -| test.py:590:41:590:46 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:593:6:593:23 | ControlFlowNode for Tuple [Tuple element at index 0, List element] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, List element] | -| test.py:593:6:593:23 | IterableElement [List element] | semmle.label | IterableElement [List element] | -| test.py:593:6:593:23 | IterableSequence [List element, List element] | semmle.label | IterableSequence [List element, List element] | -| test.py:593:7:593:8 | SSA variable a1 | semmle.label | SSA variable a1 | -| test.py:593:7:593:16 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:593:7:593:16 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:593:7:593:16 | ControlFlowNode for Tuple [Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 2] | -| test.py:593:7:593:16 | IterableElement | semmle.label | IterableElement | -| test.py:593:7:593:16 | IterableSequence [List element] | semmle.label | IterableSequence [List element] | -| test.py:593:11:593:12 | SSA variable a2 | semmle.label | SSA variable a2 | -| test.py:593:15:593:16 | SSA variable a3 | semmle.label | SSA variable a3 | -| test.py:594:10:594:11 | ControlFlowNode for a1 | semmle.label | ControlFlowNode for a1 | -| test.py:595:12:595:13 | ControlFlowNode for a2 | semmle.label | ControlFlowNode for a2 | -| test.py:596:10:596:11 | ControlFlowNode for a3 | semmle.label | ControlFlowNode for a3 | -| test.py:601:5:601:24 | ControlFlowNode for List [Tuple element at index 0, List element] | semmle.label | ControlFlowNode for List [Tuple element at index 0, List element] | -| test.py:601:5:601:24 | IterableElement [List element] | semmle.label | IterableElement [List element] | -| test.py:601:5:601:24 | IterableSequence [List element, List element] | semmle.label | IterableSequence [List element, List element] | -| test.py:601:7:601:8 | SSA variable a1 | semmle.label | SSA variable a1 | -| test.py:601:7:601:16 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:601:7:601:16 | ControlFlowNode for Tuple [Tuple element at index 1] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 1] | -| test.py:601:7:601:16 | ControlFlowNode for Tuple [Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 2] | -| test.py:601:7:601:16 | IterableElement | semmle.label | IterableElement | -| test.py:601:7:601:16 | IterableSequence [List element] | semmle.label | IterableSequence [List element] | -| test.py:601:11:601:12 | SSA variable a2 | semmle.label | SSA variable a2 | -| test.py:601:15:601:16 | SSA variable a3 | semmle.label | SSA variable a3 | -| test.py:602:10:602:11 | ControlFlowNode for a1 | semmle.label | ControlFlowNode for a1 | -| test.py:603:12:603:13 | ControlFlowNode for a2 | semmle.label | ControlFlowNode for a2 | -| test.py:604:10:604:11 | ControlFlowNode for a3 | semmle.label | ControlFlowNode for a3 | -| test.py:609:6:609:17 | ControlFlowNode for List [Tuple element at index 0] | semmle.label | ControlFlowNode for List [Tuple element at index 0] | -| test.py:609:6:609:17 | ControlFlowNode for List [Tuple element at index 1] | semmle.label | ControlFlowNode for List [Tuple element at index 1] | -| test.py:609:6:609:17 | ControlFlowNode for List [Tuple element at index 2] | semmle.label | ControlFlowNode for List [Tuple element at index 2] | -| test.py:609:6:609:17 | IterableElement | semmle.label | IterableElement | -| test.py:609:6:609:17 | IterableSequence [List element] | semmle.label | IterableSequence [List element] | -| test.py:609:6:609:23 | ControlFlowNode for Tuple [Tuple element at index 0, List element] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, List element] | -| test.py:609:6:609:23 | IterableElement [List element] | semmle.label | IterableElement [List element] | -| test.py:609:6:609:23 | IterableSequence [List element, List element] | semmle.label | IterableSequence [List element, List element] | -| test.py:609:7:609:8 | SSA variable a1 | semmle.label | SSA variable a1 | -| test.py:609:11:609:12 | SSA variable a2 | semmle.label | SSA variable a2 | -| test.py:609:15:609:16 | SSA variable a3 | semmle.label | SSA variable a3 | -| test.py:610:10:610:11 | ControlFlowNode for a1 | semmle.label | ControlFlowNode for a1 | -| test.py:611:12:611:13 | ControlFlowNode for a2 | semmle.label | ControlFlowNode for a2 | -| test.py:612:10:612:11 | ControlFlowNode for a3 | semmle.label | ControlFlowNode for a3 | -| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | -| test.py:618:11:618:47 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | -| test.py:618:12:618:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:618:12:618:36 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:618:12:618:36 | ControlFlowNode for Tuple [Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 2] | -| test.py:618:31:618:36 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:621:5:621:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] | semmle.label | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] | -| test.py:621:5:621:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] | semmle.label | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] | -| test.py:621:6:621:14 | ControlFlowNode for List [Tuple element at index 0] | semmle.label | ControlFlowNode for List [Tuple element at index 0] | -| test.py:621:6:621:14 | ControlFlowNode for List [Tuple element at index 2] | semmle.label | ControlFlowNode for List [Tuple element at index 2] | -| test.py:621:6:621:14 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] | -| test.py:621:6:621:14 | IterableSequence [Tuple element at index 2] | semmle.label | IterableSequence [Tuple element at index 2] | -| test.py:621:7:621:8 | SSA variable a1 | semmle.label | SSA variable a1 | -| test.py:621:11:621:13 | IterableElement | semmle.label | IterableElement | -| test.py:621:11:621:13 | SSA variable a2 [List element] | semmle.label | SSA variable a2 [List element] | -| test.py:622:10:622:11 | ControlFlowNode for a1 | semmle.label | ControlFlowNode for a1 | -| test.py:624:12:624:13 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] | -| test.py:624:12:624:16 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:625:10:625:11 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] | -| test.py:625:10:625:14 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:630:6:630:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | -| test.py:630:6:630:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | -| test.py:630:7:630:8 | SSA variable a1 | semmle.label | SSA variable a1 | -| test.py:630:7:630:13 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:630:7:630:13 | ControlFlowNode for Tuple [Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 2] | -| test.py:630:7:630:13 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] | -| test.py:630:7:630:13 | IterableSequence [Tuple element at index 2] | semmle.label | IterableSequence [Tuple element at index 2] | -| test.py:630:11:630:13 | IterableElement | semmle.label | IterableElement | -| test.py:630:11:630:13 | SSA variable a2 [List element] | semmle.label | SSA variable a2 [List element] | -| test.py:631:10:631:11 | ControlFlowNode for a1 | semmle.label | ControlFlowNode for a1 | -| test.py:633:12:633:13 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] | -| test.py:633:12:633:16 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:634:10:634:11 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] | -| test.py:634:10:634:14 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:639:5:639:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] | semmle.label | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 0] | -| test.py:639:5:639:19 | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] | semmle.label | ControlFlowNode for List [Tuple element at index 0, Tuple element at index 2] | -| test.py:639:7:639:8 | SSA variable a1 | semmle.label | SSA variable a1 | -| test.py:639:7:639:13 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:639:7:639:13 | ControlFlowNode for Tuple [Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 2] | -| test.py:639:7:639:13 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] | -| test.py:639:7:639:13 | IterableSequence [Tuple element at index 2] | semmle.label | IterableSequence [Tuple element at index 2] | -| test.py:639:11:639:13 | IterableElement | semmle.label | IterableElement | -| test.py:639:11:639:13 | SSA variable a2 [List element] | semmle.label | SSA variable a2 [List element] | -| test.py:640:10:640:11 | ControlFlowNode for a1 | semmle.label | ControlFlowNode for a1 | -| test.py:642:12:642:13 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] | -| test.py:642:12:642:16 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:643:10:643:11 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] | -| test.py:643:10:643:14 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:648:6:648:14 | ControlFlowNode for List [Tuple element at index 0] | semmle.label | ControlFlowNode for List [Tuple element at index 0] | -| test.py:648:6:648:14 | ControlFlowNode for List [Tuple element at index 2] | semmle.label | ControlFlowNode for List [Tuple element at index 2] | -| test.py:648:6:648:14 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] | -| test.py:648:6:648:14 | IterableSequence [Tuple element at index 2] | semmle.label | IterableSequence [Tuple element at index 2] | -| test.py:648:6:648:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 0] | -| test.py:648:6:648:18 | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0, Tuple element at index 2] | -| test.py:648:7:648:8 | SSA variable a1 | semmle.label | SSA variable a1 | -| test.py:648:11:648:13 | IterableElement | semmle.label | IterableElement | -| test.py:648:11:648:13 | SSA variable a2 [List element] | semmle.label | SSA variable a2 [List element] | -| test.py:649:10:649:11 | ControlFlowNode for a1 | semmle.label | ControlFlowNode for a1 | -| test.py:651:12:651:13 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] | -| test.py:651:12:651:16 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:652:10:652:11 | ControlFlowNode for a2 [List element] | semmle.label | ControlFlowNode for a2 [List element] | -| test.py:652:10:652:14 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:659:19:659:24 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:660:10:660:10 | ControlFlowNode for a | semmle.label | ControlFlowNode for a | -| test.py:667:10:667:51 | ControlFlowNode for List [List element, Tuple element at index 0] | semmle.label | ControlFlowNode for List [List element, Tuple element at index 0] | -| test.py:667:12:667:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:667:12:667:28 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:667:33:667:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:667:33:667:49 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:668:9:668:9 | SSA variable x | semmle.label | SSA variable x | -| test.py:668:9:668:11 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:668:9:668:11 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] | -| test.py:668:16:668:17 | ControlFlowNode for tl [List element, Tuple element at index 0] | semmle.label | ControlFlowNode for tl [List element, Tuple element at index 0] | -| test.py:669:14:669:14 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:675:10:675:51 | ControlFlowNode for List [List element, Tuple element at index 0] | semmle.label | ControlFlowNode for List [List element, Tuple element at index 0] | -| test.py:675:12:675:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:675:12:675:28 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:675:33:675:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:675:33:675:49 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:676:9:676:10 | IterableElement | semmle.label | IterableElement | -| test.py:676:9:676:10 | SSA variable x [List element] | semmle.label | SSA variable x [List element] | -| test.py:676:9:676:12 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:676:9:676:12 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] | -| test.py:676:12:676:12 | SSA variable y | semmle.label | SSA variable y | -| test.py:676:17:676:18 | ControlFlowNode for tl [List element, Tuple element at index 0] | semmle.label | ControlFlowNode for tl [List element, Tuple element at index 0] | -| test.py:678:14:678:14 | ControlFlowNode for x [List element] | semmle.label | ControlFlowNode for x [List element] | -| test.py:678:14:678:17 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | -| test.py:679:16:679:16 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | -| test.py:684:10:684:51 | ControlFlowNode for List [List element, Tuple element at index 0] | semmle.label | ControlFlowNode for List [List element, Tuple element at index 0] | -| test.py:684:12:684:17 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:684:12:684:28 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:684:33:684:38 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:684:33:684:49 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:685:9:685:9 | SSA variable x | semmle.label | SSA variable x | -| test.py:685:9:685:14 | ControlFlowNode for Tuple [Tuple element at index 0] | semmle.label | ControlFlowNode for Tuple [Tuple element at index 0] | -| test.py:685:9:685:14 | IterableSequence [Tuple element at index 0] | semmle.label | IterableSequence [Tuple element at index 0] | -| test.py:685:19:685:20 | ControlFlowNode for tl [List element, Tuple element at index 0] | semmle.label | ControlFlowNode for tl [List element, Tuple element at index 0] | -| test.py:686:14:686:14 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:690:39:690:42 | ControlFlowNode for args [Tuple element at index 0] | semmle.label | ControlFlowNode for args [Tuple element at index 0] | -| test.py:690:39:690:42 | ControlFlowNode for args [Tuple element at index 1] | semmle.label | ControlFlowNode for args [Tuple element at index 1] | -| test.py:691:7:691:9 | SSA variable arg | semmle.label | SSA variable arg | -| test.py:691:14:691:17 | ControlFlowNode for args [Tuple element at index 0] | semmle.label | ControlFlowNode for args [Tuple element at index 0] | -| test.py:691:14:691:17 | ControlFlowNode for args [Tuple element at index 1] | semmle.label | ControlFlowNode for args [Tuple element at index 1] | -| test.py:692:10:692:12 | ControlFlowNode for arg | semmle.label | ControlFlowNode for arg | -| test.py:697:7:697:12 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:698:3:698:52 | PosOverflowNode for iterate_star_args() [Tuple element at index 0] | semmle.label | PosOverflowNode for iterate_star_args() [Tuple element at index 0] | -| test.py:698:3:698:52 | PosOverflowNode for iterate_star_args() [Tuple element at index 1] | semmle.label | PosOverflowNode for iterate_star_args() [Tuple element at index 1] | -| test.py:698:43:698:48 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:698:51:698:51 | ControlFlowNode for s | semmle.label | ControlFlowNode for s | -| test.py:769:16:769:21 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:772:10:772:36 | ControlFlowNode for return_from_inner_scope() | semmle.label | ControlFlowNode for return_from_inner_scope() | -| test.py:807:35:807:35 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:807:37:807:42 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:807:48:807:48 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | -| test.py:807:50:807:55 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:807:61:807:61 | ControlFlowNode for z | semmle.label | ControlFlowNode for z | -| test.py:807:63:807:68 | ControlFlowNode for SOURCE | semmle.label | ControlFlowNode for SOURCE | -| test.py:808:10:808:10 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | -| test.py:809:10:809:10 | ControlFlowNode for y | semmle.label | ControlFlowNode for y | -| test.py:810:10:810:10 | ControlFlowNode for z | semmle.label | ControlFlowNode for z | -subpaths -| datamodel.py:38:8:38:13 | ControlFlowNode for SOURCE | datamodel.py:35:7:35:7 | ControlFlowNode for a | datamodel.py:36:10:36:10 | ControlFlowNode for a | datamodel.py:38:6:38:17 | ControlFlowNode for f() | -| datamodel.py:71:15:71:20 | ControlFlowNode for SOURCE | datamodel.py:44:22:44:22 | ControlFlowNode for x | datamodel.py:46:16:46:16 | ControlFlowNode for x | datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() | -| datamodel.py:72:18:72:23 | ControlFlowNode for SOURCE | datamodel.py:44:22:44:22 | ControlFlowNode for x | datamodel.py:46:16:46:16 | ControlFlowNode for x | datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | -| datamodel.py:80:20:80:25 | ControlFlowNode for SOURCE | datamodel.py:49:26:49:26 | ControlFlowNode for x | datamodel.py:50:16:50:16 | ControlFlowNode for x | datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | -| datamodel.py:81:20:81:25 | ControlFlowNode for SOURCE | datamodel.py:49:26:49:26 | ControlFlowNode for x | datamodel.py:50:16:50:16 | ControlFlowNode for x | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | -| test.py:380:28:380:33 | ControlFlowNode for SOURCE | test.py:375:15:375:15 | ControlFlowNode for b | test.py:376:12:376:12 | ControlFlowNode for b | test.py:380:10:380:34 | ControlFlowNode for second() | -| test.py:388:30:388:35 | ControlFlowNode for SOURCE | test.py:375:15:375:15 | ControlFlowNode for b | test.py:376:12:376:12 | ControlFlowNode for b | test.py:388:10:388:36 | ControlFlowNode for second() | -| test.py:396:10:396:43 | KwUnpacked b | test.py:375:15:375:15 | ControlFlowNode for b | test.py:376:12:376:12 | ControlFlowNode for b | test.py:396:10:396:43 | ControlFlowNode for second() | -| test.py:404:10:404:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | test.py:399:21:399:21 | ControlFlowNode for b [Tuple element at index 0] | test.py:400:12:400:15 | ControlFlowNode for Subscript | test.py:404:10:404:39 | ControlFlowNode for f_extra_pos() | -| test.py:412:10:412:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | test.py:407:26:407:26 | ControlFlowNode for b [Dictionary element at key b] | test.py:408:12:408:17 | ControlFlowNode for Subscript | test.py:412:10:412:45 | ControlFlowNode for f_extra_keyword() | -| test.py:477:12:477:17 | ControlFlowNode for SOURCE | test.py:474:11:474:11 | ControlFlowNode for x | test.py:475:16:475:16 | ControlFlowNode for x | test.py:477:10:477:18 | ControlFlowNode for f() | -| test.py:484:28:484:33 | ControlFlowNode for SOURCE | test.py:481:19:481:19 | ControlFlowNode for b | test.py:482:16:482:16 | ControlFlowNode for b | test.py:484:10:484:34 | ControlFlowNode for second() | -| test.py:498:30:498:35 | ControlFlowNode for SOURCE | test.py:495:19:495:19 | ControlFlowNode for b | test.py:496:16:496:16 | ControlFlowNode for b | test.py:498:10:498:36 | ControlFlowNode for second() | -| test.py:512:10:512:43 | KwUnpacked b | test.py:509:19:509:19 | ControlFlowNode for b | test.py:510:16:510:16 | ControlFlowNode for b | test.py:512:10:512:43 | ControlFlowNode for second() | -| test.py:517:10:517:39 | PosOverflowNode for f_extra_pos() [Tuple element at index 0] | test.py:516:30:516:30 | ControlFlowNode for b [Tuple element at index 0] | test.py:516:33:516:36 | ControlFlowNode for Subscript | test.py:517:10:517:39 | ControlFlowNode for f_extra_pos() | -| test.py:522:10:522:45 | KwOverflowNode for f_extra_keyword() [Dictionary element at key b] | test.py:521:35:521:35 | ControlFlowNode for b [Dictionary element at key b] | test.py:521:38:521:43 | ControlFlowNode for Subscript | test.py:522:10:522:45 | ControlFlowNode for f_extra_keyword() | -#select -| datamodel.py:38:6:38:17 | ControlFlowNode for f() | datamodel.py:38:8:38:13 | ControlFlowNode for SOURCE | datamodel.py:38:6:38:17 | ControlFlowNode for f() | Flow found | -| datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() | datamodel.py:71:15:71:20 | ControlFlowNode for SOURCE | datamodel.py:71:6:71:24 | ControlFlowNode for Attribute() | Flow found | -| datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | datamodel.py:72:18:72:23 | ControlFlowNode for SOURCE | datamodel.py:72:6:72:27 | ControlFlowNode for Attribute() | Flow found | -| datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | datamodel.py:80:20:80:25 | ControlFlowNode for SOURCE | datamodel.py:80:6:80:26 | ControlFlowNode for Attribute() | Flow found | -| datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | datamodel.py:81:20:81:25 | ControlFlowNode for SOURCE | datamodel.py:81:6:81:26 | ControlFlowNode for Attribute() | Flow found | -| datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | datamodel.py:152:14:152:19 | ControlFlowNode for SOURCE | datamodel.py:159:6:159:17 | ControlFlowNode for Attribute | Flow found | -| test.py:44:10:44:10 | ControlFlowNode for y | test.py:42:21:42:26 | ControlFlowNode for SOURCE | test.py:44:10:44:10 | ControlFlowNode for y | Flow found | -| test.py:56:10:56:10 | ControlFlowNode for x | test.py:55:9:55:14 | ControlFlowNode for SOURCE | test.py:56:10:56:10 | ControlFlowNode for x | Flow found | -| test.py:62:10:62:10 | ControlFlowNode for x | test.py:61:9:61:16 | ControlFlowNode for Str | test.py:62:10:62:10 | ControlFlowNode for x | Flow found | -| test.py:67:10:67:10 | ControlFlowNode for x | test.py:66:9:66:17 | ControlFlowNode for Str | test.py:67:10:67:10 | ControlFlowNode for x | Flow found | -| test.py:72:10:72:10 | ControlFlowNode for x | test.py:71:9:71:10 | ControlFlowNode for IntegerLiteral | test.py:72:10:72:10 | ControlFlowNode for x | Flow found | -| test.py:77:10:77:10 | ControlFlowNode for x | test.py:76:9:76:12 | ControlFlowNode for FloatLiteral | test.py:77:10:77:10 | ControlFlowNode for x | Flow found | -| test.py:88:10:88:10 | ControlFlowNode for x | test.py:87:10:87:15 | ControlFlowNode for SOURCE | test.py:88:10:88:10 | ControlFlowNode for x | Flow found | -| test.py:94:10:94:13 | ControlFlowNode for Subscript | test.py:93:10:93:15 | ControlFlowNode for SOURCE | test.py:94:10:94:13 | ControlFlowNode for Subscript | Flow found | -| test.py:104:10:104:13 | ControlFlowNode for Subscript | test.py:103:10:103:15 | ControlFlowNode for SOURCE | test.py:104:10:104:13 | ControlFlowNode for Subscript | Flow found | -| test.py:109:10:109:13 | ControlFlowNode for Subscript | test.py:108:22:108:27 | ControlFlowNode for SOURCE | test.py:109:10:109:13 | ControlFlowNode for Subscript | Flow found | -| test.py:115:10:115:13 | ControlFlowNode for Subscript | test.py:113:10:113:15 | ControlFlowNode for SOURCE | test.py:115:10:115:13 | ControlFlowNode for Subscript | Flow found | -| test.py:126:10:126:16 | ControlFlowNode for Attribute() | test.py:125:10:125:15 | ControlFlowNode for SOURCE | test.py:126:10:126:16 | ControlFlowNode for Attribute() | Flow found | -| test.py:131:10:131:16 | ControlFlowNode for Attribute() | test.py:130:10:130:15 | ControlFlowNode for SOURCE | test.py:131:10:131:16 | ControlFlowNode for Attribute() | Flow found | -| test.py:136:10:136:16 | ControlFlowNode for Attribute() | test.py:135:22:135:27 | ControlFlowNode for SOURCE | test.py:136:10:136:16 | ControlFlowNode for Attribute() | Flow found | -| test.py:142:10:142:16 | ControlFlowNode for Attribute() | test.py:140:10:140:15 | ControlFlowNode for SOURCE | test.py:142:10:142:16 | ControlFlowNode for Attribute() | Flow found | -| test.py:153:10:153:15 | ControlFlowNode for Subscript | test.py:152:15:152:20 | ControlFlowNode for SOURCE | test.py:153:10:153:15 | ControlFlowNode for Subscript | Flow found | -| test.py:158:10:158:19 | ControlFlowNode for Attribute() | test.py:157:15:157:20 | ControlFlowNode for SOURCE | test.py:158:10:158:19 | ControlFlowNode for Attribute() | Flow found | -| test.py:184:10:184:13 | ControlFlowNode for Subscript | test.py:183:23:183:28 | ControlFlowNode for SOURCE | test.py:184:10:184:13 | ControlFlowNode for Subscript | Flow found | -| test.py:189:10:189:13 | ControlFlowNode for Subscript | test.py:188:25:188:30 | ControlFlowNode for SOURCE | test.py:189:10:189:13 | ControlFlowNode for Subscript | Flow found | -| test.py:200:10:200:13 | ControlFlowNode for Subscript | test.py:199:34:199:39 | ControlFlowNode for SOURCE | test.py:200:10:200:13 | ControlFlowNode for Subscript | Flow found | -| test.py:206:10:206:13 | ControlFlowNode for Subscript | test.py:205:28:205:33 | ControlFlowNode for SOURCE | test.py:206:10:206:13 | ControlFlowNode for Subscript | Flow found | -| test.py:211:10:211:13 | ControlFlowNode for Subscript | test.py:210:32:210:37 | ControlFlowNode for SOURCE | test.py:211:10:211:13 | ControlFlowNode for Subscript | Flow found | -| test.py:349:10:349:21 | ControlFlowNode for Subscript | test.py:349:11:349:16 | ControlFlowNode for SOURCE | test.py:349:10:349:21 | ControlFlowNode for Subscript | Flow found | -| test.py:353:10:353:20 | ControlFlowNode for Subscript | test.py:353:11:353:16 | ControlFlowNode for SOURCE | test.py:353:10:353:20 | ControlFlowNode for Subscript | Flow found | -| test.py:357:10:357:27 | ControlFlowNode for Subscript | test.py:357:16:357:21 | ControlFlowNode for SOURCE | test.py:357:10:357:27 | ControlFlowNode for Subscript | Flow found | -| test.py:380:10:380:34 | ControlFlowNode for second() | test.py:380:28:380:33 | ControlFlowNode for SOURCE | test.py:380:10:380:34 | ControlFlowNode for second() | Flow found | -| test.py:388:10:388:36 | ControlFlowNode for second() | test.py:388:30:388:35 | ControlFlowNode for SOURCE | test.py:388:10:388:36 | ControlFlowNode for second() | Flow found | -| test.py:396:10:396:43 | ControlFlowNode for second() | test.py:396:36:396:41 | ControlFlowNode for SOURCE | test.py:396:10:396:43 | ControlFlowNode for second() | Flow found | -| test.py:404:10:404:39 | ControlFlowNode for f_extra_pos() | test.py:404:33:404:38 | ControlFlowNode for SOURCE | test.py:404:10:404:39 | ControlFlowNode for f_extra_pos() | Flow found | -| test.py:412:10:412:45 | ControlFlowNode for f_extra_keyword() | test.py:412:39:412:44 | ControlFlowNode for SOURCE | test.py:412:10:412:45 | ControlFlowNode for f_extra_keyword() | Flow found | -| test.py:429:10:429:20 | ControlFlowNode for BoolExpr | test.py:429:15:429:20 | ControlFlowNode for SOURCE | test.py:429:10:429:20 | ControlFlowNode for BoolExpr | Flow found | -| test.py:434:10:434:21 | ControlFlowNode for BoolExpr | test.py:434:16:434:21 | ControlFlowNode for SOURCE | test.py:434:10:434:21 | ControlFlowNode for BoolExpr | Flow found | -| test.py:445:10:445:38 | ControlFlowNode for IfExp | test.py:445:10:445:15 | ControlFlowNode for SOURCE | test.py:445:10:445:38 | ControlFlowNode for IfExp | Flow found | -| test.py:453:10:453:39 | ControlFlowNode for IfExp | test.py:453:34:453:39 | ControlFlowNode for SOURCE | test.py:453:10:453:39 | ControlFlowNode for IfExp | Flow found | -| test.py:477:10:477:18 | ControlFlowNode for f() | test.py:477:12:477:17 | ControlFlowNode for SOURCE | test.py:477:10:477:18 | ControlFlowNode for f() | Flow found | -| test.py:484:10:484:34 | ControlFlowNode for second() | test.py:484:28:484:33 | ControlFlowNode for SOURCE | test.py:484:10:484:34 | ControlFlowNode for second() | Flow found | -| test.py:498:10:498:36 | ControlFlowNode for second() | test.py:498:30:498:35 | ControlFlowNode for SOURCE | test.py:498:10:498:36 | ControlFlowNode for second() | Flow found | -| test.py:512:10:512:43 | ControlFlowNode for second() | test.py:512:36:512:41 | ControlFlowNode for SOURCE | test.py:512:10:512:43 | ControlFlowNode for second() | Flow found | -| test.py:517:10:517:39 | ControlFlowNode for f_extra_pos() | test.py:517:33:517:38 | ControlFlowNode for SOURCE | test.py:517:10:517:39 | ControlFlowNode for f_extra_pos() | Flow found | -| test.py:522:10:522:45 | ControlFlowNode for f_extra_keyword() | test.py:522:39:522:44 | ControlFlowNode for SOURCE | test.py:522:10:522:45 | ControlFlowNode for f_extra_keyword() | Flow found | -| test.py:536:10:536:10 | ControlFlowNode for a | test.py:534:9:534:14 | ControlFlowNode for SOURCE | test.py:536:10:536:10 | ControlFlowNode for a | Flow found | -| test.py:541:10:541:10 | ControlFlowNode for b | test.py:534:9:534:14 | ControlFlowNode for SOURCE | test.py:541:10:541:10 | ControlFlowNode for b | Flow found | -| test.py:548:10:548:10 | ControlFlowNode for a | test.py:546:10:546:15 | ControlFlowNode for SOURCE | test.py:548:10:548:10 | ControlFlowNode for a | Flow found | -| test.py:556:10:556:10 | ControlFlowNode for a | test.py:554:10:554:15 | ControlFlowNode for SOURCE | test.py:556:10:556:10 | ControlFlowNode for a | Flow found | -| test.py:558:10:558:10 | ControlFlowNode for c | test.py:554:30:554:35 | ControlFlowNode for SOURCE | test.py:558:10:558:10 | ControlFlowNode for c | Flow found | -| test.py:565:10:565:10 | ControlFlowNode for a | test.py:563:13:563:18 | ControlFlowNode for SOURCE | test.py:565:10:565:10 | ControlFlowNode for a | Flow found | -| test.py:573:10:573:10 | ControlFlowNode for a | test.py:571:10:571:15 | ControlFlowNode for SOURCE | test.py:573:10:573:10 | ControlFlowNode for a | Flow found | -| test.py:575:10:575:13 | ControlFlowNode for Subscript | test.py:571:18:571:23 | ControlFlowNode for SOURCE | test.py:575:10:575:13 | ControlFlowNode for Subscript | Flow found | -| test.py:576:12:576:12 | ControlFlowNode for c | test.py:571:18:571:23 | ControlFlowNode for SOURCE | test.py:576:12:576:12 | ControlFlowNode for c | Flow found | -| test.py:583:10:583:10 | ControlFlowNode for a | test.py:581:10:581:15 | ControlFlowNode for SOURCE | test.py:583:10:583:10 | ControlFlowNode for a | Flow found | -| test.py:585:10:585:10 | ControlFlowNode for c | test.py:581:18:581:23 | ControlFlowNode for SOURCE | test.py:585:10:585:10 | ControlFlowNode for c | Flow found | -| test.py:594:10:594:11 | ControlFlowNode for a1 | test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:594:10:594:11 | ControlFlowNode for a1 | Flow found | -| test.py:594:10:594:11 | ControlFlowNode for a1 | test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:594:10:594:11 | ControlFlowNode for a1 | Flow found | -| test.py:594:10:594:11 | ControlFlowNode for a1 | test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:594:10:594:11 | ControlFlowNode for a1 | Flow found | -| test.py:595:12:595:13 | ControlFlowNode for a2 | test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:595:12:595:13 | ControlFlowNode for a2 | Flow found | -| test.py:595:12:595:13 | ControlFlowNode for a2 | test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:595:12:595:13 | ControlFlowNode for a2 | Flow found | -| test.py:595:12:595:13 | ControlFlowNode for a2 | test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:595:12:595:13 | ControlFlowNode for a2 | Flow found | -| test.py:596:10:596:11 | ControlFlowNode for a3 | test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:596:10:596:11 | ControlFlowNode for a3 | Flow found | -| test.py:596:10:596:11 | ControlFlowNode for a3 | test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:596:10:596:11 | ControlFlowNode for a3 | Flow found | -| test.py:596:10:596:11 | ControlFlowNode for a3 | test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:596:10:596:11 | ControlFlowNode for a3 | Flow found | -| test.py:602:10:602:11 | ControlFlowNode for a1 | test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:602:10:602:11 | ControlFlowNode for a1 | Flow found | -| test.py:602:10:602:11 | ControlFlowNode for a1 | test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:602:10:602:11 | ControlFlowNode for a1 | Flow found | -| test.py:602:10:602:11 | ControlFlowNode for a1 | test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:602:10:602:11 | ControlFlowNode for a1 | Flow found | -| test.py:603:12:603:13 | ControlFlowNode for a2 | test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:603:12:603:13 | ControlFlowNode for a2 | Flow found | -| test.py:603:12:603:13 | ControlFlowNode for a2 | test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:603:12:603:13 | ControlFlowNode for a2 | Flow found | -| test.py:603:12:603:13 | ControlFlowNode for a2 | test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:603:12:603:13 | ControlFlowNode for a2 | Flow found | -| test.py:604:10:604:11 | ControlFlowNode for a3 | test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:604:10:604:11 | ControlFlowNode for a3 | Flow found | -| test.py:604:10:604:11 | ControlFlowNode for a3 | test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:604:10:604:11 | ControlFlowNode for a3 | Flow found | -| test.py:604:10:604:11 | ControlFlowNode for a3 | test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:604:10:604:11 | ControlFlowNode for a3 | Flow found | -| test.py:610:10:610:11 | ControlFlowNode for a1 | test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:610:10:610:11 | ControlFlowNode for a1 | Flow found | -| test.py:610:10:610:11 | ControlFlowNode for a1 | test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:610:10:610:11 | ControlFlowNode for a1 | Flow found | -| test.py:610:10:610:11 | ControlFlowNode for a1 | test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:610:10:610:11 | ControlFlowNode for a1 | Flow found | -| test.py:611:12:611:13 | ControlFlowNode for a2 | test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:611:12:611:13 | ControlFlowNode for a2 | Flow found | -| test.py:611:12:611:13 | ControlFlowNode for a2 | test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:611:12:611:13 | ControlFlowNode for a2 | Flow found | -| test.py:611:12:611:13 | ControlFlowNode for a2 | test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:611:12:611:13 | ControlFlowNode for a2 | Flow found | -| test.py:612:10:612:11 | ControlFlowNode for a3 | test.py:590:12:590:17 | ControlFlowNode for SOURCE | test.py:612:10:612:11 | ControlFlowNode for a3 | Flow found | -| test.py:612:10:612:11 | ControlFlowNode for a3 | test.py:590:31:590:36 | ControlFlowNode for SOURCE | test.py:612:10:612:11 | ControlFlowNode for a3 | Flow found | -| test.py:612:10:612:11 | ControlFlowNode for a3 | test.py:590:41:590:46 | ControlFlowNode for SOURCE | test.py:612:10:612:11 | ControlFlowNode for a3 | Flow found | -| test.py:622:10:622:11 | ControlFlowNode for a1 | test.py:618:12:618:17 | ControlFlowNode for SOURCE | test.py:622:10:622:11 | ControlFlowNode for a1 | Flow found | -| test.py:624:12:624:16 | ControlFlowNode for Subscript | test.py:618:31:618:36 | ControlFlowNode for SOURCE | test.py:624:12:624:16 | ControlFlowNode for Subscript | Flow found | -| test.py:625:10:625:14 | ControlFlowNode for Subscript | test.py:618:31:618:36 | ControlFlowNode for SOURCE | test.py:625:10:625:14 | ControlFlowNode for Subscript | Flow found | -| test.py:631:10:631:11 | ControlFlowNode for a1 | test.py:618:12:618:17 | ControlFlowNode for SOURCE | test.py:631:10:631:11 | ControlFlowNode for a1 | Flow found | -| test.py:633:12:633:16 | ControlFlowNode for Subscript | test.py:618:31:618:36 | ControlFlowNode for SOURCE | test.py:633:12:633:16 | ControlFlowNode for Subscript | Flow found | -| test.py:634:10:634:14 | ControlFlowNode for Subscript | test.py:618:31:618:36 | ControlFlowNode for SOURCE | test.py:634:10:634:14 | ControlFlowNode for Subscript | Flow found | -| test.py:640:10:640:11 | ControlFlowNode for a1 | test.py:618:12:618:17 | ControlFlowNode for SOURCE | test.py:640:10:640:11 | ControlFlowNode for a1 | Flow found | -| test.py:642:12:642:16 | ControlFlowNode for Subscript | test.py:618:31:618:36 | ControlFlowNode for SOURCE | test.py:642:12:642:16 | ControlFlowNode for Subscript | Flow found | -| test.py:643:10:643:14 | ControlFlowNode for Subscript | test.py:618:31:618:36 | ControlFlowNode for SOURCE | test.py:643:10:643:14 | ControlFlowNode for Subscript | Flow found | -| test.py:649:10:649:11 | ControlFlowNode for a1 | test.py:618:12:618:17 | ControlFlowNode for SOURCE | test.py:649:10:649:11 | ControlFlowNode for a1 | Flow found | -| test.py:651:12:651:16 | ControlFlowNode for Subscript | test.py:618:31:618:36 | ControlFlowNode for SOURCE | test.py:651:12:651:16 | ControlFlowNode for Subscript | Flow found | -| test.py:652:10:652:14 | ControlFlowNode for Subscript | test.py:618:31:618:36 | ControlFlowNode for SOURCE | test.py:652:10:652:14 | ControlFlowNode for Subscript | Flow found | -| test.py:660:10:660:10 | ControlFlowNode for a | test.py:659:19:659:24 | ControlFlowNode for SOURCE | test.py:660:10:660:10 | ControlFlowNode for a | Flow found | -| test.py:669:14:669:14 | ControlFlowNode for x | test.py:667:12:667:17 | ControlFlowNode for SOURCE | test.py:669:14:669:14 | ControlFlowNode for x | Flow found | -| test.py:669:14:669:14 | ControlFlowNode for x | test.py:667:33:667:38 | ControlFlowNode for SOURCE | test.py:669:14:669:14 | ControlFlowNode for x | Flow found | -| test.py:678:14:678:17 | ControlFlowNode for Subscript | test.py:675:12:675:17 | ControlFlowNode for SOURCE | test.py:678:14:678:17 | ControlFlowNode for Subscript | Flow found | -| test.py:678:14:678:17 | ControlFlowNode for Subscript | test.py:675:33:675:38 | ControlFlowNode for SOURCE | test.py:678:14:678:17 | ControlFlowNode for Subscript | Flow found | -| test.py:679:16:679:16 | ControlFlowNode for y | test.py:675:12:675:17 | ControlFlowNode for SOURCE | test.py:679:16:679:16 | ControlFlowNode for y | Flow found | -| test.py:679:16:679:16 | ControlFlowNode for y | test.py:675:33:675:38 | ControlFlowNode for SOURCE | test.py:679:16:679:16 | ControlFlowNode for y | Flow found | -| test.py:686:14:686:14 | ControlFlowNode for x | test.py:684:12:684:17 | ControlFlowNode for SOURCE | test.py:686:14:686:14 | ControlFlowNode for x | Flow found | -| test.py:686:14:686:14 | ControlFlowNode for x | test.py:684:33:684:38 | ControlFlowNode for SOURCE | test.py:686:14:686:14 | ControlFlowNode for x | Flow found | -| test.py:692:10:692:12 | ControlFlowNode for arg | test.py:697:7:697:12 | ControlFlowNode for SOURCE | test.py:692:10:692:12 | ControlFlowNode for arg | Flow found | -| test.py:692:10:692:12 | ControlFlowNode for arg | test.py:698:43:698:48 | ControlFlowNode for SOURCE | test.py:692:10:692:12 | ControlFlowNode for arg | Flow found | -| test.py:772:10:772:36 | ControlFlowNode for return_from_inner_scope() | test.py:769:16:769:21 | ControlFlowNode for SOURCE | test.py:772:10:772:36 | ControlFlowNode for return_from_inner_scope() | Flow found | -| test.py:808:10:808:10 | ControlFlowNode for x | test.py:807:37:807:42 | ControlFlowNode for SOURCE | test.py:808:10:808:10 | ControlFlowNode for x | Flow found | -| test.py:809:10:809:10 | ControlFlowNode for y | test.py:807:50:807:55 | ControlFlowNode for SOURCE | test.py:809:10:809:10 | ControlFlowNode for y | Flow found | -| test.py:810:10:810:10 | ControlFlowNode for z | test.py:807:63:807:68 | ControlFlowNode for SOURCE | test.py:810:10:810:10 | ControlFlowNode for z | Flow found | diff --git a/python/ql/test/experimental/dataflow/coverage/dataflow.ql b/python/ql/test/experimental/dataflow/coverage/dataflow.ql deleted file mode 100644 index 868f24a598f..00000000000 --- a/python/ql/test/experimental/dataflow/coverage/dataflow.ql +++ /dev/null @@ -1,11 +0,0 @@ -/** - * @kind path-problem - */ - -import python -import experimental.dataflow.testConfig -import DataFlow::PathGraph - -from TestConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink -where config.hasFlowPath(source, sink) -select sink.getNode(), source, sink, "Flow found" diff --git a/python/ql/test/experimental/dataflow/coverage/datamodel.py b/python/ql/test/experimental/dataflow/coverage/datamodel.py index f165e3d67e1..364dbb299d7 100644 --- a/python/ql/test/experimental/dataflow/coverage/datamodel.py +++ b/python/ql/test/experimental/dataflow/coverage/datamodel.py @@ -8,15 +8,30 @@ # Intended sources should be the variable `SOURCE` and intended sinks should be # arguments to the function `SINK` (see python/ql/test/experimental/dataflow/testConfig.qll). +import sys +import os +import functools + +sys.path.append(os.path.dirname(os.path.dirname((__file__)))) +from testlib import expects + # These are defined so that we can evaluate the test code. NONSOURCE = "not a source" SOURCE = "source" +arg1 = "source1" +arg2 = "source2" +arg3 = "source3" +arg4 = "source4" +arg5 = "source5" +arg6 = "source6" +arg7 = "source7" + def is_source(x): return x == "source" or x == b"source" or x == 42 or x == 42.0 or x == 42j -def SINK(x): - if is_source(x): +def SINK(x, expected=SOURCE): + if is_source(x) or x == expected: print("OK") else: print("Unexpected flow", x) @@ -27,6 +42,14 @@ def SINK_F(x): else: print("OK") +SINK1 = functools.partial(SINK, expected=arg1) +SINK2 = functools.partial(SINK, expected=arg2) +SINK3 = functools.partial(SINK, expected=arg3) +SINK4 = functools.partial(SINK, expected=arg4) +SINK5 = functools.partial(SINK, expected=arg5) +SINK6 = functools.partial(SINK, expected=arg6) +SINK7 = functools.partial(SINK, expected=arg7) + # Callable types # These are the types to which the function call operation (see section Calls) can be applied: @@ -41,17 +64,19 @@ SINK(f(SOURCE, 3)) #$ flow="SOURCE -> f(..)" # An instance method object combines a class, a class instance and any callable object (normally a user-defined function). class C(object): - def method(self, x, cls): - assert cls is self.__class__ - return x + def method(self, x, y): + SINK1(x) + SINK2(y) @classmethod - def classmethod(cls, x): - return x + def classmethod(cls, x, y): + SINK1(x) + SINK2(y) @staticmethod - def staticmethod(x): - return x + def staticmethod(x, y): + SINK1(x) + SINK2(y) def gen(self, x, count): n = count @@ -64,22 +89,40 @@ class C(object): c = C() -# When an instance method object is created by retrieving a user-defined function object from a class via one of its instances, its __self__ attribute is the instance, and the method object is said to be bound. The new method’s __func__ attribute is the original function object. -func_obj = c.method.__func__ +@expects(6) +def test_method_call(): + # When an instance method object is created by retrieving a user-defined function object from a class via one of its instances, its __self__ attribute is the instance, and the method object is said to be bound. The new method’s __func__ attribute is the original function object. + func_obj = c.method.__func__ -# When an instance method object is called, the underlying function (__func__) is called, inserting the class instance (__self__) in front of the argument list. For instance, when C is a class which contains a definition for a function f(), and x is an instance of C, calling x.f(1) is equivalent to calling C.f(x, 1). -SINK(c.method(SOURCE, C)) #$ flow="SOURCE -> c.method(..)" -SINK(C.method(c, SOURCE, C)) #$ flow="SOURCE -> C.method(..)" -SINK(func_obj(c, SOURCE, C)) #$ MISSING: flow="SOURCE -> func_obj(..)" + # When an instance method object is called, the underlying function (__func__) is called, inserting the class instance (__self__) in front of the argument list. For instance, when C is a class which contains a definition for a function f(), and x is an instance of C, calling x.f(1) is equivalent to calling C.f(x, 1). + c.method(arg1, arg2) # $ func=C.method arg1 arg2 + C.method(c, arg1, arg2) # $ func=C.method arg1 arg2 + func_obj(c, arg1, arg2) # $ MISSING: func=C.method arg1 arg2 -# When an instance method object is created by retrieving a class method object from a class or instance, its __self__ attribute is the class itself, and its __func__ attribute is the function object underlying the class method. -c_func_obj = C.classmethod.__func__ +@expects(6) +def test_classmethod_call(): + # When an instance method object is created by retrieving a class method object from a class or instance, its __self__ attribute is the class itself, and its __func__ attribute is the function object underlying the class method. + c_func_obj = C.classmethod.__func__ + + # When an instance method object is derived from a class method object, the “class instance” stored in __self__ will actually be the class itself, so that calling either x.f(1) or C.f(1) is equivalent to calling f(C,1) where f is the underlying function. + c.classmethod(arg1, arg2) # $ func=C.classmethod arg1 arg2 + C.classmethod(arg1, arg2) # $ func=C.classmethod arg1 arg2 + c_func_obj(C, arg1, arg2) # $ MISSING: func=C.classmethod arg1 arg2 + + +@expects(5) +def test_staticmethod_call(): + # staticmethods does not have a __func__ attribute + try: + C.staticmethod.__func__ + except AttributeError: + print("OK") + + # When an instance method object is derived from a class method object, the “class instance” stored in __self__ will actually be the class itself, so that calling either x.f(1) or C.f(1) is equivalent to calling f(C,1) where f is the underlying function. + c.staticmethod(arg1, arg2) # $ func=C.staticmethod arg1 arg2 + C.staticmethod(arg1, arg2) # $ func=C.staticmethod arg1 arg2 -# When an instance method object is derived from a class method object, the “class instance” stored in __self__ will actually be the class itself, so that calling either x.f(1) or C.f(1) is equivalent to calling f(C,1) where f is the underlying function. -SINK(c.classmethod(SOURCE)) #$ flow="SOURCE -> c.classmethod(..)" -SINK(C.classmethod(SOURCE)) #$ flow="SOURCE -> C.classmethod(..)" -SINK(c_func_obj(C, SOURCE)) #$ MISSING: flow="SOURCE -> c_func_obj(..)" # Generator functions # A function or method which uses the yield statement (see section The yield statement) is called a generator function. Such a function, when called, always returns an iterator object which can be used to execute the body of the function: calling the iterator’s iterator.__next__() method will cause the function to execute until it provides a value using the yield statement. When the function executes a return statement or falls off the end, a StopIteration exception is raised and the iterator will have reached the end of the set of values to be returned. diff --git a/python/ql/test/experimental/dataflow/enclosing-callable/EnclosingCallable.expected b/python/ql/test/experimental/dataflow/enclosing-callable/EnclosingCallable.expected new file mode 100644 index 00000000000..a1e3de562f5 --- /dev/null +++ b/python/ql/test/experimental/dataflow/enclosing-callable/EnclosingCallable.expected @@ -0,0 +1,24 @@ +| file://:0:0:0:0 | Function generator_func | generator.py:1:20:1:21 | ControlFlowNode for xs | +| file://:0:0:0:0 | Function generator_func | generator.py:2:12:2:26 | ControlFlowNode for .0 | +| file://:0:0:0:0 | Function generator_func | generator.py:2:12:2:26 | ControlFlowNode for .0 | +| file://:0:0:0:0 | Function generator_func | generator.py:2:12:2:26 | ControlFlowNode for ListComp | +| file://:0:0:0:0 | Function generator_func | generator.py:2:13:2:13 | ControlFlowNode for Yield | +| file://:0:0:0:0 | Function generator_func | generator.py:2:13:2:13 | ControlFlowNode for x | +| file://:0:0:0:0 | Function generator_func | generator.py:2:19:2:19 | ControlFlowNode for x | +| file://:0:0:0:0 | Function generator_func | generator.py:2:24:2:25 | ControlFlowNode for xs | +| file://:0:0:0:0 | Module class_example | class_example.py:1:1:1:3 | ControlFlowNode for wat | +| file://:0:0:0:0 | Module class_example | class_example.py:1:7:1:7 | ControlFlowNode for IntegerLiteral | +| file://:0:0:0:0 | Module class_example | class_example.py:3:1:3:10 | ControlFlowNode for ClassExpr | +| file://:0:0:0:0 | Module class_example | class_example.py:3:7:3:9 | ControlFlowNode for Wat | +| file://:0:0:0:0 | Module class_example | class_example.py:4:5:4:7 | ControlFlowNode for wat | +| file://:0:0:0:0 | Module class_example | class_example.py:4:11:4:11 | ControlFlowNode for IntegerLiteral | +| file://:0:0:0:0 | Module class_example | class_example.py:5:5:5:9 | ControlFlowNode for print | +| file://:0:0:0:0 | Module class_example | class_example.py:5:5:5:26 | ControlFlowNode for print() | +| file://:0:0:0:0 | Module class_example | class_example.py:5:11:5:20 | ControlFlowNode for Str | +| file://:0:0:0:0 | Module class_example | class_example.py:5:23:5:25 | ControlFlowNode for wat | +| file://:0:0:0:0 | Module class_example | class_example.py:7:1:7:5 | ControlFlowNode for print | +| file://:0:0:0:0 | Module class_example | class_example.py:7:1:7:23 | ControlFlowNode for print() | +| file://:0:0:0:0 | Module class_example | class_example.py:7:7:7:17 | ControlFlowNode for Str | +| file://:0:0:0:0 | Module class_example | class_example.py:7:20:7:22 | ControlFlowNode for wat | +| file://:0:0:0:0 | Module generator | generator.py:1:1:1:23 | ControlFlowNode for FunctionExpr | +| file://:0:0:0:0 | Module generator | generator.py:1:5:1:18 | ControlFlowNode for generator_func | diff --git a/python/ql/test/experimental/dataflow/enclosing-callable/EnclosingCallable.ql b/python/ql/test/experimental/dataflow/enclosing-callable/EnclosingCallable.ql new file mode 100644 index 00000000000..1fa6a8ffc54 --- /dev/null +++ b/python/ql/test/experimental/dataflow/enclosing-callable/EnclosingCallable.ql @@ -0,0 +1,6 @@ +import python +import semmle.python.dataflow.new.DataFlow + +from DataFlow::CfgNode node +where exists(node.getLocation().getFile().getRelativePath()) +select node.getEnclosingCallable() as enclosingCallable, node diff --git a/python/ql/test/experimental/dataflow/enclosing-callable/class_example.py b/python/ql/test/experimental/dataflow/enclosing-callable/class_example.py new file mode 100644 index 00000000000..389b293d2bd --- /dev/null +++ b/python/ql/test/experimental/dataflow/enclosing-callable/class_example.py @@ -0,0 +1,7 @@ +wat = 1 + +class Wat: + wat = 2 + print("in class", wat) # prints 2 + +print("in module", wat) # prints 1 diff --git a/python/ql/test/experimental/dataflow/enclosing-callable/generator.py b/python/ql/test/experimental/dataflow/enclosing-callable/generator.py new file mode 100644 index 00000000000..56b6202abd7 --- /dev/null +++ b/python/ql/test/experimental/dataflow/enclosing-callable/generator.py @@ -0,0 +1,2 @@ +def generator_func(xs): + return [x for x in xs] diff --git a/python/ql/test/experimental/dataflow/fieldflow/test.py b/python/ql/test/experimental/dataflow/fieldflow/test.py index d8d4b5f6fe0..100ab6aac70 100644 --- a/python/ql/test/experimental/dataflow/fieldflow/test.py +++ b/python/ql/test/experimental/dataflow/fieldflow/test.py @@ -13,7 +13,17 @@ def is_source(x): return x == "source" or x == b"source" or x == 42 or x == 42.0 or x == 42j -def SINK(x): +def SINK(x, *, not_present_at_runtime=False): + # not_present_at_runtime supports use-cases where we want flow from data-flow layer + # (so we want to use SINK), but we end up in a siaution where it's not possible to + # actually get flow from a source at runtime. The only use-case is for the + # cross-talk tests, where our ability to use if-then-else is limited because doing + # so would make cfg-splitting kick in, and that would solve the problem trivially + # (by the splitting). + if not_present_at_runtime: + print("OK") + return + if is_source(x): print("OK") else: @@ -39,33 +49,55 @@ class MyObj(object): self.foo = foo def setFoo(obj, x): - SINK_F(obj.foo) obj.foo = x -@expects(2) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..) + +@expects(3) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..) def test_indirect_assign(): - myobj = MyObj("OK") + myobj = MyObj(NONSOURCE) + SINK_F(myobj.foo) setFoo(myobj, SOURCE) SINK(myobj.foo) # $ flow="SOURCE, l:-1 -> myobj.foo" + setFoo(myobj, NONSOURCE) + SINK_F(myobj.foo) # $ SPURIOUS: flow="SOURCE, l:-4 -> myobj.foo" + +@expects(3) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..) def test_indirect_assign_method(): - myobj = MyObj("OK") + myobj = MyObj(NONSOURCE) + SINK_F(myobj.foo) myobj.setFoo(SOURCE) SINK(myobj.foo) # $ flow="SOURCE, l:-1 -> myobj.foo" + myobj.setFoo(NONSOURCE) + SINK_F(myobj.foo) # $ SPURIOUS: flow="SOURCE, l:-4 -> myobj.foo" + +@expects(3) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..) +def test_indirect_assign_bound_method(): + myobj = MyObj(NONSOURCE) + SINK_F(myobj.foo) + + sf = myobj.setFoo + + sf(SOURCE) + SINK(myobj.foo) # $ MISSING: flow="SOURCE, l:-1 -> myobj.foo" + + sf(NONSOURCE) + SINK_F(myobj.foo) + + +@expects(3) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..) def test_direct_assign(): myobj = MyObj(NONSOURCE) + SINK_F(myobj.foo) + myobj.foo = SOURCE SINK(myobj.foo) # $ flow="SOURCE, l:-1 -> myobj.foo" - -def test_direct_assign_overwrite(): - myobj = MyObj(NONSOURCE) - myobj.foo = SOURCE myobj.foo = NONSOURCE SINK_F(myobj.foo) @@ -160,6 +192,164 @@ def test_nested_obj_method(): a.getObj().foo = x SINK(a.obj.foo) # $ flow="SOURCE, l:-3 -> a.obj.foo" + +# ------------------------------------------------------------------------------ +# Field access on compound arguments +# ------------------------------------------------------------------------------ + +# TODO: Add support for this, see https://github.com/github/codeql/pull/10444 + +@expects(5) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..) +def test_field_on_compound_arg(cond_true=True, cond_false=False): + class Ex: + def __init__(self): + self.attr = None + + def set_attr(obj): + obj.attr = SOURCE + + x = Ex() + y = Ex() + set_attr(x if cond_true else y) + SINK(x.attr) # $ MISSING: flow + + x = Ex() + y = Ex() + set_attr(x if cond_false else y) + SINK(y.attr) # $ MISSING: flow + + x = Ex() + y = Ex() + z = Ex() + set_attr(x if cond_false else (y if cond_true else z)) + SINK_F(x.attr) # $ MISSING: flow + SINK(y.attr) # $ MISSING: flow + SINK_F(z.attr) # $ MISSING: flow + +# ------------------------------------------------------------------------------ +# Crosstalk test -- using different function based on conditional +# ------------------------------------------------------------------------------ +# NOTE: These tests use `SINK(objy.y, not_present_at_runtime=True)` since it's not +# possible to use if-then-else statements, since that would make cfg-splitting kick in, +# and that would solve the problem trivially (by the splitting). + +class CrosstalkTestX: + def __init__(self): + self.x = None + self.y = None + + def setx(self, value): + self.x = value + + def setvalue(self, value): + self.x = value + + +class CrosstalkTestY: + def __init__(self): + self.x = None + self.y = None + + def sety(self ,value): + self.y = value + + def setvalue(self, value): + self.y = value + + +@expects(8) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..) +def test_no_crosstalk_reference(cond=True): + objx = CrosstalkTestX() + SINK_F(objx.x) + SINK_F(objx.y) + + objy = CrosstalkTestY() + SINK_F(objy.x) + SINK_F(objy.y) + + if cond: + objx.setvalue(SOURCE) + else: + objy.setvalue(SOURCE) + + SINK(objx.x) # $ flow="SOURCE, l:-4 -> objx.x" + SINK_F(objx.y) + SINK_F(objy.x) + SINK(objy.y, not_present_at_runtime=True) # $ flow="SOURCE, l:-5 -> objy.y" + + +@expects(8) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..) +def test_potential_crosstalk_different_name(cond=True): + objx = CrosstalkTestX() + SINK_F(objx.x) + SINK_F(objx.y) + + objy = CrosstalkTestY() + SINK_F(objy.x) + SINK_F(objy.y) + + if cond: + func = objx.setx + else: + func = objy.sety + + func(SOURCE) + + SINK(objx.x) # $ MISSING: flow="SOURCE, l:-2 -> objx.x" + SINK_F(objx.y) + SINK_F(objy.x) + SINK(objy.y, not_present_at_runtime=True) # $ MISSING: flow="SOURCE, l:-5 -> objy.y" + + +@expects(8) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..) +def test_potential_crosstalk_same_name(cond=True): + objx = CrosstalkTestX() + SINK_F(objx.x) + SINK_F(objx.y) + + objy = CrosstalkTestY() + SINK_F(objy.x) + SINK_F(objy.y) + + if cond: + func = objx.setvalue + else: + func = objy.setvalue + + func(SOURCE) + + SINK(objx.x) # $ MISSING: flow="SOURCE, l:-2 -> objx.x" + SINK_F(objx.y) + SINK_F(objy.x) + SINK(objy.y, not_present_at_runtime=True) # $ MISSING: flow="SOURCE, l:-5 -> objy.y" + + +@expects(10) # $ unresolved_call=expects(..) unresolved_call=expects(..)(..) +def test_potential_crosstalk_same_name_object_reference(cond=True): + objx = CrosstalkTestX() + SINK_F(objx.x) + SINK_F(objx.y) + + objy = CrosstalkTestY() + SINK_F(objy.x) + SINK_F(objy.y) + + if cond: + obj = objx + else: + obj = objy + + obj.setvalue(SOURCE) + + SINK(objx.x) # $ MISSING: flow="SOURCE, l:-2 -> objx.x" + SINK_F(objx.y) + SINK_F(objy.x) + SINK(objy.y, not_present_at_runtime=True) # $ MISSING: flow="SOURCE, l:-5 -> objy.y" + + SINK(obj.x) # $ flow="SOURCE, l:-7 -> obj.x" + SINK(obj.y, not_present_at_runtime=True) # $ flow="SOURCE, l:-8 -> obj.y" + + # ------------------------------------------------------------------------------ # Global scope # ------------------------------------------------------------------------------ diff --git a/python/ql/test/experimental/dataflow/testConfig.qll b/python/ql/test/experimental/dataflow/testConfig.qll index 03815e2f7f9..addbeefeebf 100644 --- a/python/ql/test/experimental/dataflow/testConfig.qll +++ b/python/ql/test/experimental/dataflow/testConfig.qll @@ -38,9 +38,10 @@ class TestConfiguration extends DataFlow::Configuration { } override predicate isSink(DataFlow::Node node) { - exists(CallNode call | - call.getFunction().(NameNode).getId() in ["SINK", "SINK_F"] and - node.(DataFlow::CfgNode).getNode() = call.getAnArg() + exists(DataFlow::CallCfgNode call | + call.getFunction().asCfgNode().(NameNode).getId() in ["SINK", "SINK_F"] and + (node = call.getArg(_) or node = call.getArgByName(_)) and + not node = call.getArgByName("not_present_at_runtime") ) } diff --git a/python/ql/test/experimental/dataflow/validTest.py b/python/ql/test/experimental/dataflow/validTest.py index 86336af05ba..edfe685c266 100644 --- a/python/ql/test/experimental/dataflow/validTest.py +++ b/python/ql/test/experimental/dataflow/validTest.py @@ -55,6 +55,7 @@ if __name__ == "__main__": check_tests_valid("coverage.classes") check_tests_valid("coverage.test") check_tests_valid("coverage.argumentPassing") + check_tests_valid("coverage.datamodel") check_tests_valid("variable-capture.in") check_tests_valid("variable-capture.nonlocal") check_tests_valid("variable-capture.dict") diff --git a/python/ql/test/experimental/import-resolution/importflow.ql b/python/ql/test/experimental/import-resolution/importflow.ql index 6160560a3ee..e6e51afa963 100644 --- a/python/ql/test/experimental/import-resolution/importflow.ql +++ b/python/ql/test/experimental/import-resolution/importflow.ql @@ -93,26 +93,29 @@ class ResolutionTest extends InlineExpectationsTest { } } -class ResolutionTest3 extends InlineExpectationsTest { - ResolutionTest3() { this = "ResolutionTest3" } +private string getTagForVersion(int version) { + result = "prints" + version and + version = major_version() +} - override string getARelevantTag() { result = "prints3" and major_version() = 3 } +class VersionSpecificResolutionTest extends InlineExpectationsTest { + VersionSpecificResolutionTest() { this = "VersionSpecificResolutionTest" } + + override string getARelevantTag() { result = getTagForVersion(_) } override predicate hasActualResult(Location location, string element, string tag, string value) { ( exists(DataFlow::PathNode source, DataFlow::PathNode sink, ImportConfiguration config | config.hasFlowPath(source, sink) and - sink.getNode().(VersionGuardedNode).getVersion() = 3 and - tag = "prints3" and + tag = getTagForVersion(sink.getNode().(VersionGuardedNode).getVersion()) and location = sink.getNode().getLocation() and value = source.getNode().(SourceString).getContents() and element = sink.getNode().toString() ) or exists(ModuleRef ref | - ref.(VersionGuardedNode).getVersion() = 3 and ref instanceof CheckArgument and - tag = "prints3" and + tag = getTagForVersion(ref.(VersionGuardedNode).getVersion()) and location = ref.getLocation() and value = "\"\"" and element = ref.toString() diff --git a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/InlineCallGraphTest.expected b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/InlineCallGraphTest.expected new file mode 100644 index 00000000000..c847f9a8aa2 --- /dev/null +++ b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/InlineCallGraphTest.expected @@ -0,0 +1,5 @@ +failures +debug_callableNotUnique +pointsTo_found_typeTracker_notFound +| example.py:22:1:22:16 | ControlFlowNode for explicit_afunc() | explicit_afunc | +typeTracker_found_pointsTo_notFound diff --git a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/InlineCallGraphTest.qlref b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/InlineCallGraphTest.qlref new file mode 100644 index 00000000000..25117a4582b --- /dev/null +++ b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/InlineCallGraphTest.qlref @@ -0,0 +1 @@ +../CallGraph/InlineCallGraphTest.ql diff --git a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/PointsTo.expected b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/PointsTo.expected deleted file mode 100644 index 349d99b46d8..00000000000 --- a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/PointsTo.expected +++ /dev/null @@ -1,6 +0,0 @@ -debug_missingAnnotationForCallable -debug_nonUniqueAnnotationForCallable -debug_missingAnnotationForCall -expectedCallEdgeNotFound -| example.py:19:1:19:7 | afunc() | foo/bar/a.py:2:1:2:12 | Function afunc | -unexpectedCallEdgeFound diff --git a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/PointsTo.qlref b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/PointsTo.qlref deleted file mode 100644 index da8a0d1631a..00000000000 --- a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/PointsTo.qlref +++ /dev/null @@ -1 +0,0 @@ -../CallGraph/PointsTo.ql diff --git a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/Relative.expected b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/Relative.expected deleted file mode 100644 index 38aab0b5888..00000000000 --- a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/Relative.expected +++ /dev/null @@ -1,6 +0,0 @@ -debug_missingAnnotationForCallable -debug_nonUniqueAnnotationForCallable -debug_missingAnnotationForCall -pointsTo_found_typeTracker_notFound -| example.py:22:1:22:16 | explicit_afunc() | foo_explicit/bar/a.py:2:1:2:21 | Function explicit_afunc | -pointsTo_notFound_typeTracker_found diff --git a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/Relative.qlref b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/Relative.qlref deleted file mode 100644 index 2ffa6c10d51..00000000000 --- a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/Relative.qlref +++ /dev/null @@ -1 +0,0 @@ -../CallGraph/Relative.ql diff --git a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/TypeTracker.expected b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/TypeTracker.expected deleted file mode 100644 index 5283683a6a5..00000000000 --- a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/TypeTracker.expected +++ /dev/null @@ -1,7 +0,0 @@ -debug_missingAnnotationForCallable -debug_nonUniqueAnnotationForCallable -debug_missingAnnotationForCall -expectedCallEdgeNotFound -| example.py:19:1:19:7 | afunc() | foo/bar/a.py:2:1:2:12 | Function afunc | -| example.py:22:1:22:16 | explicit_afunc() | foo_explicit/bar/a.py:2:1:2:21 | Function explicit_afunc | -unexpectedCallEdgeFound diff --git a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/TypeTracker.qlref b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/TypeTracker.qlref deleted file mode 100644 index 60c029a5510..00000000000 --- a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/TypeTracker.qlref +++ /dev/null @@ -1 +0,0 @@ -../CallGraph/TypeTracker.ql diff --git a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/example.py b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/example.py index 35d56620af7..75ad8a9db11 100644 --- a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/example.py +++ b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/example.py @@ -5,18 +5,18 @@ This is not included in the standard `CallGraph/code` folder, since we're testin understanding import work properly, so it's better to have a clean test setup that is obviously correct (the other one isn't in regards to imports). -Technically this is part of PEP 420 -- Implicit Namespace Packages, but does use the +Technically this is part of PEP 420 -- Implicit Namespace Packages, but does not use the *real* namespace package feature of allowing source code for a single package to reside in multiple places. +Maybe this should have been an import resolution test, and not a call-graph test ¯\_(ツ)_/¯ + Since PEP 420 was accepted in Python 3, this test is Python 3 only. """ from foo.bar.a import afunc from foo_explicit.bar.a import explicit_afunc -# calls:afunc -afunc() +afunc() # $ MISSING: pt,tt=afunc -# calls:explicit_afunc -explicit_afunc() +explicit_afunc() # $ pt=explicit_afunc MISSING: tt=explicit_afunc diff --git a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/foo/bar/a.py b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/foo/bar/a.py index a294b33191e..bc639f6c537 100644 --- a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/foo/bar/a.py +++ b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/foo/bar/a.py @@ -1,4 +1,3 @@ -# name:afunc def afunc(): print("afunc called") return 1 diff --git a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/foo_explicit/bar/a.py b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/foo_explicit/bar/a.py index 616c3fddca1..c84d63cfce2 100644 --- a/python/ql/test/experimental/library-tests/CallGraph-implicit-init/foo_explicit/bar/a.py +++ b/python/ql/test/experimental/library-tests/CallGraph-implicit-init/foo_explicit/bar/a.py @@ -1,4 +1,3 @@ -# name:explicit_afunc def explicit_afunc(): print("explicit_afunc called") return 1 diff --git a/python/ql/test/experimental/library-tests/CallGraph-xfail/PointsTo.expected b/python/ql/test/experimental/library-tests/CallGraph-xfail/PointsTo.expected deleted file mode 100644 index 680cee0e8b2..00000000000 --- a/python/ql/test/experimental/library-tests/CallGraph-xfail/PointsTo.expected +++ /dev/null @@ -1,18 +0,0 @@ -debug_missingAnnotationForCallable -| annotation_xfail.py:10:1:10:24 | callable_not_annotated() | This call is annotated with 'callable_not_annotated', but no callable with that annotation was extracted. Please fix. | -debug_nonUniqueAnnotationForCallable -| annotation_xfail.py:13:1:13:17 | Function non_unique | Multiple callables are annotated with 'non_unique'. Please fix. | -| annotation_xfail.py:17:1:17:26 | Function too_much_copy_paste | Multiple callables are annotated with 'non_unique'. Please fix. | -debug_missingAnnotationForCall -| annotation_xfail.py:2:1:2:24 | Function no_annotated_call | This callable is annotated with 'no_annotated_call', but no call with that annotation was extracted. Please fix. | -expectedCallEdgeNotFound -| call_edge_xfail.py:36:1:36:11 | xfail_foo() | call_edge_xfail.py:8:1:8:16 | Function xfail_bar | -| call_edge_xfail.py:39:1:39:11 | xfail_baz() | call_edge_xfail.py:8:1:8:16 | Function xfail_bar | -unexpectedCallEdgeFound -| call_edge_xfail.py:29:1:29:6 | func() | call_edge_xfail.py:4:1:4:16 | Function xfail_foo | Call resolved to the callable named 'xfail_foo' but was not annotated as such | -| call_edge_xfail.py:29:1:29:6 | func() | call_edge_xfail.py:8:1:8:16 | Function xfail_bar | Call resolved to the callable named 'xfail_bar' but was not annotated as such | -| call_edge_xfail.py:30:1:30:11 | xfail_foo() | call_edge_xfail.py:4:1:4:16 | Function xfail_foo | Call resolved to the callable named 'xfail_foo' but was not annotated as such | -| call_edge_xfail.py:31:1:31:14 | xfail_lambda() | call_edge_xfail.py:15:16:15:44 | Function lambda | Call resolved to the callable named 'xfail_lambda' but was not annotated as such | -| call_edge_xfail.py:36:1:36:11 | xfail_foo() | call_edge_xfail.py:4:1:4:16 | Function xfail_foo | Call resolved to the callable named 'xfail_foo' but was not annotated as such | -| call_edge_xfail.py:39:1:39:11 | xfail_baz() | call_edge_xfail.py:11:1:11:16 | Function xfail_baz | Annotated call resolved to unannotated callable | -| call_edge_xfail.py:43:1:43:6 | func() | call_edge_xfail.py:8:1:8:16 | Function xfail_bar | Call resolved to the callable named 'xfail_bar' but was not annotated as such | diff --git a/python/ql/test/experimental/library-tests/CallGraph-xfail/PointsTo.qlref b/python/ql/test/experimental/library-tests/CallGraph-xfail/PointsTo.qlref deleted file mode 100644 index da8a0d1631a..00000000000 --- a/python/ql/test/experimental/library-tests/CallGraph-xfail/PointsTo.qlref +++ /dev/null @@ -1 +0,0 @@ -../CallGraph/PointsTo.ql diff --git a/python/ql/test/experimental/library-tests/CallGraph-xfail/README.md b/python/ql/test/experimental/library-tests/CallGraph-xfail/README.md deleted file mode 100644 index 39021bce2a1..00000000000 --- a/python/ql/test/experimental/library-tests/CallGraph-xfail/README.md +++ /dev/null @@ -1 +0,0 @@ -Test that show our failure handling in [CallGraph](../CallGraph/) works as expected. diff --git a/python/ql/test/experimental/library-tests/CallGraph-xfail/annotation_xfail.py b/python/ql/test/experimental/library-tests/CallGraph-xfail/annotation_xfail.py deleted file mode 100644 index f8dbf88aa02..00000000000 --- a/python/ql/test/experimental/library-tests/CallGraph-xfail/annotation_xfail.py +++ /dev/null @@ -1,21 +0,0 @@ -# name:no_annotated_call -def no_annotated_call(): - pass - -def callable_not_annotated(): - pass - -no_annotated_call() -# calls:callable_not_annotated -callable_not_annotated() - -# name:non_unique -def non_unique(): - pass - -# name:non_unique -def too_much_copy_paste(): - pass - -# calls:non_unique -non_unique() diff --git a/python/ql/test/experimental/library-tests/CallGraph-xfail/call_edge_xfail.py b/python/ql/test/experimental/library-tests/CallGraph-xfail/call_edge_xfail.py deleted file mode 100644 index e72e02e376b..00000000000 --- a/python/ql/test/experimental/library-tests/CallGraph-xfail/call_edge_xfail.py +++ /dev/null @@ -1,43 +0,0 @@ -import sys - -# name:xfail_foo -def xfail_foo(): - print('xfail_foo') - -# name:xfail_bar -def xfail_bar(): - print('xfail_bar') - -def xfail_baz(): - print('xfail_baz') - -# name:xfail_lambda -xfail_lambda = lambda: print('xfail_lambda') - -if len(sys.argv) >= 2 and not sys.argv[1] in ['0', 'False', 'false']: - func = xfail_foo -else: - func = xfail_bar - -# Correct usage to suppress bad annotation errors -# calls:xfail_foo calls:xfail_bar -func() -# calls:xfail_lambda -xfail_lambda() - -# These are not annotated, and will give rise to unexpectedCallEdgeFound -func() -xfail_foo() -xfail_lambda() - -# These are annotated wrongly, and will give rise to unexpectedCallEdgeFound - -# calls:xfail_bar -xfail_foo() - -# calls:xfail_bar -xfail_baz() - -# The annotation is incomplete (does not include the call to xfail_bar) -# calls:xfail_foo -func() diff --git a/python/ql/test/experimental/library-tests/CallGraph/CallGraphTest.qll b/python/ql/test/experimental/library-tests/CallGraph/CallGraphTest.qll deleted file mode 100644 index 0f8b3162980..00000000000 --- a/python/ql/test/experimental/library-tests/CallGraph/CallGraphTest.qll +++ /dev/null @@ -1,147 +0,0 @@ -import python - -/** Gets the comment on the line above `ast` */ -Comment commentFor(AstNode ast) { - exists(int line | line = ast.getLocation().getStartLine() - 1 | - result - .getLocation() - .hasLocationInfo(ast.getLocation().getFile().getAbsolutePath(), line, _, line, _) - ) -} - -/** Gets the value from `tag:value` in the comment for `ast` */ -string getAnnotation(AstNode ast, string tag) { - exists(Comment comment, string match, string theRegex | - theRegex = "([\\w]+):([\\w.]+)" and - comment = commentFor(ast) and - match = comment.getText().regexpFind(theRegex, _, _) and - tag = match.regexpCapture(theRegex, 1) and - result = match.regexpCapture(theRegex, 2) - ) -} - -/** Gets a callable annotated with `name:name` */ -Function annotatedCallable(string name) { name = getAnnotation(result, "name") } - -/** Gets a call annotated with `calls:name` */ -Call annotatedCall(string name) { name = getAnnotation(result, "calls") } - -predicate missingAnnotationForCallable(string name, Call call) { - call = annotatedCall(name) and - not exists(annotatedCallable(name)) -} - -predicate nonUniqueAnnotationForCallable(string name, Function callable) { - strictcount(annotatedCallable(name)) > 1 and - callable = annotatedCallable(name) -} - -predicate missingAnnotationForCall(string name, Function callable) { - not exists(annotatedCall(name)) and - callable = annotatedCallable(name) -} - -/** There is an obvious problem with the annotation `name` */ -predicate nameInErrorState(string name) { - missingAnnotationForCallable(name, _) - or - nonUniqueAnnotationForCallable(name, _) - or - missingAnnotationForCall(name, _) -} - -/** Source code has annotation with `name` showing that `call` will call `callable` */ -predicate annotatedCallEdge(string name, Call call, Function callable) { - not nameInErrorState(name) and - call = annotatedCall(name) and - callable = annotatedCallable(name) -} - -// ------------------------- Annotation debug query predicates ------------------------- -query predicate debug_missingAnnotationForCallable(Call call, string message) { - exists(string name | - message = - "This call is annotated with '" + name + - "', but no callable with that annotation was extracted. Please fix." and - missingAnnotationForCallable(name, call) - ) -} - -query predicate debug_nonUniqueAnnotationForCallable(Function callable, string message) { - exists(string name | - message = "Multiple callables are annotated with '" + name + "'. Please fix." and - nonUniqueAnnotationForCallable(name, callable) - ) -} - -query predicate debug_missingAnnotationForCall(Function callable, string message) { - exists(string name | - message = - "This callable is annotated with '" + name + - "', but no call with that annotation was extracted. Please fix." and - missingAnnotationForCall(name, callable) - ) -} - -// ------------------------- Call Graph resolution ------------------------- -private newtype TCallGraphResolver = - TPointsToResolver() or - TTypeTrackerResolver() - -/** A method of call graph resolution */ -abstract class CallGraphResolver extends TCallGraphResolver { - abstract predicate callEdge(Call call, Function callable); - - /** - * Holds if annotations show that `call` will call `callable`, - * but our call graph resolver was not able to figure that out - */ - predicate expectedCallEdgeNotFound(Call call, Function callable) { - annotatedCallEdge(_, call, callable) and - not this.callEdge(call, callable) - } - - /** - * Holds if there are no annotations that show that `call` will call `callable` (where at least one of these are annotated), - * but the call graph resolver claims that `call` will call `callable` - */ - predicate unexpectedCallEdgeFound(Call call, Function callable, string message) { - this.callEdge(call, callable) and - not annotatedCallEdge(_, call, callable) and - ( - exists(string name | - message = "Call resolved to the callable named '" + name + "' but was not annotated as such" and - callable = annotatedCallable(name) and - not nameInErrorState(name) - ) - or - exists(string name | - message = "Annotated call resolved to unannotated callable" and - call = annotatedCall(name) and - not nameInErrorState(name) and - not exists( | callable = annotatedCallable(_)) - ) - ) - } - - string toString() { result = "CallGraphResolver" } -} - -/** A call graph resolver based on the existing points-to analysis */ -class PointsToResolver extends CallGraphResolver, TPointsToResolver { - override predicate callEdge(Call call, Function callable) { - exists(PythonFunctionValue funcValue | - funcValue.getScope() = callable and - call = funcValue.getACall().getNode() - ) - } - - override string toString() { result = "PointsToResolver" } -} - -/** A call graph resolved based on Type Trackers */ -class TypeTrackerResolver extends CallGraphResolver, TTypeTrackerResolver { - override predicate callEdge(Call call, Function callable) { none() } - - override string toString() { result = "TypeTrackerResolver" } -} diff --git a/python/ql/test/experimental/library-tests/CallGraph/InlineCallGraphTest.expected b/python/ql/test/experimental/library-tests/CallGraph/InlineCallGraphTest.expected new file mode 100644 index 00000000000..02d08ac4c81 --- /dev/null +++ b/python/ql/test/experimental/library-tests/CallGraph/InlineCallGraphTest.expected @@ -0,0 +1,22 @@ +failures +debug_callableNotUnique +| code/class_advanced.py:18:5:18:18 | Function arg | Qualified function name 'B.arg' is not unique. Please fix. | +| code/class_advanced.py:23:5:23:25 | Function arg | Qualified function name 'B.arg' is not unique. Please fix. | +pointsTo_found_typeTracker_notFound +| code/class_simple.py:24:1:24:15 | ControlFlowNode for Attribute() | A.some_method | +| code/class_simple.py:25:1:25:21 | ControlFlowNode for Attribute() | A.some_staticmethod | +| code/class_simple.py:26:1:26:20 | ControlFlowNode for Attribute() | A.some_classmethod | +| code/class_simple.py:28:1:28:21 | ControlFlowNode for Attribute() | A.some_staticmethod | +| code/class_simple.py:29:1:29:20 | ControlFlowNode for Attribute() | A.some_classmethod | +| code/runtime_decision.py:18:1:18:6 | ControlFlowNode for func() | rd_bar | +| code/runtime_decision.py:18:1:18:6 | ControlFlowNode for func() | rd_foo | +| code/runtime_decision.py:26:1:26:7 | ControlFlowNode for func2() | rd_bar | +| code/runtime_decision.py:26:1:26:7 | ControlFlowNode for func2() | rd_foo | +| code/simple.py:15:1:15:5 | ControlFlowNode for foo() | foo | +| code/simple.py:16:1:16:14 | ControlFlowNode for indirect_foo() | foo | +| code/simple.py:17:1:17:5 | ControlFlowNode for bar() | bar | +| code/simple.py:18:1:18:5 | ControlFlowNode for lam() | lambda[simple.py:12:7] | +| code/underscore_prefix_func_name.py:18:5:18:19 | ControlFlowNode for some_function() | some_function | +| code/underscore_prefix_func_name.py:21:5:21:19 | ControlFlowNode for some_function() | some_function | +| code/underscore_prefix_func_name.py:24:1:24:21 | ControlFlowNode for _works_since_called() | _works_since_called | +typeTracker_found_pointsTo_notFound diff --git a/python/ql/test/experimental/library-tests/CallGraph/InlineCallGraphTest.ql b/python/ql/test/experimental/library-tests/CallGraph/InlineCallGraphTest.ql new file mode 100644 index 00000000000..50ad10bd191 --- /dev/null +++ b/python/ql/test/experimental/library-tests/CallGraph/InlineCallGraphTest.ql @@ -0,0 +1,68 @@ +import python +import TestUtilities.InlineExpectationsTest + +/** Holds when `call` is resolved to `callable` using points-to based call-graph. */ +predicate pointsToCallEdge(CallNode call, Function callable) { + exists(PythonFunctionValue funcValue | + funcValue.getScope() = callable and + call = funcValue.getACall() + ) +} + +/** Holds when `call` is resolved to `callable` using type-tracking based call-graph. */ +predicate typeTrackerCallEdge(CallNode call, Function callable) { none() } + +class CallGraphTest extends InlineExpectationsTest { + CallGraphTest() { this = "CallGraphTest" } + + override string getARelevantTag() { result in ["pt", "tt"] } + + override predicate hasActualResult(Location location, string element, string tag, string value) { + exists(location.getFile().getRelativePath()) and + exists(CallNode call, Function target | + tag = "tt" and + typeTrackerCallEdge(call, target) + or + tag = "pt" and + pointsToCallEdge(call, target) + | + location = call.getLocation() and + element = call.toString() and + value = betterQualName(target) + ) + } +} + +bindingset[func] +string betterQualName(Function func) { + // note: `target.getQualifiedName` for Lambdas is just "lambda", so is not very useful :| + not func.isLambda() and + result = func.getQualifiedName() + or + func.isLambda() and + result = + "lambda[" + func.getLocation().getFile().getShortName() + ":" + + func.getLocation().getStartLine() + ":" + func.getLocation().getStartColumn() + "]" +} + +query predicate debug_callableNotUnique(Function callable, string message) { + exists(Function f | f != callable and f.getQualifiedName() = callable.getQualifiedName()) and + message = + "Qualified function name '" + callable.getQualifiedName() + "' is not unique. Please fix." +} + +query predicate pointsTo_found_typeTracker_notFound(CallNode call, string qualname) { + exists(Function target | + pointsToCallEdge(call, target) and + not typeTrackerCallEdge(call, target) and + qualname = betterQualName(target) + ) +} + +query predicate typeTracker_found_pointsTo_notFound(CallNode call, string qualname) { + exists(Function target | + not pointsToCallEdge(call, target) and + typeTrackerCallEdge(call, target) and + qualname = betterQualName(target) + ) +} diff --git a/python/ql/test/experimental/library-tests/CallGraph/PointsTo.expected b/python/ql/test/experimental/library-tests/CallGraph/PointsTo.expected deleted file mode 100644 index e7db0fde98c..00000000000 --- a/python/ql/test/experimental/library-tests/CallGraph/PointsTo.expected +++ /dev/null @@ -1,6 +0,0 @@ -debug_missingAnnotationForCallable -debug_nonUniqueAnnotationForCallable -debug_missingAnnotationForCall -expectedCallEdgeNotFound -| code/underscore_prefix_func_name.py:16:5:16:19 | some_function() | code/underscore_prefix_func_name.py:10:1:10:20 | Function some_function | -unexpectedCallEdgeFound diff --git a/python/ql/test/experimental/library-tests/CallGraph/PointsTo.ql b/python/ql/test/experimental/library-tests/CallGraph/PointsTo.ql deleted file mode 100644 index f86842f2fe4..00000000000 --- a/python/ql/test/experimental/library-tests/CallGraph/PointsTo.ql +++ /dev/null @@ -1,10 +0,0 @@ -import python -import CallGraphTest - -query predicate expectedCallEdgeNotFound(Call call, Function callable) { - any(PointsToResolver r).expectedCallEdgeNotFound(call, callable) -} - -query predicate unexpectedCallEdgeFound(Call call, Function callable, string message) { - any(PointsToResolver r).unexpectedCallEdgeFound(call, callable, message) -} diff --git a/python/ql/test/experimental/library-tests/CallGraph/README.md b/python/ql/test/experimental/library-tests/CallGraph/README.md deleted file mode 100644 index 0fbf6bdac9d..00000000000 --- a/python/ql/test/experimental/library-tests/CallGraph/README.md +++ /dev/null @@ -1,38 +0,0 @@ -# Call Graph Tests - -A small testing framework for our call graph resolution. It relies on manual annotation of calls and callables, **and will only include output if something is wrong**. For example, if we are not able to resolve that the `foo()` call will call the `foo` function, that should give an alert. - -```py -# name:foo -def foo(): - pass -# calls:foo -foo() -``` - -This is greatly inspired by [`CallGraphs/AnnotatedTest`](https://github.com/github/codeql/blob/696d19cb1440b6f6a75c6a2c1319e18860ceb436/javascript/ql/test/library-tests/CallGraphs/AnnotatedTest/Test.ql) from JavaScript. - -IMPORTANT: Names used in annotations are not scoped, so must be unique globally. (this is a bit annoying, but makes things simple). If multiple identical annotations are used, an error message will be output. - -Important files: - -- `CallGraphTest.qll`: main code to find annotated calls/callables and setting everything up. -- `PointsTo.ql`: results when using points-to for call graph resolution. -- `TypeTracker.ql`: results when using TypeTracking for call graph resolution. -- `Relative.ql`: differences between using points-to and TypeTracking. -- `code/` contains the actual Python code we test against (included by `test.py`). - -All queries will also execute some `debug_*` predicates. These highlight any obvious problems with the annotation setup, and so there should never be any results committed. To show that this works as expected, see the [CallGraph-xfail](../CallGraph-xfail/) which uses symlinked versions of the files in this directory (can't include as subdir, so has to be a sibling). - -## `options` file - -If the value for `--max-import-depth` is set so that `import random` will extract `random.py` from the standard library, BUT NO transitive imports are extracted, then points-to analysis will fail to handle the following snippet. - -```py -import random -if random.random() < 0.5: - func = foo -else: - func = bar -func() -``` diff --git a/python/ql/test/experimental/library-tests/CallGraph/Relative.expected b/python/ql/test/experimental/library-tests/CallGraph/Relative.expected deleted file mode 100644 index 9882dda21bf..00000000000 --- a/python/ql/test/experimental/library-tests/CallGraph/Relative.expected +++ /dev/null @@ -1,20 +0,0 @@ -debug_missingAnnotationForCallable -debug_nonUniqueAnnotationForCallable -debug_missingAnnotationForCall -pointsTo_found_typeTracker_notFound -| code/class_simple.py:28:1:28:15 | Attribute() | code/class_simple.py:8:5:8:26 | Function some_method | -| code/class_simple.py:30:1:30:21 | Attribute() | code/class_simple.py:13:5:13:28 | Function some_staticmethod | -| code/class_simple.py:32:1:32:20 | Attribute() | code/class_simple.py:18:5:18:30 | Function some_classmethod | -| code/class_simple.py:35:1:35:21 | Attribute() | code/class_simple.py:13:5:13:28 | Function some_staticmethod | -| code/class_simple.py:37:1:37:20 | Attribute() | code/class_simple.py:18:5:18:30 | Function some_classmethod | -| code/runtime_decision.py:21:1:21:6 | func() | code/runtime_decision.py:8:1:8:13 | Function rd_foo | -| code/runtime_decision.py:21:1:21:6 | func() | code/runtime_decision.py:12:1:12:13 | Function rd_bar | -| code/runtime_decision.py:30:1:30:7 | func2() | code/runtime_decision.py:8:1:8:13 | Function rd_foo | -| code/runtime_decision.py:30:1:30:7 | func2() | code/runtime_decision.py:12:1:12:13 | Function rd_bar | -| code/simple.py:19:1:19:5 | foo() | code/simple.py:2:1:2:10 | Function foo | -| code/simple.py:21:1:21:14 | indirect_foo() | code/simple.py:2:1:2:10 | Function foo | -| code/simple.py:23:1:23:5 | bar() | code/simple.py:10:1:10:10 | Function bar | -| code/simple.py:25:1:25:5 | lam() | code/simple.py:15:7:15:36 | Function lambda | -| code/underscore_prefix_func_name.py:21:5:21:19 | some_function() | code/underscore_prefix_func_name.py:10:1:10:20 | Function some_function | -| code/underscore_prefix_func_name.py:25:5:25:19 | some_function() | code/underscore_prefix_func_name.py:10:1:10:20 | Function some_function | -pointsTo_notFound_typeTracker_found diff --git a/python/ql/test/experimental/library-tests/CallGraph/Relative.ql b/python/ql/test/experimental/library-tests/CallGraph/Relative.ql deleted file mode 100644 index f62e4d21cbd..00000000000 --- a/python/ql/test/experimental/library-tests/CallGraph/Relative.ql +++ /dev/null @@ -1,14 +0,0 @@ -import python -import CallGraphTest - -query predicate pointsTo_found_typeTracker_notFound(Call call, Function callable) { - annotatedCallEdge(_, call, callable) and - any(PointsToResolver r).callEdge(call, callable) and - not any(TypeTrackerResolver r).callEdge(call, callable) -} - -query predicate pointsTo_notFound_typeTracker_found(Call call, Function callable) { - annotatedCallEdge(_, call, callable) and - not any(PointsToResolver r).callEdge(call, callable) and - any(TypeTrackerResolver r).callEdge(call, callable) -} diff --git a/python/ql/test/experimental/library-tests/CallGraph/TypeTracker.expected b/python/ql/test/experimental/library-tests/CallGraph/TypeTracker.expected deleted file mode 100644 index 5fc5376ca25..00000000000 --- a/python/ql/test/experimental/library-tests/CallGraph/TypeTracker.expected +++ /dev/null @@ -1,21 +0,0 @@ -debug_missingAnnotationForCallable -debug_nonUniqueAnnotationForCallable -debug_missingAnnotationForCall -expectedCallEdgeNotFound -| code/class_simple.py:28:1:28:15 | Attribute() | code/class_simple.py:8:5:8:26 | Function some_method | -| code/class_simple.py:30:1:30:21 | Attribute() | code/class_simple.py:13:5:13:28 | Function some_staticmethod | -| code/class_simple.py:32:1:32:20 | Attribute() | code/class_simple.py:18:5:18:30 | Function some_classmethod | -| code/class_simple.py:35:1:35:21 | Attribute() | code/class_simple.py:13:5:13:28 | Function some_staticmethod | -| code/class_simple.py:37:1:37:20 | Attribute() | code/class_simple.py:18:5:18:30 | Function some_classmethod | -| code/runtime_decision.py:21:1:21:6 | func() | code/runtime_decision.py:8:1:8:13 | Function rd_foo | -| code/runtime_decision.py:21:1:21:6 | func() | code/runtime_decision.py:12:1:12:13 | Function rd_bar | -| code/runtime_decision.py:30:1:30:7 | func2() | code/runtime_decision.py:8:1:8:13 | Function rd_foo | -| code/runtime_decision.py:30:1:30:7 | func2() | code/runtime_decision.py:12:1:12:13 | Function rd_bar | -| code/simple.py:19:1:19:5 | foo() | code/simple.py:2:1:2:10 | Function foo | -| code/simple.py:21:1:21:14 | indirect_foo() | code/simple.py:2:1:2:10 | Function foo | -| code/simple.py:23:1:23:5 | bar() | code/simple.py:10:1:10:10 | Function bar | -| code/simple.py:25:1:25:5 | lam() | code/simple.py:15:7:15:36 | Function lambda | -| code/underscore_prefix_func_name.py:16:5:16:19 | some_function() | code/underscore_prefix_func_name.py:10:1:10:20 | Function some_function | -| code/underscore_prefix_func_name.py:21:5:21:19 | some_function() | code/underscore_prefix_func_name.py:10:1:10:20 | Function some_function | -| code/underscore_prefix_func_name.py:25:5:25:19 | some_function() | code/underscore_prefix_func_name.py:10:1:10:20 | Function some_function | -unexpectedCallEdgeFound diff --git a/python/ql/test/experimental/library-tests/CallGraph/TypeTracker.ql b/python/ql/test/experimental/library-tests/CallGraph/TypeTracker.ql deleted file mode 100644 index a62332e3839..00000000000 --- a/python/ql/test/experimental/library-tests/CallGraph/TypeTracker.ql +++ /dev/null @@ -1,10 +0,0 @@ -import python -import CallGraphTest - -query predicate expectedCallEdgeNotFound(Call call, Function callable) { - any(TypeTrackerResolver r).expectedCallEdgeNotFound(call, callable) -} - -query predicate unexpectedCallEdgeFound(Call call, Function callable, string message) { - any(TypeTrackerResolver r).unexpectedCallEdgeFound(call, callable, message) -} diff --git a/python/ql/test/experimental/library-tests/CallGraph/code/class_simple.py b/python/ql/test/experimental/library-tests/CallGraph/code/class_simple.py index 7309620b3ec..f201e648e3a 100644 --- a/python/ql/test/experimental/library-tests/CallGraph/code/class_simple.py +++ b/python/ql/test/experimental/library-tests/CallGraph/code/class_simple.py @@ -4,17 +4,14 @@ class A(object): print('A.__init__', arg) self.arg = arg - # name:A.some_method def some_method(self): print('A.some_method', self) @staticmethod - # name:A.some_staticmethod def some_staticmethod(): print('A.some_staticmethod') @classmethod - # name:A.some_classmethod def some_classmethod(cls): print('A.some_classmethod', cls) @@ -24,14 +21,9 @@ class A(object): # However, current test setup uses "callable" for naming, and expects things to be Function. a = A(42) -# calls:A.some_method -a.some_method() -# calls:A.some_staticmethod -a.some_staticmethod() -# calls:A.some_classmethod -a.some_classmethod() +a.some_method() # $ pt=A.some_method +a.some_staticmethod() # $ pt=A.some_staticmethod +a.some_classmethod() # $ pt=A.some_classmethod -# calls:A.some_staticmethod -A.some_staticmethod() -# calls:A.some_classmethod -A.some_classmethod() +A.some_staticmethod() # $ pt=A.some_staticmethod +A.some_classmethod() # $ pt=A.some_classmethod diff --git a/python/ql/test/experimental/library-tests/CallGraph/code/runtime_decision.py b/python/ql/test/experimental/library-tests/CallGraph/code/runtime_decision.py index fd2f7773ced..3c4ebbb73e1 100644 --- a/python/ql/test/experimental/library-tests/CallGraph/code/runtime_decision.py +++ b/python/ql/test/experimental/library-tests/CallGraph/code/runtime_decision.py @@ -4,11 +4,9 @@ import random # hmm, annoying that you have to keep names unique across files :| # since I like to use foo and bar ALL the time :D -# name:rd_foo def rd_foo(): print('rd_foo') -# name:rd_bar def rd_bar(): print('rd_bar') @@ -17,8 +15,7 @@ if len(sys.argv) >= 2 and not sys.argv[1] in ['0', 'False', 'false']: else: func = rd_bar -# calls:rd_foo calls:rd_bar -func() +func() # $ pt=rd_foo pt=rd_bar # Random doesn't work with points-to :O if random.random() < 0.5: @@ -26,5 +23,4 @@ if random.random() < 0.5: else: func2 = rd_bar -# calls:rd_foo calls:rd_bar -func2() +func2() # $ pt=rd_foo pt=rd_bar diff --git a/python/ql/test/experimental/library-tests/CallGraph/code/simple.py b/python/ql/test/experimental/library-tests/CallGraph/code/simple.py index d3c39e42fd5..ac07ace93b2 100644 --- a/python/ql/test/experimental/library-tests/CallGraph/code/simple.py +++ b/python/ql/test/experimental/library-tests/CallGraph/code/simple.py @@ -1,4 +1,3 @@ -# name:foo def foo(): print("foo called") @@ -6,22 +5,16 @@ def foo(): indirect_foo = foo -# name:bar def bar(): print("bar called") -# name:lam lam = lambda: print("lambda called") -# calls:foo -foo() -# calls:foo -indirect_foo() -# calls:bar -bar() -# calls:lam -lam() +foo() # $ pt=foo +indirect_foo() # $ pt=foo +bar() # $ pt=bar +lam() # $ pt=lambda[simple.py:12:7] # python -m trace --trackcalls simple.py diff --git a/python/ql/test/experimental/library-tests/CallGraph/code/underscore_prefix_func_name.py b/python/ql/test/experimental/library-tests/CallGraph/code/underscore_prefix_func_name.py index 1ec87efd757..fb3f5fc45a8 100644 --- a/python/ql/test/experimental/library-tests/CallGraph/code/underscore_prefix_func_name.py +++ b/python/ql/test/experimental/library-tests/CallGraph/code/underscore_prefix_func_name.py @@ -6,23 +6,19 @@ # points-to information about the `open` call in # https://google-gruyere.appspot.com/code/gruyere.py on line 227 -# name:some_function def some_function(): print('some_function') def _ignored(): print('_ignored') - # calls:some_function some_function() def _works_since_called(): print('_works_since_called') - # calls:some_function - some_function() + some_function() # $ pt=some_function def works_even_though_not_called(): - # calls:some_function - some_function() + some_function() # $ pt=some_function globals()['_ignored']() -_works_since_called() +_works_since_called() # $ pt=_works_since_called diff --git a/python/ql/test/query-tests/Security/CWE-020-ExternalAPIs/ExternalAPIsUsedWithUntrustedData.expected b/python/ql/test/query-tests/Security/CWE-020-ExternalAPIs/ExternalAPIsUsedWithUntrustedData.expected index c070169615c..7438c415858 100644 --- a/python/ql/test/query-tests/Security/CWE-020-ExternalAPIs/ExternalAPIsUsedWithUntrustedData.expected +++ b/python/ql/test/query-tests/Security/CWE-020-ExternalAPIs/ExternalAPIsUsedWithUntrustedData.expected @@ -1 +1 @@ -| hmac.new [param 1] | 1 | 1 | +| hmac.new [param 1] | 2 | 1 | diff --git a/python/ql/test/query-tests/Security/CWE-020-ExternalAPIs/UntrustedDataToExternalAPI.expected b/python/ql/test/query-tests/Security/CWE-020-ExternalAPIs/UntrustedDataToExternalAPI.expected index c64a6943813..e024ef20cba 100644 --- a/python/ql/test/query-tests/Security/CWE-020-ExternalAPIs/UntrustedDataToExternalAPI.expected +++ b/python/ql/test/query-tests/Security/CWE-020-ExternalAPIs/UntrustedDataToExternalAPI.expected @@ -1,9 +1,12 @@ edges | test.py:0:0:0:0 | ModuleVariableNode for test.request | test.py:13:16:13:22 | ControlFlowNode for request | +| test.py:0:0:0:0 | ModuleVariableNode for test.request | test.py:23:16:23:22 | ControlFlowNode for request | | test.py:5:26:5:32 | ControlFlowNode for ImportMember | test.py:5:26:5:32 | GSSA Variable request | | test.py:5:26:5:32 | GSSA Variable request | test.py:0:0:0:0 | ModuleVariableNode for test.request | | test.py:13:16:13:22 | ControlFlowNode for request | test.py:13:16:13:27 | ControlFlowNode for Attribute | | test.py:13:16:13:27 | ControlFlowNode for Attribute | test.py:15:36:15:39 | ControlFlowNode for data | +| test.py:23:16:23:22 | ControlFlowNode for request | test.py:23:16:23:27 | ControlFlowNode for Attribute | +| test.py:23:16:23:27 | ControlFlowNode for Attribute | test.py:25:44:25:47 | ControlFlowNode for data | nodes | test.py:0:0:0:0 | ModuleVariableNode for test.request | semmle.label | ModuleVariableNode for test.request | | test.py:5:26:5:32 | ControlFlowNode for ImportMember | semmle.label | ControlFlowNode for ImportMember | @@ -11,6 +14,10 @@ nodes | test.py:13:16:13:22 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | | test.py:13:16:13:27 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | | test.py:15:36:15:39 | ControlFlowNode for data | semmle.label | ControlFlowNode for data | +| test.py:23:16:23:22 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | +| test.py:23:16:23:27 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| test.py:25:44:25:47 | ControlFlowNode for data | semmle.label | ControlFlowNode for data | subpaths #select | test.py:15:36:15:39 | ControlFlowNode for data | test.py:5:26:5:32 | ControlFlowNode for ImportMember | test.py:15:36:15:39 | ControlFlowNode for data | Call to hmac.new [param 1] with untrusted data from $@. | test.py:5:26:5:32 | ControlFlowNode for ImportMember | ControlFlowNode for ImportMember | +| test.py:25:44:25:47 | ControlFlowNode for data | test.py:5:26:5:32 | ControlFlowNode for ImportMember | test.py:25:44:25:47 | ControlFlowNode for data | Call to hmac.new [param 1] with untrusted data from $@. | test.py:5:26:5:32 | ControlFlowNode for ImportMember | ControlFlowNode for ImportMember | diff --git a/python/ql/test/query-tests/Security/CWE-020-ExternalAPIs/test.py b/python/ql/test/query-tests/Security/CWE-020-ExternalAPIs/test.py index b88748fbb29..ca4191ded85 100644 --- a/python/ql/test/query-tests/Security/CWE-020-ExternalAPIs/test.py +++ b/python/ql/test/query-tests/Security/CWE-020-ExternalAPIs/test.py @@ -18,11 +18,22 @@ def hmac_example(): return "ok" +@app.route("/hmac-example2") +def hmac_example2(): + data_raw = request.args.get("data").encode('utf-8') + data = base64.decodebytes(data_raw) + my_hmac = hmac.new(key=SECRET_KEY, msg=data, digestmod=hashlib.sha256) + digest = my_hmac.digest() + print(digest) + return "ok" + + @app.route("/unknown-lib-1") def unknown_lib_1(): from unknown.lib import func data = request.args.get("data") func(data) # TODO: currently not recognized + func(kw=data) # TODO: currently not recognized @app.route("/unknown-lib-2") @@ -30,6 +41,7 @@ def unknown_lib_2(): import unknown.lib data = request.args.get("data") unknown.lib.func(data) # TODO: currently not recognized + unknown.lib.func(kw=data) # TODO: currently not recognized if __name__ == "__main__": diff --git a/python/ql/test/query-tests/Security/CWE-312-CleartextLogging/CleartextLogging.expected b/python/ql/test/query-tests/Security/CWE-312-CleartextLogging/CleartextLogging.expected index e9b5ac67585..b2162352bae 100644 --- a/python/ql/test/query-tests/Security/CWE-312-CleartextLogging/CleartextLogging.expected +++ b/python/ql/test/query-tests/Security/CWE-312-CleartextLogging/CleartextLogging.expected @@ -4,8 +4,9 @@ edges | test.py:19:16:19:29 | ControlFlowNode for get_password() | test.py:23:58:23:65 | ControlFlowNode for password | | test.py:19:16:19:29 | ControlFlowNode for get_password() | test.py:27:40:27:47 | ControlFlowNode for password | | test.py:19:16:19:29 | ControlFlowNode for get_password() | test.py:30:58:30:65 | ControlFlowNode for password | -| test.py:65:14:68:5 | ControlFlowNode for Dict | test.py:69:11:69:31 | ControlFlowNode for Subscript | -| test.py:67:21:67:37 | ControlFlowNode for Attribute | test.py:65:14:68:5 | ControlFlowNode for Dict | +| test.py:44:9:44:25 | ControlFlowNode for Attribute() | test.py:45:11:45:11 | ControlFlowNode for x | +| test.py:70:14:73:5 | ControlFlowNode for Dict | test.py:74:11:74:31 | ControlFlowNode for Subscript | +| test.py:72:21:72:37 | ControlFlowNode for Attribute | test.py:70:14:73:5 | ControlFlowNode for Dict | nodes | test.py:19:16:19:29 | ControlFlowNode for get_password() | semmle.label | ControlFlowNode for get_password() | | test.py:20:48:20:55 | ControlFlowNode for password | semmle.label | ControlFlowNode for password | @@ -17,9 +18,11 @@ nodes | test.py:37:11:37:24 | ControlFlowNode for get_password() | semmle.label | ControlFlowNode for get_password() | | test.py:39:22:39:35 | ControlFlowNode for get_password() | semmle.label | ControlFlowNode for get_password() | | test.py:40:22:40:35 | ControlFlowNode for get_password() | semmle.label | ControlFlowNode for get_password() | -| test.py:65:14:68:5 | ControlFlowNode for Dict | semmle.label | ControlFlowNode for Dict | -| test.py:67:21:67:37 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | -| test.py:69:11:69:31 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | +| test.py:44:9:44:25 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | +| test.py:45:11:45:11 | ControlFlowNode for x | semmle.label | ControlFlowNode for x | +| test.py:70:14:73:5 | ControlFlowNode for Dict | semmle.label | ControlFlowNode for Dict | +| test.py:72:21:72:37 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | +| test.py:74:11:74:31 | ControlFlowNode for Subscript | semmle.label | ControlFlowNode for Subscript | subpaths #select | test.py:20:48:20:55 | ControlFlowNode for password | test.py:19:16:19:29 | ControlFlowNode for get_password() | test.py:20:48:20:55 | ControlFlowNode for password | This expression logs $@ as clear text. | test.py:19:16:19:29 | ControlFlowNode for get_password() | sensitive data (password) | @@ -31,4 +34,5 @@ subpaths | test.py:37:11:37:24 | ControlFlowNode for get_password() | test.py:37:11:37:24 | ControlFlowNode for get_password() | test.py:37:11:37:24 | ControlFlowNode for get_password() | This expression logs $@ as clear text. | test.py:37:11:37:24 | ControlFlowNode for get_password() | sensitive data (password) | | test.py:39:22:39:35 | ControlFlowNode for get_password() | test.py:39:22:39:35 | ControlFlowNode for get_password() | test.py:39:22:39:35 | ControlFlowNode for get_password() | This expression logs $@ as clear text. | test.py:39:22:39:35 | ControlFlowNode for get_password() | sensitive data (password) | | test.py:40:22:40:35 | ControlFlowNode for get_password() | test.py:40:22:40:35 | ControlFlowNode for get_password() | test.py:40:22:40:35 | ControlFlowNode for get_password() | This expression logs $@ as clear text. | test.py:40:22:40:35 | ControlFlowNode for get_password() | sensitive data (password) | -| test.py:69:11:69:31 | ControlFlowNode for Subscript | test.py:67:21:67:37 | ControlFlowNode for Attribute | test.py:69:11:69:31 | ControlFlowNode for Subscript | This expression logs $@ as clear text. | test.py:67:21:67:37 | ControlFlowNode for Attribute | sensitive data (password) | +| test.py:45:11:45:11 | ControlFlowNode for x | test.py:44:9:44:25 | ControlFlowNode for Attribute() | test.py:45:11:45:11 | ControlFlowNode for x | This expression logs $@ as clear text. | test.py:44:9:44:25 | ControlFlowNode for Attribute() | sensitive data (password) | +| test.py:74:11:74:31 | ControlFlowNode for Subscript | test.py:72:21:72:37 | ControlFlowNode for Attribute | test.py:74:11:74:31 | ControlFlowNode for Subscript | This expression logs $@ as clear text. | test.py:72:21:72:37 | ControlFlowNode for Attribute | sensitive data (password) | diff --git a/python/ql/test/query-tests/Security/CWE-312-CleartextLogging/test.py b/python/ql/test/query-tests/Security/CWE-312-CleartextLogging/test.py index 0a3d97426e0..b5ebe7593ba 100644 --- a/python/ql/test/query-tests/Security/CWE-312-CleartextLogging/test.py +++ b/python/ql/test/query-tests/Security/CWE-312-CleartextLogging/test.py @@ -39,6 +39,11 @@ def print_password(): sys.stdout.write(get_password()) # NOT OK sys.stderr.write(get_password()) # NOT OK + import getpass + + x = getpass.getpass() + print(x) # NOT OK + def FPs(account, account_id): # we assume that any account parameter is sensitive (id/username) diff --git a/ql/ql/src/codeql/GlobalValueNumbering.qll b/ql/ql/src/codeql/GlobalValueNumbering.qll index cb9483200d0..21fe729fedc 100644 --- a/ql/ql/src/codeql/GlobalValueNumbering.qll +++ b/ql/ql/src/codeql/GlobalValueNumbering.qll @@ -237,7 +237,7 @@ private TValueNumber nonUniqueValueNumber(Expr e) { /** Gets the value number of an expression `e`. */ cached -TValueNumber valueNumber(Expr e) { +ValueNumber valueNumber(Expr e) { result = nonUniqueValueNumber(e) or uniqueValueNumber(e) and diff --git a/ql/ql/src/queries/style/RedundantAssignment.ql b/ql/ql/src/queries/style/RedundantAssignment.ql new file mode 100644 index 00000000000..7baf046b97a --- /dev/null +++ b/ql/ql/src/queries/style/RedundantAssignment.ql @@ -0,0 +1,110 @@ +/** + * @name Redundant assignment. + * @description Assigning the same value twice is redundant. + * @kind problem + * @problem.severity warning + * @precision high + * @id ql/redunant-assignment + * @tags maintainability + */ + +import ql + +/** + * A variable that is set equal to (assigned) a value one or more times. + */ +class AssignedVariable extends VarDecl { + AssignedVariable() { + exists(VarAccess access, ComparisonFormula comp | comp.getOperator() = "=" | + access.getDeclaration() = this and + comp.getAnOperand() = access + ) + } + + /** + * Gets an expression that is assigned to this variable. + */ + Expr getAnAssignedExpr() { + exists(VarAccess access, ComparisonFormula comp, Expr operand | + comp.getOperator() = "=" and + access.getDeclaration() = this and + comp.getAnOperand() = access and + operand = comp.getAnOperand() and + not operand.(VarAccess).getDeclaration() = this + | + result = operand and + not result instanceof Set + or + result = operand.(Set).getAnElement() + ) + } +} + +import codeql.GlobalValueNumbering as GVN + +/** + * Holds if `assigned1` and `assigned2` assigns the same value to `var`. + * The assignments may be on different branches of a disjunction. + */ +predicate candidateRedundantAssignment(AssignedVariable var, Expr assigned1, Expr assigned2) { + assigned1 = var.getAnAssignedExpr() and + assigned2 = var.getAnAssignedExpr() and + ( + GVN::valueNumber(assigned1) = GVN::valueNumber(assigned2) + or + // because GVN skips large strings, we need to check for equality manually + assigned1.(String).getValue() = assigned2.(String).getValue() + ) and + assigned1 != assigned2 +} + +/** + * Gets a (transitive) parent of `p`, where the parent is not a disjunction, and `p` is a candidate assignment from `candidateRedundantAssignment`. + */ +AstNode getConjunctionParentRec(AstNode p) { + candidateRedundantAssignment(_, p, _) and + result = p + or + result = getConjunctionParentRec(p).getParent() and + not result instanceof Disjunction and + not result instanceof IfFormula and + not result instanceof Implication and + not result instanceof Negation and + not result instanceof Predicate +} + +/** + * Gets which level in the AST `p` is at. + * E.g. the top-level is 0, the next level is 1, etc. + */ +int level(AstNode p) { + p instanceof TopLevel and result = 0 + or + result = level(p.getParent()) + 1 +} + +/** + * Gets the top-most parent of `p` that is not a disjunction. + */ +AstNode getConjunctionParent(AstNode p) { + result = + min(int level, AstNode parent | + parent = getConjunctionParentRec(p) and level = level(parent) + | + parent order by level + ) +} + +from AssignedVariable var, Expr assigned1, Expr assigned2 +where + candidateRedundantAssignment(var, assigned1, assigned2) and + getConjunctionParent(assigned1) = getConjunctionParent(assigned2) and + // de-duplcation: + ( + assigned1.getLocation().getStartLine() < assigned2.getLocation().getStartLine() + or + assigned1.getLocation().getStartLine() = assigned2.getLocation().getStartLine() and + assigned1.getLocation().getStartColumn() < assigned2.getLocation().getStartColumn() + ) +select assigned2, "$@ has previously been assigned $@.", var, "The variable " + var.getName(), + assigned1, "the same value"