diff --git a/.github/workflows/check-change-note.yml b/.github/workflows/check-change-note.yml
index 70b78ce72944..d29bd00486be 100644
--- a/.github/workflows/check-change-note.yml
+++ b/.github/workflows/check-change-note.yml
@@ -1,10 +1,11 @@
name: Check change note
permissions:
+ contents: read
pull-requests: read
on:
- pull_request_target:
+ pull_request:
types: [labeled, unlabeled, opened, synchronize, reopened, ready_for_review]
paths:
- "*/ql/src/**/*.ql"
@@ -23,7 +24,7 @@ jobs:
env:
REPO: ${{ github.repository }}
PULL_REQUEST_NUMBER: ${{ github.event.number }}
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ GH_TOKEN: ${{ github.token }}
runs-on: ubuntu-latest
steps:
diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml
index 512fa40d2e3a..e3f464331790 100644
--- a/.github/workflows/labeler.yml
+++ b/.github/workflows/labeler.yml
@@ -1,15 +1,144 @@
name: "Pull Request Labeler"
+
on:
-- pull_request_target
+ schedule:
+ # Reconcile recently updated PRs promptly, including unapproved forks and
+ # conflicted PRs for which pull_request workflows do not run.
+ - cron: "7,22,37,52 * * * *"
+ # Reconcile one stable shard of all open PRs each hour to recover from
+ # delayed or missed scheduled runs.
+ - cron: "12 * * * *"
+ workflow_dispatch:
+ inputs:
+ pr_number:
+ description: "Open pull request number to reconcile"
+ required: true
+ type: string
+
+permissions: {}
-permissions:
- contents: read
- pull-requests: write
+concurrency:
+ group: pull-request-labeler
+ cancel-in-progress: false
jobs:
triage:
+ if: github.ref_name == github.event.repository.default_branch
runs-on: ubuntu-latest
+ timeout-minutes: 30
+ permissions:
+ contents: read
+ pull-requests: write
steps:
- - uses: actions/labeler@v4
- with:
- repo-token: "${{ secrets.GITHUB_TOKEN }}"
+ - uses: actions/checkout@v5
+ with:
+ persist-credentials: false
+ sparse-checkout: .github/labeler.yml
+ sparse-checkout-cone-mode: false
+
+ - name: Collect pull requests to reconcile
+ id: collect
+ env:
+ GH_TOKEN: ${{ github.token }}
+ REPO: ${{ github.repository }}
+ EVENT_NAME: ${{ github.event_name }}
+ SCHEDULE: ${{ github.event.schedule }}
+ REQUESTED_PR: ${{ inputs.pr_number }}
+ run: |
+ set -euo pipefail
+
+ if [ "$EVENT_NAME" = "workflow_dispatch" ]; then
+ if [[ ! "$REQUESTED_PR" =~ ^[1-9][0-9]*$ ]]; then
+ echo "Invalid pull request number: $REQUESTED_PR"
+ exit 1
+ fi
+
+ pr_json=$(gh api "repos/$REPO/pulls/$REQUESTED_PR")
+ candidates=$(jq -c '[{
+ number: .number,
+ head_sha: .head.sha
+ }]' <<<"$pr_json")
+ else
+ pulls_json=$(gh api --paginate \
+ "repos/$REPO/pulls?state=open&sort=updated&direction=desc&per_page=100" |
+ jq -cs 'add')
+
+ if [ "$SCHEDULE" = "12 * * * *" ]; then
+ shard=$(( ($(date -u +%s) / 3600) % 6 ))
+ candidates=$(jq -c --argjson shard "$shard" \
+ '[.[] | select((.number % 6) == $shard) | {
+ number: .number,
+ head_sha: .head.sha
+ }]' <<<"$pulls_json")
+ else
+ cutoff=$(date -u -d "1 hour ago" "+%Y-%m-%dT%H:%M:%SZ")
+ # Hourly shards reconcile any candidates beyond this API budget.
+ candidates=$(jq -c --arg cutoff "$cutoff" \
+ '[.[] | select(.updated_at >= $cutoff) | {
+ number: .number,
+ head_sha: .head.sha
+ }][0:100]' <<<"$pulls_json")
+ fi
+ fi
+
+ echo "Collected $(jq 'length' <<<"$candidates") pull request(s)."
+ {
+ echo "candidates<> "$GITHUB_OUTPUT"
+
+ - name: Validate pull request state
+ id: validate
+ env:
+ GH_TOKEN: ${{ github.token }}
+ REPO: ${{ github.repository }}
+ CANDIDATES: ${{ steps.collect.outputs.candidates }}
+ run: |
+ set -euo pipefail
+
+ valid_numbers=()
+ while IFS=$'\t' read -r pr_number expected_sha; do
+ if [[ ! "$pr_number" =~ ^[1-9][0-9]*$ ]] ||
+ [[ ! "$expected_sha" =~ ^[0-9a-f]{40}$ ]]; then
+ echo "Skipping malformed pull request candidate."
+ continue
+ fi
+
+ if ! pr_json=$(gh api "repos/$REPO/pulls/$pr_number"); then
+ echo "Pull request #$pr_number could not be fetched; skipping."
+ continue
+ fi
+
+ if ! jq -e \
+ --arg repo "$REPO" \
+ --arg sha "$expected_sha" \
+ '.state == "open" and
+ .base.repo.full_name == $repo and
+ .head.sha == $sha and
+ (.head.repo.full_name | type == "string")' \
+ >/dev/null <<<"$pr_json"; then
+ echo "Pull request #$pr_number changed or is no longer open; skipping."
+ continue
+ fi
+
+ valid_numbers+=("$pr_number")
+ done < <(jq -r '.[] | [.number, .head_sha] | @tsv' <<<"$CANDIDATES")
+
+ if [ "${#valid_numbers[@]}" -eq 0 ]; then
+ echo "has_prs=false" >> "$GITHUB_OUTPUT"
+ exit 0
+ fi
+
+ {
+ echo "has_prs=true"
+ echo "pr_numbers<> "$GITHUB_OUTPUT"
+
+ - uses: actions/labeler@v4
+ if: steps.validate.outputs.has_prs == 'true'
+ with:
+ repo-token: "${{ github.token }}"
+ pr-number: ${{ steps.validate.outputs.pr_numbers }}
diff --git a/BUILD.bazel b/BUILD.bazel
index b2e4ea806785..07027f4ad9a7 100644
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -3,3 +3,11 @@ exports_files([
"Cargo.lock",
"Cargo.toml",
])
+
+constraint_setting(name = "swift_runtime_linkage")
+
+constraint_value(
+ name = "static_swift_runtime",
+ constraint_setting = ":swift_runtime_linkage",
+ visibility = ["//visibility:public"],
+)
diff --git a/MODULE.bazel b/MODULE.bazel
index 91c871445eb4..7f15d1e889f7 100644
--- a/MODULE.bazel
+++ b/MODULE.bazel
@@ -31,7 +31,7 @@ bazel_dep(name = "gazelle", version = "0.50.0")
bazel_dep(name = "rules_dotnet", version = "0.21.5-codeql.1")
bazel_dep(name = "googletest", version = "1.17.0.bcr.2")
bazel_dep(name = "rules_rust", version = "0.69.0")
-bazel_dep(name = "rules_swift", version = "4.0.0-rc5-codeql.1")
+bazel_dep(name = "rules_swift", version = "4.0.0-rc5-codeql.2")
bazel_dep(name = "swift-syntax", version = "603.0.2")
bazel_dep(name = "zstd", version = "1.5.7.bcr.1")
@@ -228,7 +228,7 @@ use_repo(
# `unified/swift-syntax-rs` package is not loadable in that context. Keep this
# in sync with `unified/swift-syntax-rs/.swift-version` (used by the `cargo`
# build) and the `swift-syntax` release in `swift/Package.swift`.
-swift = use_extension("@rules_swift//swift:extensions.bzl", "swift")
+swift = use_extension("@rules_swift//swift:extensions.bzl", "swift", dev_dependency = True)
swift.toolchain(
name = "swift_toolchain",
swift_version = "6.3.3",
@@ -237,12 +237,15 @@ use_repo(
swift,
"swift_toolchain",
"swift_toolchain_ubuntu22.04",
+ "swift_toolchain_ubuntu22.04-aarch64",
"swift_toolchain_xcode",
)
register_toolchains(
"@swift_toolchain//:swift_toolchain_exec_ubuntu22.04",
+ "@swift_toolchain//:swift_toolchain_exec_ubuntu22.04-aarch64",
"@swift_toolchain//:swift_toolchain_exec_xcode",
+ dev_dependency = True,
)
node = use_extension("@rules_nodejs//nodejs:extensions.bzl", "node")
diff --git a/actions/extractor/tools/autobuild-impl.ps1 b/actions/extractor/tools/autobuild-impl.ps1
index e232cd3cc545..6f04a28520d3 100644
--- a/actions/extractor/tools/autobuild-impl.ps1
+++ b/actions/extractor/tools/autobuild-impl.ps1
@@ -9,7 +9,8 @@ $DefaultPathFilters = @(
'include:.github/reusable_workflows/**/*.yml',
'include:.github/reusable_workflows/**/*.yaml',
'include:**/action.yml',
- 'include:**/action.yaml'
+ 'include:**/action.yaml',
+ 'include:**/actions.lock'
)
if ($null -ne $env:LGTM_INDEX_FILTERS) {
diff --git a/actions/extractor/tools/autobuild.sh b/actions/extractor/tools/autobuild.sh
index f2cbb7ddfa7e..c2794044ee64 100755
--- a/actions/extractor/tools/autobuild.sh
+++ b/actions/extractor/tools/autobuild.sh
@@ -14,6 +14,7 @@ include:.github/reusable_workflows/**/*.yml
include:.github/reusable_workflows/**/*.yaml
include:**/action.yml
include:**/action.yaml
+include:**/actions.lock
END
)
diff --git a/actions/extractor/tools/baseline-config.json b/actions/extractor/tools/baseline-config.json
index fde0bd1ecdff..5c0044c8d9d8 100644
--- a/actions/extractor/tools/baseline-config.json
+++ b/actions/extractor/tools/baseline-config.json
@@ -5,6 +5,7 @@
".github/reusable_workflows/**/*.yml",
".github/reusable_workflows/**/*.yaml",
"**/action.yml",
- "**/action.yaml"
+ "**/action.yaml",
+ "**/actions.lock"
]
}
diff --git a/actions/ql/integration-tests/actions-lock/query/actions.ql b/actions/ql/integration-tests/actions-lock/query/actions.ql
new file mode 100644
index 000000000000..03451f476fcd
--- /dev/null
+++ b/actions/ql/integration-tests/actions-lock/query/actions.ql
@@ -0,0 +1,4 @@
+import codeql.actions.Lock
+
+from ActionsLock lock
+select lock.getFile()
diff --git a/actions/ql/integration-tests/actions-lock/query/qlpack.yml b/actions/ql/integration-tests/actions-lock/query/qlpack.yml
new file mode 100644
index 000000000000..03c0b4e9a3af
--- /dev/null
+++ b/actions/ql/integration-tests/actions-lock/query/qlpack.yml
@@ -0,0 +1,4 @@
+name: codeql/actions-lock-integration-test
+dependencies:
+ codeql/actions-all: "*"
+warnOnImplicitThis: true
diff --git a/actions/ql/integration-tests/actions-lock/src/.github/workflows/test.yml b/actions/ql/integration-tests/actions-lock/src/.github/workflows/test.yml
new file mode 100644
index 000000000000..79afdcdf7006
--- /dev/null
+++ b/actions/ql/integration-tests/actions-lock/src/.github/workflows/test.yml
@@ -0,0 +1,6 @@
+on: push
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo test
diff --git a/actions/ql/integration-tests/actions-lock/src/actions.lock b/actions/ql/integration-tests/actions-lock/src/actions.lock
new file mode 100644
index 000000000000..bb91aa093d86
--- /dev/null
+++ b/actions/ql/integration-tests/actions-lock/src/actions.lock
@@ -0,0 +1,19 @@
+# This file is machine-generated by `gh actions-lock`.
+# Do not edit by hand; run `gh actions-lock` to update.
+# Docs: https://gh.io/actions-lockfile
+version: 'v0.0.2'
+workflows:
+ '.github/workflows/test.yml':
+ - 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1'
+ - 'github/codeql-action@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28'
+dependencies:
+ 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1':
+ ref: '3d3c42e5aac5ba805825da76410c181273ba90b1'
+ commit: 'sha1-3d3c42e5aac5ba805825da76410c181273ba90b1'
+ owner_id: 44036562
+ repo_id: 197814629
+ 'github/codeql-action@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28':
+ ref: 'v4.37.8'
+ commit: 'sha1-db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28'
+ owner_id: 9919
+ repo_id: 259445878
diff --git a/actions/ql/integration-tests/actions-lock/test.py b/actions/ql/integration-tests/actions-lock/test.py
new file mode 100644
index 000000000000..3d63143dc1c2
--- /dev/null
+++ b/actions/ql/integration-tests/actions-lock/test.py
@@ -0,0 +1,4 @@
+def test_actions_lock(codeql, actions, javascript):
+ codeql.database.create(source_root="src", language="actions")
+ output = codeql.query.run("query/actions.ql", database="test-db", _capture=True)
+ assert "actions.lock" in output
diff --git a/actions/ql/lib/CHANGELOG.md b/actions/ql/lib/CHANGELOG.md
index e686c4d596f4..d1943239b2d1 100644
--- a/actions/ql/lib/CHANGELOG.md
+++ b/actions/ql/lib/CHANGELOG.md
@@ -1,3 +1,14 @@
+## 0.6.1
+
+### New Features
+
+* GitHub Actions databases now extract `actions.lock` files. The new `ActionsLock` class
+ provides access to their YAML abstract syntax trees.
+
+### Minor Analysis Improvements
+
+* Checks on author association fields read from the event payload (e.g. `github.event.pull_request.author_association`) now only count as protection for events whose payload actually populates that field. Previously, a condition such as `github.event.pull_request.author_association != 'NONE'` on a workflow triggered by `issues` events was treated as a protective check even though `github.event.pull_request` is not populated for `issues` events, which makes the condition vacuous. This change may result in more alerts for queries using the `ControlCheck` class.
+
## 0.6.0
### Breaking Changes
diff --git a/actions/ql/lib/actions.qll b/actions/ql/lib/actions.qll
index 2c1d1cee9259..f57127b0f031 100644
--- a/actions/ql/lib/actions.qll
+++ b/actions/ql/lib/actions.qll
@@ -1 +1,2 @@
import codeql.actions.Ast
+import codeql.actions.Lock
diff --git a/actions/ql/lib/change-notes/released/0.6.1.md b/actions/ql/lib/change-notes/released/0.6.1.md
new file mode 100644
index 000000000000..19f7f95948ad
--- /dev/null
+++ b/actions/ql/lib/change-notes/released/0.6.1.md
@@ -0,0 +1,10 @@
+## 0.6.1
+
+### New Features
+
+* GitHub Actions databases now extract `actions.lock` files. The new `ActionsLock` class
+ provides access to their YAML abstract syntax trees.
+
+### Minor Analysis Improvements
+
+* Checks on author association fields read from the event payload (e.g. `github.event.pull_request.author_association`) now only count as protection for events whose payload actually populates that field. Previously, a condition such as `github.event.pull_request.author_association != 'NONE'` on a workflow triggered by `issues` events was treated as a protective check even though `github.event.pull_request` is not populated for `issues` events, which makes the condition vacuous. This change may result in more alerts for queries using the `ControlCheck` class.
diff --git a/actions/ql/lib/codeql-pack.release.yml b/actions/ql/lib/codeql-pack.release.yml
index a3f820f884d3..80fb0899f645 100644
--- a/actions/ql/lib/codeql-pack.release.yml
+++ b/actions/ql/lib/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 0.6.0
+lastReleaseVersion: 0.6.1
diff --git a/actions/ql/lib/codeql/actions/Lock.qll b/actions/ql/lib/codeql/actions/Lock.qll
new file mode 100644
index 000000000000..8fb8a8a8b359
--- /dev/null
+++ b/actions/ql/lib/codeql/actions/Lock.qll
@@ -0,0 +1,10 @@
+/**
+ * Provides classes for working with GitHub Actions lockfiles.
+ */
+
+private import codeql.actions.ast.internal.Yaml
+
+/** An `actions.lock` file. */
+class ActionsLock extends YamlDocument {
+ ActionsLock() { this.getFile().getBaseName() = "actions.lock" }
+}
diff --git a/actions/ql/lib/codeql/actions/security/ControlChecks.qll b/actions/ql/lib/codeql/actions/security/ControlChecks.qll
index 675c1d18852b..aea57fdc4b71 100644
--- a/actions/ql/lib/codeql/actions/security/ControlChecks.qll
+++ b/actions/ql/lib/codeql/actions/security/ControlChecks.qll
@@ -408,16 +408,37 @@ class WorkflowRunRepositoryIfCheck extends RepositoryCheck instanceof If {
}
}
+/**
+ * Gets a regular expression matching a condition on an author association field
+ * that is only populated for events whose payload contains the `context_prefix`
+ * context.
+ */
+private string eventPayloadAssociationFieldRegex(string context_prefix) {
+ context_prefix = "github.event.comment" and
+ result = "\\bgithub\\.event\\.comment\\.author_association\\b"
+ or
+ context_prefix = "github.event.issue" and
+ result = "\\bgithub\\.event\\.issue\\.author_association\\b"
+ or
+ context_prefix = "github.event.pull_request" and
+ result = "\\bgithub\\.event\\.pull_request\\.author_association\\b"
+}
+
class AssociationIfCheck extends AssociationCheck instanceof If {
+ string context_prefix;
+
AssociationIfCheck() {
// eg: contains(fromJson('["MEMBER", "OWNER"]'), github.event.comment.author_association)
- normalizeExpr(this.getCondition())
- .splitAt("\n")
- .regexpMatch([
- ".*\\bgithub\\.event\\.comment\\.author_association\\b.*",
- ".*\\bgithub\\.event\\.issue\\.author_association\\b.*",
- ".*\\bgithub\\.event\\.pull_request\\.author_association\\b.*",
- ])
+ exists(
+ normalizeExpr(this.getCondition())
+ .regexpFind(eventPayloadAssociationFieldRegex(context_prefix), _, _)
+ )
+ }
+
+ override predicate protectsCategoryAndEvent(string category, string event) {
+ AssociationCheck.super.protectsCategoryAndEvent(category, event) and
+ // association fields only restrict events whose payload populates them
+ contextTriggerDataModel(event, context_prefix)
}
}
diff --git a/actions/ql/lib/qlpack.yml b/actions/ql/lib/qlpack.yml
index b86df498d3dd..268018a37415 100644
--- a/actions/ql/lib/qlpack.yml
+++ b/actions/ql/lib/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/actions-all
-version: 0.6.1-dev
+version: 0.6.2-dev
library: true
warnOnImplicitThis: true
dependencies:
diff --git a/actions/ql/src/CHANGELOG.md b/actions/ql/src/CHANGELOG.md
index 6a4f17d872be..9676c26efaff 100644
--- a/actions/ql/src/CHANGELOG.md
+++ b/actions/ql/src/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.6.35
+
+No user-facing changes.
+
## 0.6.34
### Minor Analysis Improvements
diff --git a/actions/ql/src/change-notes/released/0.6.35.md b/actions/ql/src/change-notes/released/0.6.35.md
new file mode 100644
index 000000000000..aa17e650d404
--- /dev/null
+++ b/actions/ql/src/change-notes/released/0.6.35.md
@@ -0,0 +1,3 @@
+## 0.6.35
+
+No user-facing changes.
diff --git a/actions/ql/src/codeql-pack.release.yml b/actions/ql/src/codeql-pack.release.yml
index fe5075b50fbd..0a9561ad0b98 100644
--- a/actions/ql/src/codeql-pack.release.yml
+++ b/actions/ql/src/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 0.6.34
+lastReleaseVersion: 0.6.35
diff --git a/actions/ql/src/qlpack.yml b/actions/ql/src/qlpack.yml
index f4323defe6e8..5feedb52c8ce 100644
--- a/actions/ql/src/qlpack.yml
+++ b/actions/ql/src/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/actions-queries
-version: 0.6.35-dev
+version: 0.6.36-dev
library: false
warnOnImplicitThis: true
groups: [actions, queries]
diff --git a/actions/ql/test/library-tests/actions-lock/.github/workflows/test.yml b/actions/ql/test/library-tests/actions-lock/.github/workflows/test.yml
new file mode 100644
index 000000000000..79afdcdf7006
--- /dev/null
+++ b/actions/ql/test/library-tests/actions-lock/.github/workflows/test.yml
@@ -0,0 +1,6 @@
+on: push
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ steps:
+ - run: echo test
diff --git a/actions/ql/test/library-tests/actions-lock/actions.lock b/actions/ql/test/library-tests/actions-lock/actions.lock
new file mode 100644
index 000000000000..bb91aa093d86
--- /dev/null
+++ b/actions/ql/test/library-tests/actions-lock/actions.lock
@@ -0,0 +1,19 @@
+# This file is machine-generated by `gh actions-lock`.
+# Do not edit by hand; run `gh actions-lock` to update.
+# Docs: https://gh.io/actions-lockfile
+version: 'v0.0.2'
+workflows:
+ '.github/workflows/test.yml':
+ - 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1'
+ - 'github/codeql-action@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28'
+dependencies:
+ 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1':
+ ref: '3d3c42e5aac5ba805825da76410c181273ba90b1'
+ commit: 'sha1-3d3c42e5aac5ba805825da76410c181273ba90b1'
+ owner_id: 44036562
+ repo_id: 197814629
+ 'github/codeql-action@db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28':
+ ref: 'v4.37.8'
+ commit: 'sha1-db488ddef3bf6cb639b32c2e9a7c0a7ea8271d28'
+ owner_id: 9919
+ repo_id: 259445878
diff --git a/actions/ql/test/library-tests/actions-lock/options b/actions/ql/test/library-tests/actions-lock/options
new file mode 100644
index 000000000000..fa6fe3a34854
--- /dev/null
+++ b/actions/ql/test/library-tests/actions-lock/options
@@ -0,0 +1 @@
+semmle-extractor-options: actions.lock
diff --git a/actions/ql/test/library-tests/actions-lock/test.expected b/actions/ql/test/library-tests/actions-lock/test.expected
new file mode 100644
index 000000000000..128df0e9ae0e
--- /dev/null
+++ b/actions/ql/test/library-tests/actions-lock/test.expected
@@ -0,0 +1 @@
+| actions.lock:0:0:0:0 | actions.lock |
diff --git a/actions/ql/test/library-tests/actions-lock/test.ql b/actions/ql/test/library-tests/actions-lock/test.ql
new file mode 100644
index 000000000000..03451f476fcd
--- /dev/null
+++ b/actions/ql/test/library-tests/actions-lock/test.ql
@@ -0,0 +1,4 @@
+import codeql.actions.Lock
+
+from ActionsLock lock
+select lock.getFile()
diff --git a/actions/ql/test/query-tests/Security/CWE-094/.github/workflows/association_check_wrong_event.yml b/actions/ql/test/query-tests/Security/CWE-094/.github/workflows/association_check_wrong_event.yml
new file mode 100644
index 000000000000..b1cef25955dd
--- /dev/null
+++ b/actions/ql/test/query-tests/Security/CWE-094/.github/workflows/association_check_wrong_event.yml
@@ -0,0 +1,21 @@
+on:
+ issues:
+ types: [opened]
+
+jobs:
+ # The `if:` condition compares an association field that is never populated
+ # for `issues` events, so it is always true and does not protect the
+ # injectable step.
+ vacuous-association-check:
+ runs-on: ubuntu-latest
+ if: github.event.pull_request.author_association != 'NONE'
+ steps:
+ - run: echo '${{ github.event.issue.title }}'
+
+ # `github.event.issue` is populated for `issues` events, so this check is
+ # effective and the injectable step is protected.
+ valid-association-check:
+ runs-on: ubuntu-latest
+ if: github.event.issue.author_association == 'MEMBER'
+ steps:
+ - run: echo '${{ github.event.issue.title }}'
diff --git a/actions/ql/test/query-tests/Security/CWE-094/CodeInjectionCritical.expected b/actions/ql/test/query-tests/Security/CWE-094/CodeInjectionCritical.expected
index 14e50942d734..e32155dffe93 100644
--- a/actions/ql/test/query-tests/Security/CWE-094/CodeInjectionCritical.expected
+++ b/actions/ql/test/query-tests/Security/CWE-094/CodeInjectionCritical.expected
@@ -312,6 +312,8 @@ nodes
| .github/workflows/artifactpoisoning8.yml:17:9:21:6 | Run Step: artifact [id] | semmle.label | Run Step: artifact [id] |
| .github/workflows/artifactpoisoning8.yml:19:14:19:58 | echo "::set-output name=id::$( {
class SummarizedCallableBase = Function;
- class SourceBase extends Void {
- Location getLocation() { none() }
- }
-
- class SinkBase = SourceBase;
-
class FlowSummaryCallBase = CallInstruction;
predicate callableFromSource(SummarizedCallableBase c) { exists(c.getBlock()) }
@@ -134,15 +128,208 @@ module Input implements InputSig {
private import Make as Impl
+private class ConversionCall extends Call {
+ ConversionCall() { this.getTarget() instanceof ConversionOperator }
+}
+
private module Input2 implements Impl::Private::InputSig2 {
private import codeql.util.Void
- class SourceSinkReportingElement extends Void {
- Location getLocation() { none() }
+ pragma[nomagic]
+ private predicate hasFunctionAndIndirectionIndex(
+ Function f, int indirectionIndex, Ssa::ExplicitDefinition def
+ ) {
+ def.getFunction() = f and
+ def.getSourceVariable().getIRVariable() instanceof IRReturnVariable and
+ def.getIndirectionIndex() = indirectionIndex
+ }
- DataFlowCallable getEnclosingCallable() { none() }
+ /** Holds if `def` defines `e` as a returned value with return kind `rk`. */
+ bindingset[rk, e]
+ private predicate isReturnExpr(Function f, ReturnKind rk, Expr e) {
+ exists(Ssa::ExplicitDefinition def |
+ hasFunctionAndIndirectionIndex(f, rk.getIndirectionIndex(), def) and
+ e =
+ def.getAssignedInstruction()
+ .(StoreInstruction)
+ .getSourceValue()
+ .getUnconvertedResultExpression()
+ )
+ }
+
+ private MemberFunction getFunctionFromType(Expr e) {
+ result.getClassAndName("operator()").getADerivedClass*() = e.getUnspecifiedType()
+ }
- SourceSinkReportingElement getASuccessor(Impl::Private::SummaryComponent sc) { none() }
+ private Function getFunctionFromExpr(Expr e) {
+ result = e.(FunctionAccess).getTarget()
+ or
+ result = e.(ConversionCall).getQualifier().(LambdaExpression).getLambdaFunction()
+ }
+
+ private predicate isRelevantUltimateDefinition(Ssa::DirectExplicitDefinition def, Function f) {
+ f =
+ getFunctionFromExpr(def.getAssignedInstruction()
+ .(StoreInstruction)
+ .getSourceValue()
+ .getUnconvertedResultExpression())
+ }
+
+ private module GetAnUltimateDefinitionInput implements Ssa::GetAnUltimateDefinitionSig {
+ predicate isRelevantUltimateDefinition(Ssa::Definition def) {
+ isRelevantUltimateDefinition(def, _)
+ }
+ }
+
+ private predicate hasAnUltimateFunctionAccessDefinition(Ssa::Definition def, Function f) {
+ exists(Ssa::Definition ultimate |
+ ultimate =
+ Ssa::GetAnUltimateDefinition::getAnUltimateDefinition(def) and
+ isRelevantUltimateDefinition(ultimate, f)
+ )
+ }
+
+ class SourceSinkReportingElement extends Element {
+ SourceSinkReportingElement() { this instanceof Expr or this instanceof Parameter }
+
+ DataFlowCallable getEnclosingCallable() {
+ result.asSourceCallable() =
+ [this.(Expr).getEnclosingFunction(), this.(Parameter).getFunction()]
+ }
+
+ /** Gets the function invoked when this element is used as a callback. */
+ private Function getCallable() {
+ // The expression is a struct which implements `operator()`.
+ result = getFunctionFromType(this)
+ or
+ // The expression is a function pointer
+ result = getFunctionFromExpr(this)
+ or
+ // The expression is an SSA read of an assignment of a callable
+ exists(Ssa::Definition def |
+ def.getAUse().getDef().getUnconvertedResultExpression() = this and
+ hasAnUltimateFunctionAccessDefinition(def, result)
+ )
+ }
+
+ SourceSinkReportingElement getASuccessor(Impl::Private::SummaryComponent sc) {
+ exists(Function f | f = this.getCallable() |
+ exists(ParameterPosition pos | sc = Impl::Private::SummaryComponent::parameter(pos) |
+ result = pos.getParameter(f)
+ )
+ or
+ exists(ReturnKind rk |
+ sc = Impl::Private::SummaryComponent::return(rk) and
+ isReturnExpr(f, rk, result)
+ )
+ )
+ }
+ }
+
+ bindingset[source, sc]
+ SourceSinkReportingElement getASourceReportingElement(
+ Input::SummarizedCallableBase source, Impl::Private::SummaryComponent sc
+ ) {
+ exists(Call call | call.getTarget() = source |
+ sc = Impl::Private::SummaryComponent::return(_) and
+ result = call
+ or
+ exists(ArgumentPosition pos |
+ sc = Impl::Private::SummaryComponent::argument(pos) and
+ result = pos.getArgument(call)
+ )
+ )
+ or
+ exists(ParameterPosition pos |
+ sc = Impl::Private::SummaryComponent::parameter(pos) and
+ result = pos.getParameter(source)
+ )
+ }
+
+ pragma[nomagic]
+ private IndirectReturnOutNode getIndirectReturn(CallInstruction call, NormalReturnKind rk) {
+ result.getCallInstruction() = call and
+ pragma[only_bind_out](result.getIndirectionIndex()) =
+ pragma[only_bind_out](rk.getIndirectionIndex())
+ }
+
+ pragma[nomagic]
+ private predicate hasKindAndEnclosingFunction(Function f, ReturnKind rk, ReturnNode r) {
+ r.getEnclosingCallable().asSourceCallable() = f and
+ r.getKind() = rk
+ }
+
+ pragma[nomagic]
+ private predicate hasParameterAndIndirectionIndex(
+ Parameter p, int indirectionIndex, ParameterNode n
+ ) {
+ n.getParameter() = p and
+ n.getIndirectionIndex() = indirectionIndex
+ }
+
+ bindingset[e, sc]
+ Node getSourceDataFlowNode(SourceSinkReportingElement e, Impl::Private::SummaryComponent sc) {
+ exists(DataFlowCall call |
+ exists(ArgumentPosition pos |
+ sc = Impl::Private::SummaryComponent::argument(pos) and
+ pos.getArgument(call.asCallInstruction().getUnconvertedResultExpression()) = e
+ |
+ pos.getIndirectionIndex() = 0 and
+ result.(PostUpdateNode).getPreUpdateNode().asExpr() = e
+ or
+ result.(PostUpdateNode).getPreUpdateNode().asIndirectExpr(pos.getIndirectionIndex()) = e
+ )
+ or
+ exists(ReturnKind rk |
+ sc = Impl::Private::SummaryComponent::return(rk) and
+ // When `e` is a call the node becomes an `OutNode`.
+ e = call.asCallInstruction().getUnconvertedResultExpression()
+ |
+ rk.getIndirectionIndex() = 0 and
+ simpleOutNode(result, call.asCallInstruction())
+ or
+ result = getIndirectReturn(call.asCallInstruction(), rk)
+ )
+ )
+ or
+ exists(ParameterPosition pos |
+ sc = Impl::Private::SummaryComponent::parameter(pos) and
+ hasParameterAndIndirectionIndex(e, pos.getIndirectionIndex(), result)
+ )
+ or
+ exists(Function f, ReturnKind rk |
+ sc = Impl::Private::SummaryComponent::return(rk) and
+ // When `e` is the returned expression from a function the node is
+ // the `ReturnNode`.
+ isReturnExpr(f, rk, e) and
+ hasKindAndEnclosingFunction(f, rk, result)
+ )
+ }
+
+ bindingset[sink, sc]
+ SourceSinkReportingElement getASinkReportingElement(
+ Input::SummarizedCallableBase sink, Impl::Private::SummaryComponent sc
+ ) {
+ exists(Call call, ArgumentPosition pos |
+ call.getTarget() = sink and
+ sc = Impl::Private::SummaryComponent::argument(pos) and
+ result = pos.getArgument(call)
+ )
+ }
+
+ bindingset[e, sc]
+ Node getSinkDataFlowNode(SourceSinkReportingElement e, Impl::Private::SummaryComponent sc) {
+ exists(ArgumentPosition pos, CallInstruction call |
+ sc = Impl::Private::SummaryComponent::argument(pos) and
+ pos.getArgument(call.getUnconvertedResultExpression()) = e and
+ result.(ArgumentNode).sourceArgumentOf(call, pos)
+ )
+ or
+ exists(Function f, ReturnKind rk |
+ sc = Impl::Private::SummaryComponent::return(rk) and
+ isReturnExpr(f, rk, e) and
+ hasKindAndEnclosingFunction(f, rk, result)
+ )
}
}
@@ -319,3 +506,45 @@ module Private {
}
module Public = Impl::Public;
+
+private class SourceModelFunction extends Public::SourceElement instanceof Function {
+ private string namespace;
+ private string type;
+ private boolean subtypes;
+ private string name;
+ private string signature;
+ private string ext;
+
+ SourceModelFunction() {
+ sourceModel(namespace, type, subtypes, name, signature, ext, _, _, _, _) and
+ this = interpretElement(namespace, type, subtypes, name, signature, ext)
+ }
+
+ override predicate isSource(
+ string output, string kind, Public::Provenance provenance, boolean isExact, string model
+ ) {
+ sourceModel(namespace, type, subtypes, name, signature, ext, output, kind, provenance, model) and
+ isExact = true
+ }
+}
+
+private class SinkModelFunction extends Public::SinkElement instanceof Function {
+ private string namespace;
+ private string type;
+ private boolean subtypes;
+ private string name;
+ private string signature;
+ private string ext;
+
+ SinkModelFunction() {
+ sinkModel(namespace, type, subtypes, name, signature, ext, _, _, _, _) and
+ this = interpretElement(namespace, type, subtypes, name, signature, ext)
+ }
+
+ override predicate isSink(
+ string input, string kind, Public::Provenance provenance, boolean isExact, string model
+ ) {
+ sinkModel(namespace, type, subtypes, name, signature, ext, input, kind, provenance, model) and
+ isExact = true
+ }
+}
diff --git a/cpp/ql/lib/semmle/code/cpp/exprs/Expr.qll b/cpp/ql/lib/semmle/code/cpp/exprs/Expr.qll
index 851d987b1fd6..82824445ccf2 100644
--- a/cpp/ql/lib/semmle/code/cpp/exprs/Expr.qll
+++ b/cpp/ql/lib/semmle/code/cpp/exprs/Expr.qll
@@ -108,7 +108,8 @@ class Expr extends StmtParent, @expr {
/** Holds if this is an auxiliary expression generated by the compiler. */
predicate isCompilerGenerated() {
compgenerated(underlyingElement(this)) or
- this.getParent().(ConstructorFieldInit).isCompilerGenerated()
+ this.getParent().(ConstructorFieldInit).isCompilerGenerated() or
+ this.getParent().(Initializer).isCompilerGenerated()
}
/**
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll
index bce936552768..03a565ef946d 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll
@@ -131,6 +131,15 @@ private predicate qualifierSourceImpl(RelevantNode n, Class c) {
)
}
+pragma[nomagic]
+private predicate hasKindAndEnclosingCallable(
+ DataFlowPrivate::DataFlowCallable callable, DataFlowPrivate::ReturnKind kind,
+ DataFlowPrivate::ReturnNode return
+) {
+ return.getEnclosingCallable() = callable and
+ return.getKind() = kind
+}
+
private module TrackVirtualDispatch {
/**
* Gets a possible runtime target of `c` using both static call-target
@@ -197,11 +206,21 @@ private module TrackVirtualDispatch {
)
}
+ pragma[nomagic]
+ private predicate hasDispatchWithKind(
+ DataFlowPrivate::DataFlowCallable callable, DataFlowPrivate::ReturnKind kind,
+ LocalSourceNode n2
+ ) {
+ exists(DataFlowPrivate::DataFlowCall call |
+ n2 = DataFlowPrivate::getAnOutNode(call, kind) and
+ callable = dispatch(call)
+ )
+ }
+
predicate returnStep(Node n1, LocalSourceNode n2) {
- exists(DataFlowPrivate::DataFlowCallable callable, DataFlowPrivate::DataFlowCall call |
- n1.(DataFlowPrivate::ReturnNode).getEnclosingCallable() = callable and
- callable = dispatch(call) and
- n2 = DataFlowPrivate::getAnOutNode(call, n1.(DataFlowPrivate::ReturnNode).getKind())
+ exists(DataFlowPrivate::DataFlowCallable callable, DataFlowPrivate::ReturnKind kind |
+ hasKindAndEnclosingCallable(callable, kind, n1) and
+ hasDispatchWithKind(callable, kind, n2)
)
}
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll
index 6b0de326d114..3b900320882b 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll
@@ -6,6 +6,8 @@
private import cpp
private import DataFlowImplSpecific
private import TaintTrackingImplSpecific
+private import DataFlowNodes as Nodes
+private import semmle.code.cpp.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
private import codeql.dataflow.internal.DataFlowImplConsistency
private module Input implements InputSig {
@@ -14,6 +16,12 @@ private module Input implements InputSig {
// complex to model here.
any()
}
+
+ predicate postWithInFlowExclude(CppDataFlow::Node n) {
+ n instanceof Nodes::FlowSummaryNode
+ or
+ FlowSummaryImpl::Private::Steps::summaryLocalStep(_, n, _, _)
+ }
}
module Consistency = MakeConsistency;
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowNodes.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowNodes.qll
index 27f7497422db..541b6d13b149 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowNodes.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowNodes.qll
@@ -1541,6 +1541,43 @@ class FlowSummaryNode extends Node, TFlowSummaryNode {
override Location getLocationImpl() { result = this.getSummaryNode().getLocation() }
override string toStringImpl() { result = this.getSummaryNode().toString() }
+
+ /** Gets the source element that this node belongs to, if any. */
+ FlowSummaryImpl::Public::SourceElement getSourceElement() {
+ result = this.getSummaryNode().getSourceElement()
+ }
+
+ /** Gets the sink element that this node belongs to, if any. */
+ FlowSummaryImpl::Public::SinkElement getSinkElement() {
+ result = this.getSummaryNode().getSinkElement()
+ }
+
+ /** Holds if this node is a source node of kind `kind`. */
+ predicate isSource(string kind, string model) {
+ this.getSummaryNode().(FlowSummaryImpl::Private::SourceOutputNode).isEntry(kind, model)
+ }
+
+ /** Holds if this node is a sink node of kind `kind`. */
+ predicate isSink(string kind, string model) {
+ this.getSummaryNode().(FlowSummaryImpl::Private::SinkInputNode).isExit(kind, model)
+ }
+}
+
+private class SourceOutputNode extends FlowSummaryImpl::Private::SourceOutputNode {
+ final override string toString() {
+ exists(Call call |
+ this.isOutArgument(call) and
+ result = call.getTarget() + " output argument"
+ )
+ or
+ not this.isOutArgument(_) and
+ result = super.toString()
+ }
+
+ private predicate isOutArgument(Call call) {
+ call.getTarget() = this.getSourceElement() and
+ [call.getAnArgument(), call.getQualifier()] = this.getSourceSinkReportingElement()
+ }
}
/**
@@ -1655,13 +1692,13 @@ abstract private class AbstractParameterNode extends Node {
* Holds if this node represents an implicit `this` parameter, if it exists.
*/
predicate isThis() { none() } // overridden by subclasses
-}
-abstract private class AbstractIndirectParameterNode extends AbstractParameterNode {
/** Gets the indirection index of this parameter node. */
- abstract int getIndirectionIndex();
+ int getIndirectionIndex() { none() }
}
+abstract private class AbstractIndirectParameterNode extends AbstractParameterNode { }
+
pragma[noinline]
private predicate indirectParameterNodeHasArgumentIndexAndIndex(
IndirectInstructionParameterNode node, int argumentIndex, int indirectionIndex
@@ -1725,7 +1762,9 @@ private class IndirectInstructionParameterNode extends AbstractIndirectParameter
final override int getIndirectionIndex() { this.hasInstructionAndIndirectionIndex(init, result) }
}
-abstract private class AbstractDirectParameterNode extends AbstractParameterNode { }
+abstract private class AbstractDirectParameterNode extends AbstractParameterNode {
+ override int getIndirectionIndex() { result = 0 }
+}
/**
* A non-indirect parameter node that is represented as an `Instruction`.
@@ -1796,6 +1835,8 @@ private class DirectBodyLessParameterNode extends AbstractExplicitParameterNode,
}
override Parameter getParameter() { result = p }
+
+ final override int getIndirectionIndex() { result = 0 }
}
private class IndirectBodyLessParameterNode extends AbstractIndirectParameterNode,
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll
index df4901b65fe8..3a1b42645642 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll
@@ -508,11 +508,31 @@ predicate isArgumentNode(ArgumentNode arg, DataFlowCall c, ArgumentPosition pos)
* on parameters are also included.
*/
abstract class ArgumentNode extends Node {
+ /**
+ * Holds if this argument occurs at the given position in the given call,
+ * and this call is represented in the source code.
+ * The instance argument is considered to have index `-1`.
+ */
+ predicate sourceArgumentOf(CallInstruction call, ArgumentPosition pos) { none() }
+
+ /**
+ * Holds if this argument occurs at the given position in the given call,
+ * and this call is part of a summary.
+ * The instance argument is considered to have index `-1`.
+ */
+ predicate summaryArgumentOf(FlowSummaryImpl::Public::SummarizedCallable call, ArgumentPosition pos) {
+ none()
+ }
+
/**
* Holds if this argument occurs at the given position in the given call.
* The instance argument is considered to have index `-1`.
*/
- abstract predicate argumentOf(DataFlowCall call, ArgumentPosition pos);
+ final predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
+ this.sourceArgumentOf(call.asCallInstruction(), pos)
+ or
+ this.summaryArgumentOf(call.asSummaryCall(), pos)
+ }
/** Gets the call in which this node is an argument. */
DataFlowCall getCall() { this.argumentOf(result, _) }
@@ -527,16 +547,16 @@ private class PrimaryArgumentNode extends ArgumentNode, OperandNode {
PrimaryArgumentNode() { exists(CallInstruction call | op = call.getAnArgumentOperand()) }
- override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
+ override predicate sourceArgumentOf(CallInstruction call, ArgumentPosition pos) {
op = call.getArgumentOperand(pos.(DirectPosition).getArgumentIndex())
}
}
private class SideEffectArgumentNode extends ArgumentNode, SideEffectOperandNode {
- override predicate argumentOf(DataFlowCall dfCall, ArgumentPosition pos) {
+ override predicate sourceArgumentOf(CallInstruction c, ArgumentPosition pos) {
exists(int indirectionIndex |
pos = TIndirectionPosition(argumentIndex, pragma[only_bind_into](indirectionIndex)) and
- this.getCallInstruction() = dfCall.asCallInstruction() and
+ this.getCallInstruction() = c and
super.hasAddressOperandAndIndirectionIndex(arg, pragma[only_bind_into](indirectionIndex))
)
}
@@ -554,8 +574,10 @@ class SummaryArgumentNode extends ArgumentNode, FlowSummaryNode {
FlowSummaryImpl::Private::summaryArgumentNode(call_.getReceiver(), this.getSummaryNode(), pos_)
}
- override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
- call = call_ and
+ override predicate summaryArgumentOf(
+ FlowSummaryImpl::Public::SummarizedCallable call, ArgumentPosition pos
+ ) {
+ call = call_.asSummaryCall() and
pos = pos_
}
}
@@ -569,8 +591,8 @@ private class FlowSummaryArgumentNode extends ArgumentNode, FlowSummaryNode {
this.getSummaryNode() = FlowSummaryImpl::Private::summaryArgumentNode(callInstruction, rk)
}
- override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
- call.asCallInstruction() = callInstruction and
+ override predicate sourceArgumentOf(CallInstruction call, ArgumentPosition pos) {
+ call = callInstruction and
pos = TFlowSummaryPosition(rk)
}
}
@@ -593,6 +615,32 @@ abstract class Position extends TPosition {
/** Gets the indirection index of this position. */
abstract int getIndirectionIndex();
+
+ /**
+ * Gets the parameter of `f` associated with this position, if any.
+ *
+ * Since a `Position` is defined by both an argument index and an
+ * indirection multiple `Position`s can be associated with the
+ * same `Parameter`.
+ */
+ Parameter getParameter(Function f) {
+ result.getFunction() = f and
+ this.getArgumentIndex() = result.getIndex()
+ }
+
+ /**
+ * Gets the argument (or qualifier) of `call` associated with this position, if any.
+ *
+ * Since a `Position` is defined by both an argument index and an
+ * indirection multiple `Position`s can be associated with the
+ * same argument/qualifier.
+ */
+ Expr getArgument(Cpp::Call call) {
+ result = call.getArgument(this.getArgumentIndex())
+ or
+ this.getArgumentIndex() = -1 and
+ result = call.getQualifier()
+ }
}
class DirectPosition extends Position, TDirectPosition {
@@ -1189,6 +1237,11 @@ class DataFlowCall extends TDataFlowCall {
*/
CallInstruction asCallInstruction() { none() }
+ /**
+ * Gets the underlying summarized call, if any.
+ */
+ FlowSummaryImpl::Public::SummarizedCallable asSummaryCall() { none() }
+
/**
* Gets the operand the specifies the target function of the call.
*/
@@ -1306,6 +1359,8 @@ class SummaryCall extends DataFlowCall, TSummaryCall {
*/
FlowSummaryImpl::Private::SummaryNode getReceiver() { result = receiver }
+ final override FlowSummaryImpl::Public::SummarizedCallable asSummaryCall() { result = c }
+
// no implementation for `getCallTargetOperand()`, `getStaticCallTarget()`
// or `getArgumentOperand(int index)`. This is because the flow summary
// library is responsible for finding the call target, and there are no
@@ -1922,13 +1977,23 @@ module IteratorFlow {
}
/**
- * Gets an ultimate definition of `def`.
- *
- * Note: Unlike `def.getAnUltimateDefinition()` this predicate also
- * traverses back through iterator increment and decrement operations.
+ * Holds if `write` is an instruction that writes to address `address`
*/
- private Ssa::Definition getAnUltimateDefinition(Ssa::Definition def) {
- result = def.getAnUltimateDefinition()
+ private predicate isIteratorWrite(Instruction write, Operand address) {
+ exists(Ssa::DefImpl writeDef, IRBlock bb, int i |
+ writeDef.hasIndexInBlock(_, bb, i) and
+ bb.getInstruction(i) = write and
+ address = writeDef.getAddressOperand()
+ )
+ }
+
+ private module GetAnUltimateDefinitionInput implements Ssa::GetAnUltimateDefinitionSig {
+ predicate isRelevantUltimateDefinition(Ssa::Definition def) { fwd(_, def) }
+ }
+
+ private Ssa::Definition getAnUltimateDefinitionStep(Ssa::Definition def) {
+ result =
+ Ssa::GetAnUltimateDefinition::getAnUltimateDefinition(def)
or
exists(IRBlock bb, int i, IteratorCrementCall crementCall, Ssa::SourceVariable sv |
crementCall = def.getValue().asInstruction().(StoreInstruction).getSourceValue() and
@@ -1938,14 +2003,28 @@ module IteratorFlow {
)
}
- /**
- * Holds if `write` is an instruction that writes to address `address`
- */
- private predicate isIteratorWrite(Instruction write, Operand address) {
- exists(Ssa::DefImpl writeDef, IRBlock bb, int i |
- writeDef.hasIndexInBlock(_, bb, i) and
- bb.getInstruction(i) = write and
- address = writeDef.getAddressOperand()
+ private predicate isSource(GetsIteratorCall beginCall, Ssa::Definition def) {
+ exists(StoreInstruction beginStore |
+ beginStore = def.getValue().asInstruction() and
+ operandForFullyConvertedCall(beginStore.getSourceValueOperand(), beginCall)
+ )
+ }
+
+ private predicate isSink(Instruction writeToDeref, Ssa::Definition def) {
+ exists(IteratorPointerDereferenceCall starCall, Operand address, IRBlock bbStar, int iStar |
+ isIteratorWrite(writeToDeref, address) and
+ operandForFullyConvertedCall(address, starCall) and
+ bbStar.getInstruction(iStar) = starCall and
+ Ssa::ssaDefReachesRead(_, def, bbStar, iStar)
+ )
+ }
+
+ private predicate fwd(GetsIteratorCall beginCall, Ssa::Definition def) {
+ isSource(beginCall, def)
+ or
+ exists(Ssa::Definition def0 |
+ fwd(beginCall, def0) and
+ def0 = getAnUltimateDefinitionStep(def)
)
}
@@ -1961,17 +2040,9 @@ module IteratorFlow {
private predicate isIteratorStoreInstruction(
GetsIteratorCall beginCall, Instruction writeToDeref
) {
- exists(
- StoreInstruction beginStore, IRBlock bbStar, int iStar, Ssa::Definition def,
- IteratorPointerDereferenceCall starCall, Ssa::Definition ultimate, Operand address
- |
- isIteratorWrite(writeToDeref, address) and
- operandForFullyConvertedCall(address, starCall) and
- bbStar.getInstruction(iStar) = starCall and
- Ssa::ssaDefReachesRead(_, def, bbStar, iStar) and
- ultimate = getAnUltimateDefinition*(def) and
- beginStore = ultimate.getValue().asInstruction() and
- operandForFullyConvertedCall(beginStore.getSourceValueOperand(), beginCall)
+ exists(Ssa::Definition def |
+ fwd(beginCall, def) and
+ isSink(writeToDeref, def)
)
}
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll
index 13d16375f236..17c3438f098a 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll
@@ -1018,4 +1018,6 @@ module Ssa {
class IndirectExplicitDefinition = SsaImpl::IndirectExplicitDefinition;
class PhiNode = SsaImpl::PhiNode;
+
+ import SsaImpl::Public
}
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImpl.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImpl.qll
index 432261dfe278..38fbf87a403b 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImpl.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImpl.qll
@@ -1405,14 +1405,96 @@ private class PhiCycle extends PhiCycleEquivalence::EquivalenceClass {
}
}
-/** An static single assignment (SSA) definition. */
-class Definition extends SsaImpl::Definition {
- private Definition getAPhiInputOrPriorDefinition() {
- result = this.(PhiNode).getAnInput()
- or
- uncertainWriteDefinitionInput(this, result)
+private Definition getAPhiInputOrPriorDefinition(Definition def) {
+ result = def.(PhiNode).getAnInput()
+ or
+ uncertainWriteDefinitionInput(def, result)
+}
+
+module Public {
+ /**
+ * A module signature to define relevant ultimate definitions for an
+ * optimized version of `Definition.getAnUltimateDefinition`.
+ */
+ signature module GetAnUltimateDefinitionSig {
+ /**
+ * Holds if `def` is a relevant definition. This defines the set
+ * of `Definition`s which may be returned by
+ * `GetAnUltimateDefinition::getAnUltimateDefinition`.
+ */
+ predicate isRelevantUltimateDefinition(Definition def);
+ }
+
+ /**
+ * A module which constructs an optimized version of
+ * ```
+ * Definition.getAnUltimateDefinition
+ * ```
+ * by restricting the set of possible ultimate definitions.
+ *
+ * Use this module by defining a module `M` which implements
+ * `GetAnUltimateDefinitionSig` and then call:
+ * ```
+ * GetAnUltimateDefinition::getAnUltimateDefinition
+ * ```
+ */
+ module GetAnUltimateDefinition {
+ private import Sig
+
+ private predicate relevantUltimateDefinition(Definition def) {
+ isRelevantUltimateDefinition(def) and
+ not def instanceof PhiNode
+ }
+
+ /**
+ * The `getAnUltimateDefinition` predicate uses an optimized step relation
+ * which is pruned to only those uncertain steps which lead back to a
+ * definition which satisfies `relevantUltimateDefinition`. This predicate
+ * computes the subset of `Definition`s which can lead back to definitions
+ * which satisfy `relevantUltimateDefinition`.
+ */
+ private predicate fwd(Definition def) {
+ // Base case: This definition is a relevant definition
+ relevantUltimateDefinition(def)
+ or
+ exists(Definition def0 |
+ // Recursive case: `def0` is a relevant definition, and
+ // `def` is an uncertain step which takes us back to `def0`.
+ fwd(def0) and
+ def0 = getAPhiInputOrPriorDefinition(def)
+ )
+ }
+
+ /**
+ * Holds if `def1 = getAPhiInputOrPriorDefinition(def2)`, and
+ * both `def1` and `def2` are part of a sequence of uncertain
+ * steps which lead back to a `Definition` which
+ * satisfies `relevantUltimateDefinition`.
+ */
+ private predicate step(Definition def1, Definition def2) {
+ fwd(def1) and
+ fwd(def2) and
+ def1 = getAPhiInputOrPriorDefinition(def2)
+ }
+
+ /**
+ * Gets a definition that ultimately defines this SSA definition and is
+ * not itself a phi node.
+ *
+ * This predicate is restricted to ultimate definitions which
+ * satisfy `isRelevantUltimateDefinition`.
+ */
+ Definition getAnUltimateDefinition(Definition def) {
+ step*(result, def) and
+ relevantUltimateDefinition(result)
+ }
}
+}
+
+import Public
+/** A static single assignment (SSA) definition. */
+class Definition extends SsaImpl::Definition {
/**
* Holds if this SSA definition is live at the end of basic block `bb`.
* That is, this definition reaches the end of basic block `bb`, at which
@@ -1424,9 +1506,13 @@ class Definition extends SsaImpl::Definition {
/**
* Gets a definition that ultimately defines this SSA definition and is
* not itself a phi node.
+ *
+ * Note: A more efficient implementation of this predicate exists. See the
+ * `GetAnUltimateDefinition` module for a description of how to access
+ * the more efficient implementation.
*/
final Definition getAnUltimateDefinition() {
- result = this.getAPhiInputOrPriorDefinition*() and
+ result = getAPhiInputOrPriorDefinition*(this) and
not result instanceof PhiNode
}
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll
index 522cd393081e..59ee08973e1a 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/AliasedSSA.qll
@@ -295,6 +295,11 @@ abstract class MemoryLocation0 extends TMemoryLocation {
*/
abstract class VirtualVariable extends MemoryLocation0 { }
+pragma[nomagic]
+private VirtualVariable getAllocationMemoryLocation(Allocation alloc) {
+ result.getAnAllocation() = alloc
+}
+
abstract class AllocationMemoryLocation extends MemoryLocation0 {
Allocation var;
boolean isMayAccess;
@@ -313,7 +318,7 @@ abstract class AllocationMemoryLocation extends MemoryLocation0 {
result = getGroupedMemoryLocation(var, false, false).getVirtualVariable()
or
not exists(getGroupedMemoryLocation(var, false, false)) and
- result.(AllocationMemoryLocation).getAnAllocation() = var
+ result = getAllocationMemoryLocation(var)
)
}
@@ -815,6 +820,7 @@ private predicate isRelatableMemoryLocation(VariableMemoryLocation vml) {
vml.getStartBitOffset() != Ints::unknown()
}
+pragma[no_dynamic_join_order]
private predicate isCoveredOffset(Allocation var, int offsetRank, VariableMemoryLocation vml) {
exists(int startRank, int endRank, VirtualVariable vvar |
vml.getStartBitOffset() = rank[startRank](IntValue offset_ | isRelevantOffset(vvar, offset_)) and
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedInitialization.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedInitialization.qll
index 10c033131225..c24cb98d2bd9 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedInitialization.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedInitialization.qll
@@ -618,6 +618,11 @@ class TranslatedExplicitFieldInitialization extends TranslatedNonDefaultFieldIni
override int getPosition() { result = position }
}
+pragma[nomagic]
+private Instruction getCallInstruction(TranslatedDefaultFieldInitialization tdfi) {
+ result = tdfi.getInstruction(CallTag())
+}
+
/**
* The IR translation of the initialization of a field from an element of an initializer
* list where default initialization is used.
@@ -642,7 +647,7 @@ class TranslatedDefaultFieldInitialization extends TranslatedFieldInitialization
override Instruction getInstructionSuccessorInternal(InstructionTag tag, EdgeKind kind) {
tag = CallTargetTag() and
- result = this.getInstruction(CallTag())
+ result = getCallInstruction(this)
or
tag = CallTag() and
result = this.getSideEffects().getFirstInstruction(kind)
diff --git a/cpp/ql/src/Best Practices/Exceptions/CatchingByValue.qhelp b/cpp/ql/src/Best Practices/Exceptions/CatchingByValue.qhelp
index 3c60845c4a54..82bed0bc088e 100644
--- a/cpp/ql/src/Best Practices/Exceptions/CatchingByValue.qhelp
+++ b/cpp/ql/src/Best Practices/Exceptions/CatchingByValue.qhelp
@@ -43,7 +43,7 @@ void good() {
C++ FAQ:
What should I throw?,
What should I catch?.
- Wikibooks:
+ Wikibooks:
Throwing objects.
diff --git a/cpp/ql/src/Best Practices/Exceptions/ThrowingPointers.qhelp b/cpp/ql/src/Best Practices/Exceptions/ThrowingPointers.qhelp
index a05903bd350e..cbbe423c647d 100644
--- a/cpp/ql/src/Best Practices/Exceptions/ThrowingPointers.qhelp
+++ b/cpp/ql/src/Best Practices/Exceptions/ThrowingPointers.qhelp
@@ -36,7 +36,7 @@ void good() {
C++ FAQ:
What should I throw?,
What should I catch?.
- Wikibooks:
+ Wikibooks:
Throwing objects.
diff --git a/cpp/ql/src/Best Practices/Likely Errors/OffsetUseBeforeRangeCheck.qhelp b/cpp/ql/src/Best Practices/Likely Errors/OffsetUseBeforeRangeCheck.qhelp
index 6ffb30be5840..d12e1821b2d7 100644
--- a/cpp/ql/src/Best Practices/Likely Errors/OffsetUseBeforeRangeCheck.qhelp
+++ b/cpp/ql/src/Best Practices/Likely Errors/OffsetUseBeforeRangeCheck.qhelp
@@ -21,7 +21,7 @@
cplusplus.com:
C++: array.
-Wikipedia:
+Wikipedia:
Bounds checking.
diff --git a/cpp/ql/src/Best Practices/Likely Errors/Slicing.qhelp b/cpp/ql/src/Best Practices/Likely Errors/Slicing.qhelp
index 576d77c8839c..282f592e0f61 100644
--- a/cpp/ql/src/Best Practices/Likely Errors/Slicing.qhelp
+++ b/cpp/ql/src/Best Practices/Likely Errors/Slicing.qhelp
@@ -20,7 +20,7 @@ These assignments slice off all the fields added by the derived type, and can ca
- Wikipedia: Object slicing.
+ Wikipedia: Object slicing.
DevX.com: Slicing in C++.
diff --git a/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsNumbers.qhelp b/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsNumbers.qhelp
index aa97965996f2..4a5e0cff636e 100644
--- a/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsNumbers.qhelp
+++ b/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsNumbers.qhelp
@@ -35,7 +35,7 @@ then replace all the relevant occurrences in the code.
-Magic number (Wikipedia)
+Magic number (Wikipedia)
Mats Henricson and Erik Nyquist, Industrial Strength C++, published by Prentice Hall PTR (1997).
diff --git a/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsString.qhelp b/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsString.qhelp
index 19182fe5b19d..c1ccb94f59ea 100644
--- a/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsString.qhelp
+++ b/cpp/ql/src/Best Practices/Magic Constants/MagicConstantsString.qhelp
@@ -34,7 +34,7 @@ constant.
-Magic string (Wikipedia)
+Magic string (Wikipedia)
Mats Henricson and Erik Nyquist, Industrial Strength C++, published by Prentice Hall PTR (1997).
diff --git a/cpp/ql/src/Best Practices/RuleOfThree.qhelp b/cpp/ql/src/Best Practices/RuleOfThree.qhelp
index f9ac2b396b9b..1c8eace82f76 100644
--- a/cpp/ql/src/Best Practices/RuleOfThree.qhelp
+++ b/cpp/ql/src/Best Practices/RuleOfThree.qhelp
@@ -15,6 +15,6 @@
-Wikipedia: Rule of three (C++ programming)
+Wikipedia: Rule of three (C++ programming)
diff --git a/cpp/ql/src/Best Practices/RuleOfTwo.qhelp b/cpp/ql/src/Best Practices/RuleOfTwo.qhelp
index b8d4e13d283e..167740b03d97 100644
--- a/cpp/ql/src/Best Practices/RuleOfTwo.qhelp
+++ b/cpp/ql/src/Best Practices/RuleOfTwo.qhelp
@@ -38,7 +38,7 @@ should never be called.
- Rule of Three [Wikipedia]
+ Rule of Three [Wikipedia]
The Law of The Big Two
diff --git a/cpp/ql/src/CHANGELOG.md b/cpp/ql/src/CHANGELOG.md
index 9a8c1243217c..de14ac42a586 100644
--- a/cpp/ql/src/CHANGELOG.md
+++ b/cpp/ql/src/CHANGELOG.md
@@ -1,3 +1,9 @@
+## 1.8.3
+
+### Minor Analysis Improvements
+
+* The `cpp/leap-year/unsafe-array-for-days-of-the-year` query ("Unsafe array for days of the year") no longer reports an alert on the `__PRETTY_FUNCTION__` variable (and related variables) when the enclosing function has a signature that is exactly 364 characters.
+
## 1.8.2
No user-facing changes.
diff --git a/cpp/ql/src/Documentation/DocumentApi.qhelp b/cpp/ql/src/Documentation/DocumentApi.qhelp
index 4154e5cebc4e..80225102cc9b 100644
--- a/cpp/ql/src/Documentation/DocumentApi.qhelp
+++ b/cpp/ql/src/Documentation/DocumentApi.qhelp
@@ -22,10 +22,10 @@ Add comments to document the purpose of the function. In particular, ensure that
- C++ Programming Wikibook: Comments
+ C++ Programming Wikibook: Comments
- Wikipedia: Need for comments
+ Wikipedia: Need for comments
diff --git a/cpp/ql/src/Documentation/FixmeComments.qhelp b/cpp/ql/src/Documentation/FixmeComments.qhelp
index a4a0b9966a15..a884aee2bc8b 100644
--- a/cpp/ql/src/Documentation/FixmeComments.qhelp
+++ b/cpp/ql/src/Documentation/FixmeComments.qhelp
@@ -25,7 +25,7 @@ Fix the functionality indicated by the comment. If the comment no longer applies
- Wikipedia: Comment tags
+ Wikipedia: Comment tags
The case against TODO (and FIXME)
diff --git a/cpp/ql/src/Documentation/TodoComments.qhelp b/cpp/ql/src/Documentation/TodoComments.qhelp
index ad246ebf358f..30eb359cd972 100644
--- a/cpp/ql/src/Documentation/TodoComments.qhelp
+++ b/cpp/ql/src/Documentation/TodoComments.qhelp
@@ -25,7 +25,7 @@ Implement the functionality indicated by the comment. If the comment no longer a
- Wikipedia: Comment tags
+ Wikipedia: Comment tags
TODO or not TODO
diff --git a/cpp/ql/src/Documentation/UncommentedFunction.qhelp b/cpp/ql/src/Documentation/UncommentedFunction.qhelp
index 7fe5c61d9da3..1604ca496de3 100644
--- a/cpp/ql/src/Documentation/UncommentedFunction.qhelp
+++ b/cpp/ql/src/Documentation/UncommentedFunction.qhelp
@@ -23,10 +23,10 @@ cohesive functions.
- C++ Programming, Coding style conventions
+ C++ Programming, Coding style conventions
- Wikipedia: Need for comments
+ Wikipedia: Need for comments
diff --git a/cpp/ql/src/Header Cleanup/Cleanup-DuplicateIncludeGuard.qhelp b/cpp/ql/src/Header Cleanup/Cleanup-DuplicateIncludeGuard.qhelp
index 20cb7ebe94fc..43e11ff20e8c 100644
--- a/cpp/ql/src/Header Cleanup/Cleanup-DuplicateIncludeGuard.qhelp
+++ b/cpp/ql/src/Header Cleanup/Cleanup-DuplicateIncludeGuard.qhelp
@@ -44,7 +44,7 @@ the second file, for example to ANOTHER_HEADER_FILE_H.
- Wikipedia: Include guard
+ Wikipedia: Include guard
diff --git a/cpp/ql/src/Likely Bugs/Arithmetic/BadCheckOdd.qhelp b/cpp/ql/src/Likely Bugs/Arithmetic/BadCheckOdd.qhelp
index 13df1938bc3a..6d36cc6230f2 100644
--- a/cpp/ql/src/Likely Bugs/Arithmetic/BadCheckOdd.qhelp
+++ b/cpp/ql/src/Likely Bugs/Arithmetic/BadCheckOdd.qhelp
@@ -26,7 +26,7 @@ As a result, this check incorrectly considers all negative numbers as even.
MSDN Library: Multiplicative Operators and the Modulus Operator.
- Wikipedia: Modulo Operation - Common pitfalls.
+ Wikipedia: Modulo Operation - Common pitfalls.
diff --git a/cpp/ql/src/Likely Bugs/Leap Year/UnsafeArrayForDaysOfYear.ql b/cpp/ql/src/Likely Bugs/Leap Year/UnsafeArrayForDaysOfYear.ql
index b27db937b577..e6b1ccaa6687 100644
--- a/cpp/ql/src/Likely Bugs/Leap Year/UnsafeArrayForDaysOfYear.ql
+++ b/cpp/ql/src/Likely Bugs/Leap Year/UnsafeArrayForDaysOfYear.ql
@@ -26,6 +26,7 @@ where
or
exists(Variable var |
var = element and
+ not var.isCompilerGenerated() and
var.getType() instanceof LeapYearUnsafeDaysOfTheYearArrayType and
allocType = "an array allocation"
)
diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/AssignWhereCompareMeant.qhelp b/cpp/ql/src/Likely Bugs/Likely Typos/AssignWhereCompareMeant.qhelp
index 91ae1ce665b1..bef47aecc51c 100644
--- a/cpp/ql/src/Likely Bugs/Likely Typos/AssignWhereCompareMeant.qhelp
+++ b/cpp/ql/src/Likely Bugs/Likely Typos/AssignWhereCompareMeant.qhelp
@@ -30,7 +30,7 @@ An assignment is only flagged if its right hand side is a compile-time constant.
Tutorialspoint - The C++ Programming Language: Operators in C++
- Wikipedia: Operators in C and C++
+ Wikipedia: Operators in C and C++
diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/CompareWhereAssignMeant.qhelp b/cpp/ql/src/Likely Bugs/Likely Typos/CompareWhereAssignMeant.qhelp
index 6c1921cda991..437db4cb05c1 100644
--- a/cpp/ql/src/Likely Bugs/Likely Typos/CompareWhereAssignMeant.qhelp
+++ b/cpp/ql/src/Likely Bugs/Likely Typos/CompareWhereAssignMeant.qhelp
@@ -28,7 +28,7 @@ The rule flags every occurrence of an equality operator in a position where its
Tutorialspoint - The C++ Programming Language: Operators in C++
- Wikipedia: Operators in C and C++
+ Wikipedia: Operators in C and C++
diff --git a/cpp/ql/src/Likely Bugs/OO/IncorrectConstructorDelegation.qhelp b/cpp/ql/src/Likely Bugs/OO/IncorrectConstructorDelegation.qhelp
index 88af1203ef9e..3c3c615cd1d6 100644
--- a/cpp/ql/src/Likely Bugs/OO/IncorrectConstructorDelegation.qhelp
+++ b/cpp/ql/src/Likely Bugs/OO/IncorrectConstructorDelegation.qhelp
@@ -60,7 +60,7 @@ public:
Dr Dobb's Journal:
Delegating constructors?
- Wikipedia:
+ Wikipedia:
Object construction improvement in C++11.
diff --git a/cpp/ql/src/Metrics/Files/FCommentRatio.qhelp b/cpp/ql/src/Metrics/Files/FCommentRatio.qhelp
index 4a5c928f5cd4..33829320b3e1 100644
--- a/cpp/ql/src/Metrics/Files/FCommentRatio.qhelp
+++ b/cpp/ql/src/Metrics/Files/FCommentRatio.qhelp
@@ -18,10 +18,10 @@ documenting the public functions first.
- C++ Programming, Coding style conventions
+ C++ Programming, Coding style conventions
- Wikipedia: Need for comments
+ Wikipedia: Need for comments
diff --git a/cpp/ql/src/Metrics/Files/FCyclomaticComplexity.qhelp b/cpp/ql/src/Metrics/Files/FCyclomaticComplexity.qhelp
index be463857db9a..6bd6e7b47183 100644
--- a/cpp/ql/src/Metrics/Files/FCyclomaticComplexity.qhelp
+++ b/cpp/ql/src/Metrics/Files/FCyclomaticComplexity.qhelp
@@ -67,10 +67,10 @@ T. J. McCabe, A Complexity Measure. IEEE Transactions on Software Engin
Dave Thomas, Refactoring as Meta Programming?, in Journal of Object Technology, vol. 4, no. 1, January-February 2005, pp. 7-11.
-Wikipedia: Cyclomatic complexity
+Wikipedia: Cyclomatic complexity
-Wikipedia: Code refactoring
+Wikipedia: Code refactoring
diff --git a/cpp/ql/src/Metrics/Files/FLinesOfCode.qhelp b/cpp/ql/src/Metrics/Files/FLinesOfCode.qhelp
index 4eb6c1c5b009..b8dfc4e70f24 100644
--- a/cpp/ql/src/Metrics/Files/FLinesOfCode.qhelp
+++ b/cpp/ql/src/Metrics/Files/FLinesOfCode.qhelp
@@ -19,7 +19,7 @@
M. Fowler. Refactoring. Addison-Wesley, 1999.
- Wikipedia: Code refactoring
+ Wikipedia: Code refactoring
Refactoring as Meta Programming?
diff --git a/cpp/ql/src/Metrics/Files/FLinesOfComments.qhelp b/cpp/ql/src/Metrics/Files/FLinesOfComments.qhelp
index d1b1d9849181..ed7aabe9971c 100644
--- a/cpp/ql/src/Metrics/Files/FLinesOfComments.qhelp
+++ b/cpp/ql/src/Metrics/Files/FLinesOfComments.qhelp
@@ -18,10 +18,10 @@ should be given to long files.
- C++ Programming, Coding style conventions
+ C++ Programming, Coding style conventions
- Wikipedia: Need for comments
+ Wikipedia: Need for comments
diff --git a/cpp/ql/src/Metrics/Files/FTodoComments.qhelp b/cpp/ql/src/Metrics/Files/FTodoComments.qhelp
index a0906beb799e..7eb47a72c156 100644
--- a/cpp/ql/src/Metrics/Files/FTodoComments.qhelp
+++ b/cpp/ql/src/Metrics/Files/FTodoComments.qhelp
@@ -21,7 +21,7 @@ for fixing them.
- Wikipedia: Comment tags
+ Wikipedia: Comment tags
TODO or not TODO
diff --git a/cpp/ql/src/Metrics/Functions/FunLinesOfComments.qhelp b/cpp/ql/src/Metrics/Functions/FunLinesOfComments.qhelp
index 6232b6bd4418..05728eb3ad33 100644
--- a/cpp/ql/src/Metrics/Functions/FunLinesOfComments.qhelp
+++ b/cpp/ql/src/Metrics/Functions/FunLinesOfComments.qhelp
@@ -18,10 +18,10 @@ should be given to long, complex functions.
- C++ Programming, Coding style conventions
+ C++ Programming, Coding style conventions
- Wikipedia: Need for comments
+ Wikipedia: Need for comments
diff --git a/cpp/ql/src/Metrics/Functions/FunPercentageOfComments.qhelp b/cpp/ql/src/Metrics/Functions/FunPercentageOfComments.qhelp
index 5f998e3e476d..92bb1dc7e41f 100644
--- a/cpp/ql/src/Metrics/Functions/FunPercentageOfComments.qhelp
+++ b/cpp/ql/src/Metrics/Functions/FunPercentageOfComments.qhelp
@@ -17,10 +17,10 @@ documentation.
- C++ Programming, Coding style conventions
+ C++ Programming, Coding style conventions
- Wikipedia: Need for comments
+ Wikipedia: Need for comments
diff --git a/cpp/ql/src/Security/CWE/CWE-079/CgiXss.qhelp b/cpp/ql/src/Security/CWE/CWE-079/CgiXss.qhelp
index 4ad7a40fed60..bfbc7c32746b 100644
--- a/cpp/ql/src/Security/CWE/CWE-079/CgiXss.qhelp
+++ b/cpp/ql/src/Security/CWE/CWE-079/CgiXss.qhelp
@@ -38,7 +38,7 @@ OWASP:
(Cross Site Scripting) Prevention Cheat Sheet.
-Wikipedia: Cross-site scripting.
+Wikipedia: Cross-site scripting.
IETF Tools: The Common Gateway Specification (CGI).
diff --git a/cpp/ql/src/Security/CWE/CWE-089/SqlTainted.ql b/cpp/ql/src/Security/CWE/CWE-089/SqlTainted.ql
index 802888be271a..eb5d23b5fd6a 100644
--- a/cpp/ql/src/Security/CWE/CWE-089/SqlTainted.ql
+++ b/cpp/ql/src/Security/CWE/CWE-089/SqlTainted.ql
@@ -33,15 +33,20 @@ Expr asSinkExpr(DataFlow::Node node) {
result = node.asExpr()
}
+private predicate isSink(DataFlow::Node sink, string extraText) {
+ exists(SqlLikeFunction runSql, string callChain |
+ runSql.outermostWrapperFunctionCall(asSinkExpr(sink), callChain) and
+ extraText = " and then passed to " + callChain
+ )
+ or
+ // sink defined using models-as-data
+ sinkNode(sink, "sql-injection") and extraText = ""
+}
+
module SqlTaintedConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node node) { node instanceof FlowSource }
- predicate isSink(DataFlow::Node node) {
- exists(SqlLikeFunction runSql | runSql.outermostWrapperFunctionCall(asSinkExpr(node), _))
- or
- // sink defined using models-as-data
- sinkNode(node, "sql-injection")
- }
+ predicate isSink(DataFlow::Node node) { isSink(node, _) }
predicate isBarrier(DataFlow::Node node) {
node.asExpr().getUnspecifiedType() instanceof IntegralType
@@ -57,32 +62,17 @@ module SqlTaintedConfig implements DataFlow::ConfigSig {
}
predicate observeDiffInformedIncrementalMode() { any() }
-
- Location getASelectedSinkLocation(DataFlow::Node sink) {
- exists(Expr taintedArg | result = [taintedArg.getLocation(), sink.getLocation()] |
- taintedArg = asSinkExpr(sink)
- )
- }
}
module SqlTainted = TaintTracking::Global;
from
- Expr taintedArg, FlowSource taintSource, SqlTainted::PathNode sourceNode,
- SqlTainted::PathNode sinkNode, string extraText
+ FlowSource taintSource, SqlTainted::PathNode sourceNode, SqlTainted::PathNode sinkNode,
+ string extraText
where
- (
- exists(SqlLikeFunction runSql, string callChain |
- runSql.outermostWrapperFunctionCall(taintedArg, callChain) and
- extraText = " and then passed to " + callChain
- )
- or
- sinkNode(sinkNode.getNode(), "sql-injection") and
- extraText = ""
- ) and
SqlTainted::flowPath(sourceNode, sinkNode) and
- taintedArg = asSinkExpr(sinkNode.getNode()) and
+ isSink(sinkNode.getNode(), extraText) and
taintSource = sourceNode.getNode()
-select taintedArg, sourceNode, sinkNode,
+select sinkNode.getNode(), sourceNode, sinkNode,
"This argument to a SQL query function is derived from $@" + extraText + ".", taintSource,
"user input (" + taintSource.getSourceType() + ")"
diff --git a/cpp/ql/src/Security/CWE/CWE-676/DangerousFunctionOverflow.qhelp b/cpp/ql/src/Security/CWE/CWE-676/DangerousFunctionOverflow.qhelp
index 17b34f1f37e7..72a559d43e2b 100644
--- a/cpp/ql/src/Security/CWE/CWE-676/DangerousFunctionOverflow.qhelp
+++ b/cpp/ql/src/Security/CWE/CWE-676/DangerousFunctionOverflow.qhelp
@@ -36,7 +36,7 @@ rules for the following CWEs:
-Wikipedia: Morris worm.
+Wikipedia: Morris worm.
E. Spafford. The Internet Worm Program: An Analysis. Purdue Technical Report CSD-TR-823, (online), 1988.
diff --git a/cpp/ql/src/change-notes/released/1.8.3.md b/cpp/ql/src/change-notes/released/1.8.3.md
new file mode 100644
index 000000000000..46bddcbc40ea
--- /dev/null
+++ b/cpp/ql/src/change-notes/released/1.8.3.md
@@ -0,0 +1,5 @@
+## 1.8.3
+
+### Minor Analysis Improvements
+
+* The `cpp/leap-year/unsafe-array-for-days-of-the-year` query ("Unsafe array for days of the year") no longer reports an alert on the `__PRETTY_FUNCTION__` variable (and related variables) when the enclosing function has a signature that is exactly 364 characters.
diff --git a/cpp/ql/src/codeql-pack.release.yml b/cpp/ql/src/codeql-pack.release.yml
index 559af8348bb0..8071ef421ab4 100644
--- a/cpp/ql/src/codeql-pack.release.yml
+++ b/cpp/ql/src/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 1.8.2
+lastReleaseVersion: 1.8.3
diff --git a/cpp/ql/src/jsf/4.10 Classes/AV Rule 79.qhelp b/cpp/ql/src/jsf/4.10 Classes/AV Rule 79.qhelp
index a5b94460d43b..aa58cc17cbc7 100644
--- a/cpp/ql/src/jsf/4.10 Classes/AV Rule 79.qhelp
+++ b/cpp/ql/src/jsf/4.10 Classes/AV Rule 79.qhelp
@@ -87,7 +87,7 @@ to a straightforward RAII pattern. This can be achieved in several steps:
S. Meyers. Effective C++ 3d ed. pp 61-66. Addison-Wesley Professional, 2005.
- Resource Acquisition Is Initialization
+ Resource Acquisition Is Initialization
diff --git a/cpp/ql/src/qlpack.yml b/cpp/ql/src/qlpack.yml
index b365b8a398c3..034523449c24 100644
--- a/cpp/ql/src/qlpack.yml
+++ b/cpp/ql/src/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/cpp-queries
-version: 1.8.3-dev
+version: 1.8.4-dev
groups:
- cpp
- queries
diff --git a/cpp/ql/test/library-tests/compiler_generated/compilerGenerated.expected b/cpp/ql/test/library-tests/compiler_generated/compilerGenerated.expected
index b61085ff7c69..c78e4af1d6a4 100644
--- a/cpp/ql/test/library-tests/compiler_generated/compilerGenerated.expected
+++ b/cpp/ql/test/library-tests/compiler_generated/compilerGenerated.expected
@@ -16,5 +16,31 @@
| cpp.cpp:15:5:15:12 | call to ~MyClass | Expr |
| cpp.cpp:15:12:15:12 | reuse of m | Expr |
| cpp.cpp:16:1:16:1 | return ... | Stmt |
+| cpp.cpp:19:26:19:44 | __PRETTY_FUNCTION__ | Variable |
+| cpp.cpp:19:26:19:44 | array to pointer conversion | Expr |
+| cpp.cpp:19:26:19:44 | initializer for __PRETTY_FUNCTION__ | Initializer |
+| cpp.cpp:19:26:19:44 | void uses_pretty_function() | Expr |
+| cpp.cpp:20:1:20:1 | return ... | Stmt |
+| cpp.cpp:23:29:23:32 | args | Variable |
+| cpp.cpp:23:37:23:37 | return ... | Stmt |
+| cpp.cpp:23:37:23:37 | return ... | Stmt |
+| cpp.cpp:27:1:27:1 | return ... | Stmt |
+| cpp.cpp:31:5:31:5 | (__begin) | Variable |
+| cpp.cpp:31:5:31:5 | (__end) | Variable |
+| cpp.cpp:31:5:31:5 | (__range) | Variable |
+| cpp.cpp:31:5:33:5 | declaration | Stmt |
+| cpp.cpp:31:5:33:5 | declaration | Stmt |
+| cpp.cpp:31:5:33:5 | declaration | Stmt |
+| cpp.cpp:31:17:31:18 | (reference to) | Expr |
+| cpp.cpp:34:1:34:1 | return ... | Stmt |
+| file://:0:0:0:0 | (__begin) | Expr |
+| file://:0:0:0:0 | (__begin) | Expr |
+| file://:0:0:0:0 | (__begin) | Expr |
+| file://:0:0:0:0 | (__end) | Expr |
+| file://:0:0:0:0 | (reference dereference) | Expr |
+| file://:0:0:0:0 | (reference dereference) | Expr |
+| file://:0:0:0:0 | * ... | Expr |
+| file://:0:0:0:0 | array to pointer conversion | Expr |
+| file://:0:0:0:0 | array to pointer conversion | Expr |
| file://:0:0:0:0 | operator delete | Function |
| file://:0:0:0:0 | operator new | Function |
diff --git a/cpp/ql/test/library-tests/compiler_generated/compilerGenerated.ql b/cpp/ql/test/library-tests/compiler_generated/compilerGenerated.ql
index 0eb620f0c792..1ed4a9395e19 100644
--- a/cpp/ql/test/library-tests/compiler_generated/compilerGenerated.ql
+++ b/cpp/ql/test/library-tests/compiler_generated/compilerGenerated.ql
@@ -9,4 +9,6 @@ where
e.(Variable).isCompilerGenerated() and type = "Variable"
or
e.(Stmt).isCompilerGenerated() and type = "Stmt"
+ or
+ e.(Initializer).isCompilerGenerated() and type = "Initializer"
select e, type
diff --git a/cpp/ql/test/library-tests/compiler_generated/cpp.cpp b/cpp/ql/test/library-tests/compiler_generated/cpp.cpp
index 058bba7ba411..7525be8d40e8 100644
--- a/cpp/ql/test/library-tests/compiler_generated/cpp.cpp
+++ b/cpp/ql/test/library-tests/compiler_generated/cpp.cpp
@@ -15,3 +15,20 @@ void g1(void) {
delete m;
}
+void uses_pretty_function() {
+ const char* pretty = __PRETTY_FUNCTION__;
+}
+
+template
+void parameter_pack(Args... args) { }
+
+void test_parameter_pack() {
+ parameter_pack();
+}
+
+void ranged_for() {
+ int vs[] = {1, 2, 3};
+ for(int i : vs) {
+
+ }
+}
\ No newline at end of file
diff --git a/cpp/ql/test/library-tests/dataflow/external-models/bdlbb.cpp b/cpp/ql/test/library-tests/dataflow/external-models/bdlbb.cpp
new file mode 100644
index 000000000000..c8ec9dfd031e
--- /dev/null
+++ b/cpp/ql/test/library-tests/dataflow/external-models/bdlbb.cpp
@@ -0,0 +1,98 @@
+
+// --- stub library headers ---
+
+namespace bsl {
+ typedef unsigned long size_t;
+ template class allocator {};
+ template struct char_traits {};
+ template, class Allocator = allocator >
+ class basic_string {
+ public:
+ basic_string(const charT* s, const Allocator& a = Allocator());
+ const charT* data() const;
+ size_t size() const;
+ };
+ typedef basic_string string;
+ template class shared_ptr {
+ public:
+ T *get() const;
+ };
+}
+
+namespace BloombergLP {
+namespace bdlbb {
+ class BlobBuffer {
+ public:
+ char *data() const;
+ bsl::shared_ptr &buffer();
+ const bsl::shared_ptr &buffer() const;
+ };
+
+ class Blob {
+ public:
+ const BlobBuffer &buffer(int index) const;
+ };
+
+ struct BlobUtil {
+ static void copy(char *dstBuffer, const Blob &srcBlob, int position, int length);
+ static void copy(Blob *dstBlob, int dstOffset, const char *srcBuffer, int length);
+ static void copy(Blob *dstBlob, int dstOffset, const Blob &srcBlob, int srcOffset,
+ int length);
+ static char *getContiguousRangeOrCopy(char *dstBuffer, const Blob &srcBlob, int position,
+ int length, int alignment);
+ };
+}
+}
+
+// --- test code ---
+
+char *source();
+void sink(char);
+
+// A blob populated from a tainted buffer taints the bytes read back out of it.
+void test_BlobUtil_copy() {
+ bsl::string s(source());
+ BloombergLP::bdlbb::Blob blob;
+ BloombergLP::bdlbb::BlobUtil::copy(&blob, 0, s.data(), s.size());
+ char dst[16];
+ BloombergLP::bdlbb::BlobUtil::copy(dst, blob, 0, 16);
+ sink(*dst); // $ ir
+}
+
+void test_accessor_chain() {
+ bsl::string s(source());
+ BloombergLP::bdlbb::Blob blob;
+ BloombergLP::bdlbb::BlobUtil::copy(&blob, 0, s.data(), s.size());
+ const char *p = blob.buffer(0).data();
+ sink(*p); // $ ir
+}
+
+// The get() step comes from the built-in smart pointer model, not from bdlbb.model.yml.
+void test_accessor_chain_shared_ptr() {
+ bsl::string s(source());
+ BloombergLP::bdlbb::Blob blob;
+ BloombergLP::bdlbb::BlobUtil::copy(&blob, 0, s.data(), s.size());
+ const char *p = blob.buffer(0).buffer().get();
+ sink(*p); // $ ir
+}
+
+void test_getContiguousRangeOrCopy() {
+ bsl::string s(source());
+ BloombergLP::bdlbb::Blob blob;
+ BloombergLP::bdlbb::BlobUtil::copy(&blob, 0, s.data(), s.size());
+ char dst[16];
+ char *r = BloombergLP::bdlbb::BlobUtil::getContiguousRangeOrCopy(dst, blob, 0, 16, 1);
+ sink(*r); // $ ir
+}
+
+// A blob copied into another blob carries the taint across.
+void test_BlobUtil_copy_blob_to_blob() {
+ bsl::string s(source());
+ BloombergLP::bdlbb::Blob src;
+ BloombergLP::bdlbb::BlobUtil::copy(&src, 0, s.data(), s.size());
+ BloombergLP::bdlbb::Blob dst;
+ BloombergLP::bdlbb::BlobUtil::copy(&dst, 0, src, 0, 16);
+ char out[16];
+ BloombergLP::bdlbb::BlobUtil::copy(out, dst, 0, 16);
+ sink(*out); // $ ir
+}
diff --git a/cpp/ql/test/library-tests/dataflow/external-models/flow.expected b/cpp/ql/test/library-tests/dataflow/external-models/flow.expected
index 24ba3b2aa686..65817b549a90 100644
--- a/cpp/ql/test/library-tests/dataflow/external-models/flow.expected
+++ b/cpp/ql/test/library-tests/dataflow/external-models/flow.expected
@@ -1,239 +1,365 @@
models
-| 1 | Sink: ; ; false; ymlSink; ; ; Argument[0]; test-sink; manual |
-| 2 | Sink: boost::asio; ; false; write; ; ; Argument[*1]; remote-sink; manual |
-| 3 | Source: ; ; false; GetCommandLineA; ; ; ReturnValue[*]; local; manual |
-| 4 | Source: ; ; false; GetEnvironmentStringsA; ; ; ReturnValue[*]; local; manual |
-| 5 | Source: ; ; false; GetEnvironmentVariableA; ; ; Argument[*1]; local; manual |
-| 6 | Source: ; ; false; HttpReceiveClientCertificate; ; ; Argument[*3]; remote; manual |
-| 7 | Source: ; ; false; HttpReceiveHttpRequest; ; ; Argument[*3]; remote; manual |
-| 8 | Source: ; ; false; HttpReceiveRequestEntityBody; ; ; Argument[*3]; remote; manual |
-| 9 | Source: ; ; false; MapViewOfFile2; ; ; ReturnValue[*]; local; manual |
-| 10 | Source: ; ; false; MapViewOfFile3; ; ; ReturnValue[*]; local; manual |
-| 11 | Source: ; ; false; MapViewOfFile3FromApp; ; ; ReturnValue[*]; local; manual |
-| 12 | Source: ; ; false; MapViewOfFile; ; ; ReturnValue[*]; local; manual |
-| 13 | Source: ; ; false; MapViewOfFileEx; ; ; ReturnValue[*]; local; manual |
-| 14 | Source: ; ; false; MapViewOfFileFromApp; ; ; ReturnValue[*]; local; manual |
-| 15 | Source: ; ; false; MapViewOfFileNuma2; ; ; ReturnValue[*]; local; manual |
-| 16 | Source: ; ; false; NtReadFile; ; ; Argument[*5]; local; manual |
-| 17 | Source: ; ; false; ReadFile; ; ; Argument[*1]; local; manual |
-| 18 | Source: ; ; false; ReadFileEx; ; ; Argument[*1]; local; manual |
-| 19 | Source: ; ; false; RegEnumValueA; ; ; Argument[*2,*6]; windows-registry; manual |
-| 20 | Source: ; ; false; RegEnumValueW; ; ; Argument[*2,*6]; windows-registry; manual |
-| 21 | Source: ; ; false; RegGetValueA; ; ; Argument[*5]; windows-registry; manual |
-| 22 | Source: ; ; false; RegGetValueW; ; ; Argument[*5]; windows-registry; manual |
-| 23 | Source: ; ; false; RegQueryMultipleValuesA; ; ; Argument[*3]; windows-registry; manual |
-| 24 | Source: ; ; false; RegQueryMultipleValuesW; ; ; Argument[*3]; windows-registry; manual |
-| 25 | Source: ; ; false; RegQueryValueA; ; ; Argument[*2]; windows-registry; manual |
-| 26 | Source: ; ; false; RegQueryValueExA; ; ; Argument[*4]; windows-registry; manual |
-| 27 | Source: ; ; false; RegQueryValueExW; ; ; Argument[*4]; windows-registry; manual |
-| 28 | Source: ; ; false; RegQueryValueW; ; ; Argument[*2]; windows-registry; manual |
-| 29 | Source: ; ; false; WinHttpQueryHeaders; ; ; Argument[*3]; remote; manual |
-| 30 | Source: ; ; false; WinHttpQueryHeadersEx; ; ; Argument[**8]; remote; manual |
-| 31 | Source: ; ; false; WinHttpQueryHeadersEx; ; ; Argument[*5]; remote; manual |
-| 32 | Source: ; ; false; WinHttpQueryHeadersEx; ; ; Argument[*6]; remote; manual |
-| 33 | Source: ; ; false; WinHttpReadData; ; ; Argument[*1]; remote; manual |
-| 34 | Source: ; ; false; WinHttpReadDataEx; ; ; Argument[*1]; remote; manual |
-| 35 | Source: ; ; false; ymlSource; ; ; ReturnValue; local; manual |
-| 36 | Source: Azure::Core::Http; RawResponse; true; ExtractBodyStream; ; ; ReturnValue[*]; remote; manual |
-| 37 | Source: Azure::Core::Http; RawResponse; true; GetBody; ; ; ReturnValue[*]; remote; manual |
-| 38 | Source: Azure::Core::Http; RawResponse; true; GetHeaders; ; ; ReturnValue[*]; remote; manual |
-| 39 | Source: Azure::Core::Http; Request; true; GetBodyStream; ; ; ReturnValue[*]; remote; manual |
-| 40 | Source: Azure::Core::Http; Request; true; GetHeader; ; ; ReturnValue; remote; manual |
-| 41 | Source: Azure::Core::Http; Request; true; GetHeaders; ; ; ReturnValue; remote; manual |
-| 42 | Source: boost::asio; ; false; read_until; ; ; Argument[*1]; remote; manual |
-| 43 | Summary: ; ; false; CLSIDFromProgID; ; ; Argument[*0]; Argument[*1]; taint; manual |
-| 44 | Summary: ; ; false; CLSIDFromString; ; ; Argument[*0]; Argument[*1]; taint; manual |
-| 45 | Summary: ; ; false; CommandLineToArgvA; ; ; Argument[*0]; ReturnValue[**]; taint; manual |
-| 46 | Summary: ; ; false; CreateRemoteThread; ; ; Argument[@4]; Argument[3].Parameter[@0]; value; manual |
-| 47 | Summary: ; ; false; CreateRemoteThreadEx; ; ; Argument[@4]; Argument[3].Parameter[@0]; value; manual |
-| 48 | Summary: ; ; false; CreateThread; ; ; Argument[@3]; Argument[2].Parameter[@0]; value; manual |
-| 49 | Summary: ; ; false; GUIDFromString; ; ; Argument[*0]; Argument[*1]; taint; manual |
-| 50 | Summary: ; ; false; IIDFromString; ; ; Argument[*0]; Argument[*1]; taint; manual |
-| 51 | Summary: ; ; false; ProgIDFromCLSID; ; ; Argument[*0]; Argument[**1]; taint; manual |
-| 52 | Summary: ; ; false; ReadFileEx; ; ; Argument[*3].Field[@hEvent]; Argument[4].Parameter[*2].Field[@hEvent]; value; manual |
-| 53 | Summary: ; ; false; RtlCopyDeviceMemory; ; ; Argument[*@1]; Argument[*@0]; value; manual |
-| 54 | Summary: ; ; false; RtlCopyMemory; ; ; Argument[*@1]; Argument[*@0]; value; manual |
-| 55 | Summary: ; ; false; RtlCopyMemoryNonTemporal; ; ; Argument[*@1]; Argument[*@0]; value; manual |
-| 56 | Summary: ; ; false; RtlCopyUnicodeString; ; ; Argument[*1].Field[*Buffer]; Argument[*0].Field[*Buffer]; value; manual |
-| 57 | Summary: ; ; false; RtlCopyVolatileMemory; ; ; Argument[*@1]; Argument[*@0]; value; manual |
-| 58 | Summary: ; ; false; RtlInitUnicodeString; ; ; Argument[*1]; Argument[*0].Field[*Buffer]; value; manual |
-| 59 | Summary: ; ; false; RtlMoveMemory; ; ; Argument[*@1]; Argument[*@0]; value; manual |
-| 60 | Summary: ; ; false; RtlMoveVolatileMemory; ; ; Argument[*@1]; Argument[*@0]; value; manual |
-| 61 | Summary: ; ; false; StringFromCLSID; ; ; Argument[*0]; Argument[**1]; taint; manual |
-| 62 | Summary: ; ; false; StringFromGUID2; ; ; Argument[*0]; Argument[*1]; taint; manual |
-| 63 | Summary: ; ; false; StringFromIID; ; ; Argument[*0]; Argument[**1]; taint; manual |
-| 64 | Summary: ; ; false; WinHttpCrackUrl; ; ; Argument[*0]; Argument[*3]; taint; manual |
-| 65 | Summary: ; ; false; callWithArgument; ; ; Argument[1]; Argument[0].Parameter[0]; value; manual |
-| 66 | Summary: ; ; false; callWithNonTypeTemplate; (const T &); ; Argument[*0]; ReturnValue; value; manual |
-| 67 | Summary: ; ; false; pthread_create; ; ; Argument[@3]; Argument[2].Parameter[@0]; value; manual |
-| 68 | Summary: ; ; false; read_field_from_struct; ; ; Argument[*0].Field[MyNamespace::MyStructInNamespace::myField]; ReturnValue; value; manual |
-| 69 | Summary: ; ; false; read_field_from_struct_2; ; ; Argument[*0].Field[MyGlobalStruct::myField]; ReturnValue; value; manual |
-| 70 | Summary: ; ; false; ymlStepGenerated; ; ; Argument[0]; ReturnValue; taint; df-generated |
-| 71 | Summary: ; ; false; ymlStepManual; ; ; Argument[0]; ReturnValue; taint; manual |
-| 72 | Summary: ; ; false; ymlStepManual_with_body; ; ; Argument[0]; ReturnValue; taint; manual |
-| 73 | Summary: ; MyString; true; operator[]; ; ; Argument[-1]; ReturnValue[*]; taint; manual |
-| 74 | Summary: ; MyString; true; operator[]; ; ; ReturnValue[*]; Argument[-1]; taint; manual |
-| 75 | Summary: ; ReverseFlow; true; get_ptr; ; ; ReturnValue[*]; Argument[-1].Field[ReverseFlow::value]; value; manual |
-| 76 | Summary: ; TemplateClass1; true; templateFunction2; (U,V); ; Argument[1]; ReturnValue; value; manual |
-| 77 | Summary: ; TemplateClass1; false; templateFunction; (T,U); ; Argument[0]; ReturnValue; value; manual |
-| 78 | Summary: ; TemplateClass2; true; function; (U,T); ; Argument[1]; ReturnValue; value; manual |
-| 79 | Summary: Azure::Core::IO; BodyStream; true; Read; ; ; Argument[-1]; Argument[*0]; taint; manual |
-| 80 | Summary: Azure::Core::IO; BodyStream; true; ReadToCount; ; ; Argument[-1]; Argument[*0]; taint; manual |
-| 81 | Summary: Azure::Core::IO; BodyStream; true; ReadToEnd; ; ; Argument[-1]; ReturnValue.Element; taint; manual |
-| 82 | Summary: Azure; Nullable; true; Value; ; ; Argument[-1]; ReturnValue[*]; taint; manual |
-| 83 | Summary: boost::asio; ; false; buffer; ; ; Argument[*0]; ReturnValue; taint; manual |
+| 1 | Sink: ; ; false; sink_from_callback_return_template; ; ; Argument[0].ReturnValue; test-sink; manual |
+| 2 | Sink: ; ; false; sink_ptr_from_callback_return_ptr; ; ; Argument[0].ReturnValue[*]; test-sink; manual |
+| 3 | Sink: ; ; false; ymlSink; ; ; Argument[0]; test-sink; manual |
+| 4 | Sink: boost::asio; ; false; write; ; ; Argument[*1]; remote-sink; manual |
+| 5 | Source: ; ; false; GetCommandLineA; ; ; ReturnValue[*]; local; manual |
+| 6 | Source: ; ; false; GetEnvironmentStringsA; ; ; ReturnValue[*]; local; manual |
+| 7 | Source: ; ; false; GetEnvironmentVariableA; ; ; Argument[*1]; local; manual |
+| 8 | Source: ; ; false; HttpReceiveClientCertificate; ; ; Argument[*3]; remote; manual |
+| 9 | Source: ; ; false; HttpReceiveHttpRequest; ; ; Argument[*3]; remote; manual |
+| 10 | Source: ; ; false; HttpReceiveRequestEntityBody; ; ; Argument[*3]; remote; manual |
+| 11 | Source: ; ; false; MapViewOfFile2; ; ; ReturnValue[*]; local; manual |
+| 12 | Source: ; ; false; MapViewOfFile3; ; ; ReturnValue[*]; local; manual |
+| 13 | Source: ; ; false; MapViewOfFile3FromApp; ; ; ReturnValue[*]; local; manual |
+| 14 | Source: ; ; false; MapViewOfFile; ; ; ReturnValue[*]; local; manual |
+| 15 | Source: ; ; false; MapViewOfFileEx; ; ; ReturnValue[*]; local; manual |
+| 16 | Source: ; ; false; MapViewOfFileFromApp; ; ; ReturnValue[*]; local; manual |
+| 17 | Source: ; ; false; MapViewOfFileNuma2; ; ; ReturnValue[*]; local; manual |
+| 18 | Source: ; ; false; NtReadFile; ; ; Argument[*5]; local; manual |
+| 19 | Source: ; ; false; ReadFile; ; ; Argument[*1]; local; manual |
+| 20 | Source: ; ; false; ReadFileEx; ; ; Argument[*1]; local; manual |
+| 21 | Source: ; ; false; RegEnumValueA; ; ; Argument[*2,*6]; windows-registry; manual |
+| 22 | Source: ; ; false; RegEnumValueW; ; ; Argument[*2,*6]; windows-registry; manual |
+| 23 | Source: ; ; false; RegGetValueA; ; ; Argument[*5]; windows-registry; manual |
+| 24 | Source: ; ; false; RegGetValueW; ; ; Argument[*5]; windows-registry; manual |
+| 25 | Source: ; ; false; RegQueryMultipleValuesA; ; ; Argument[*3]; windows-registry; manual |
+| 26 | Source: ; ; false; RegQueryMultipleValuesW; ; ; Argument[*3]; windows-registry; manual |
+| 27 | Source: ; ; false; RegQueryValueA; ; ; Argument[*2]; windows-registry; manual |
+| 28 | Source: ; ; false; RegQueryValueExA; ; ; Argument[*4]; windows-registry; manual |
+| 29 | Source: ; ; false; RegQueryValueExW; ; ; Argument[*4]; windows-registry; manual |
+| 30 | Source: ; ; false; RegQueryValueW; ; ; Argument[*2]; windows-registry; manual |
+| 31 | Source: ; ; false; WinHttpQueryHeaders; ; ; Argument[*3]; remote; manual |
+| 32 | Source: ; ; false; WinHttpQueryHeadersEx; ; ; Argument[**8]; remote; manual |
+| 33 | Source: ; ; false; WinHttpQueryHeadersEx; ; ; Argument[*5]; remote; manual |
+| 34 | Source: ; ; false; WinHttpQueryHeadersEx; ; ; Argument[*6]; remote; manual |
+| 35 | Source: ; ; false; WinHttpReadData; ; ; Argument[*1]; remote; manual |
+| 36 | Source: ; ; false; WinHttpReadDataEx; ; ; Argument[*1]; remote; manual |
+| 37 | Source: ; ; false; source_from_callback_ptr; ; ; Argument[0].Parameter[*0].Field[SourceWrapper::value]; local; manual |
+| 38 | Source: ; ; false; source_from_callback_return_ptr; ; ; Argument[0].ReturnValue; local; manual |
+| 39 | Source: ; ; false; source_from_callback_return_template; ; ; Argument[0].ReturnValue; local; manual |
+| 40 | Source: ; ; false; source_from_callback_template; ; ; Argument[0].Parameter[*0].Field[SourceWrapper::value]; local; manual |
+| 41 | Source: ; ; false; source_ptr_from_callback_return_ptr; ; ; Argument[0].ReturnValue[*]; local; manual |
+| 42 | Source: ; ; false; test_parameter; ; ; Parameter[*0].Field[*SourceWrapper::pointer]; local; manual |
+| 43 | Source: ; ; false; test_parameter; ; ; Parameter[*0].Field[SourceWrapper::value]; local; manual |
+| 44 | Source: ; ; false; test_parameter; ; ; Parameter[*2]; local; manual |
+| 45 | Source: ; ; false; test_parameter; ; ; Parameter[1].Field[*SourceWrapper::pointer]; local; manual |
+| 46 | Source: ; ; false; test_parameter; ; ; Parameter[1].Field[SourceWrapper::value]; local; manual |
+| 47 | Source: ; ; false; ymlFieldSource; ; ; ReturnValue.Field[SourceWrapper::value]; local; manual |
+| 48 | Source: ; ; false; ymlSource; ; ; ReturnValue; local; manual |
+| 49 | Source: ; ; false; ymlSourcePtr; ; ; ReturnValue[*]; local; manual |
+| 50 | Source: Azure::Core::Http; RawResponse; true; ExtractBodyStream; ; ; ReturnValue[*]; remote; manual |
+| 51 | Source: Azure::Core::Http; RawResponse; true; GetBody; ; ; ReturnValue[*]; remote; manual |
+| 52 | Source: Azure::Core::Http; RawResponse; true; GetHeaders; ; ; ReturnValue[*]; remote; manual |
+| 53 | Source: Azure::Core::Http; Request; true; GetBodyStream; ; ; ReturnValue[*]; remote; manual |
+| 54 | Source: Azure::Core::Http; Request; true; GetHeader; ; ; ReturnValue; remote; manual |
+| 55 | Source: Azure::Core::Http; Request; true; GetHeaders; ; ; ReturnValue; remote; manual |
+| 56 | Source: boost::asio; ; false; read_until; ; ; Argument[*1]; remote; manual |
+| 57 | Summary: ; ; false; CLSIDFromProgID; ; ; Argument[*0]; Argument[*1]; taint; manual |
+| 58 | Summary: ; ; false; CLSIDFromString; ; ; Argument[*0]; Argument[*1]; taint; manual |
+| 59 | Summary: ; ; false; CommandLineToArgvA; ; ; Argument[*0]; ReturnValue[**]; taint; manual |
+| 60 | Summary: ; ; false; CreateRemoteThread; ; ; Argument[@4]; Argument[3].Parameter[@0]; value; manual |
+| 61 | Summary: ; ; false; CreateRemoteThreadEx; ; ; Argument[@4]; Argument[3].Parameter[@0]; value; manual |
+| 62 | Summary: ; ; false; CreateThread; ; ; Argument[@3]; Argument[2].Parameter[@0]; value; manual |
+| 63 | Summary: ; ; false; GUIDFromString; ; ; Argument[*0]; Argument[*1]; taint; manual |
+| 64 | Summary: ; ; false; IIDFromString; ; ; Argument[*0]; Argument[*1]; taint; manual |
+| 65 | Summary: ; ; false; ProgIDFromCLSID; ; ; Argument[*0]; Argument[**1]; taint; manual |
+| 66 | Summary: ; ; false; ReadFileEx; ; ; Argument[*3].Field[@hEvent]; Argument[4].Parameter[*2].Field[@hEvent]; value; manual |
+| 67 | Summary: ; ; false; RtlCopyDeviceMemory; ; ; Argument[*@1]; Argument[*@0]; value; manual |
+| 68 | Summary: ; ; false; RtlCopyMemory; ; ; Argument[*@1]; Argument[*@0]; value; manual |
+| 69 | Summary: ; ; false; RtlCopyMemoryNonTemporal; ; ; Argument[*@1]; Argument[*@0]; value; manual |
+| 70 | Summary: ; ; false; RtlCopyUnicodeString; ; ; Argument[*1].Field[*Buffer]; Argument[*0].Field[*Buffer]; value; manual |
+| 71 | Summary: ; ; false; RtlCopyVolatileMemory; ; ; Argument[*@1]; Argument[*@0]; value; manual |
+| 72 | Summary: ; ; false; RtlInitUnicodeString; ; ; Argument[*1]; Argument[*0].Field[*Buffer]; value; manual |
+| 73 | Summary: ; ; false; RtlMoveMemory; ; ; Argument[*@1]; Argument[*@0]; value; manual |
+| 74 | Summary: ; ; false; RtlMoveVolatileMemory; ; ; Argument[*@1]; Argument[*@0]; value; manual |
+| 75 | Summary: ; ; false; StringFromCLSID; ; ; Argument[*0]; Argument[**1]; taint; manual |
+| 76 | Summary: ; ; false; StringFromGUID2; ; ; Argument[*0]; Argument[*1]; taint; manual |
+| 77 | Summary: ; ; false; StringFromIID; ; ; Argument[*0]; Argument[**1]; taint; manual |
+| 78 | Summary: ; ; false; WinHttpCrackUrl; ; ; Argument[*0]; Argument[*3]; taint; manual |
+| 79 | Summary: ; ; false; callWithArgument; ; ; Argument[1]; Argument[0].Parameter[0]; value; manual |
+| 80 | Summary: ; ; false; callWithNonTypeTemplate; (const T &); ; Argument[*0]; ReturnValue; value; manual |
+| 81 | Summary: ; ; false; pthread_create; ; ; Argument[@3]; Argument[2].Parameter[@0]; value; manual |
+| 82 | Summary: ; ; false; read_field_from_struct; ; ; Argument[*0].Field[MyNamespace::MyStructInNamespace::myField]; ReturnValue; value; manual |
+| 83 | Summary: ; ; false; read_field_from_struct_2; ; ; Argument[*0].Field[MyGlobalStruct::myField]; ReturnValue; value; manual |
+| 84 | Summary: ; ; false; ymlStepGenerated; ; ; Argument[0]; ReturnValue; taint; df-generated |
+| 85 | Summary: ; ; false; ymlStepManual; ; ; Argument[0]; ReturnValue; taint; manual |
+| 86 | Summary: ; ; false; ymlStepManual_with_body; ; ; Argument[0]; ReturnValue; taint; manual |
+| 87 | Summary: ; MyString; true; operator[]; ; ; Argument[-1]; ReturnValue[*]; taint; manual |
+| 88 | Summary: ; MyString; true; operator[]; ; ; ReturnValue[*]; Argument[-1]; taint; manual |
+| 89 | Summary: ; ReverseFlow; true; get_ptr; ; ; ReturnValue[*]; Argument[-1].Field[ReverseFlow::value]; value; manual |
+| 90 | Summary: ; TemplateClass1; true; templateFunction2; (U,V); ; Argument[1]; ReturnValue; value; manual |
+| 91 | Summary: ; TemplateClass1; false; templateFunction; (T,U); ; Argument[0]; ReturnValue; value; manual |
+| 92 | Summary: ; TemplateClass2; true; function; (U,T); ; Argument[1]; ReturnValue; value; manual |
+| 93 | Summary: Azure::Core::IO; BodyStream; true; Read; ; ; Argument[-1]; Argument[*0]; taint; manual |
+| 94 | Summary: Azure::Core::IO; BodyStream; true; ReadToCount; ; ; Argument[-1]; Argument[*0]; taint; manual |
+| 95 | Summary: Azure::Core::IO; BodyStream; true; ReadToEnd; ; ; Argument[-1]; ReturnValue.Element; taint; manual |
+| 96 | Summary: Azure; Nullable; true; Value; ; ; Argument[-1]; ReturnValue[*]; taint; manual |
+| 97 | Summary: BloombergLP::bdlbb; Blob; true; buffer; ; ; Argument[-1]; ReturnValue[*]; taint; manual |
+| 98 | Summary: BloombergLP::bdlbb; BlobBuffer; true; buffer; ; ; Argument[-1]; ReturnValue[*]; taint; manual |
+| 99 | Summary: BloombergLP::bdlbb; BlobBuffer; true; data; ; ; Argument[-1]; ReturnValue[*]; taint; manual |
+| 100 | Summary: BloombergLP::bdlbb; BlobUtil; true; copy; (Blob *,int,const Blob &,int,int); ; Argument[*2]; Argument[*0]; taint; manual |
+| 101 | Summary: BloombergLP::bdlbb; BlobUtil; true; copy; (Blob *,int,const char *,int); ; Argument[*2]; Argument[*0]; taint; manual |
+| 102 | Summary: BloombergLP::bdlbb; BlobUtil; true; copy; (char *,const Blob &,int,int); ; Argument[*1]; Argument[*0]; taint; manual |
+| 103 | Summary: BloombergLP::bdlbb; BlobUtil; true; getContiguousRangeOrCopy; ; ; Argument[*1]; ReturnValue[*]; taint; manual |
+| 104 | Summary: boost::asio; ; false; buffer; ; ; Argument[*0]; ReturnValue; taint; manual |
edges
-| asio_streams.cpp:87:34:87:44 | read_until output argument | asio_streams.cpp:91:7:91:17 | recv_buffer | provenance | Src:MaD:42 |
-| asio_streams.cpp:87:34:87:44 | read_until output argument | asio_streams.cpp:93:29:93:39 | *recv_buffer | provenance | Src:MaD:42 Sink:MaD:2 |
+| asio_streams.cpp:87:34:87:44 | read_until output argument | asio_streams.cpp:91:7:91:17 | recv_buffer | provenance | Src:MaD:56 |
+| asio_streams.cpp:87:34:87:44 | read_until output argument | asio_streams.cpp:93:29:93:39 | recv_buffer | provenance | Src:MaD:56 Sink:MaD:4 |
| asio_streams.cpp:97:37:97:44 | call to source | asio_streams.cpp:98:7:98:14 | send_str | provenance | TaintFunction |
| asio_streams.cpp:97:37:97:44 | call to source | asio_streams.cpp:100:64:100:71 | *send_str | provenance | TaintFunction |
| asio_streams.cpp:100:44:100:62 | call to buffer | asio_streams.cpp:100:44:100:62 | call to buffer | provenance | |
| asio_streams.cpp:100:44:100:62 | call to buffer | asio_streams.cpp:101:7:101:17 | send_buffer | provenance | |
-| asio_streams.cpp:100:44:100:62 | call to buffer | asio_streams.cpp:103:29:103:39 | *send_buffer | provenance | Sink:MaD:2 |
-| asio_streams.cpp:100:64:100:71 | *send_str | asio_streams.cpp:100:44:100:62 | call to buffer | provenance | MaD:83 |
-| azure.cpp:253:48:253:60 | *call to GetBodyStream | azure.cpp:253:48:253:60 | *call to GetBodyStream | provenance | Src:MaD:39 |
+| asio_streams.cpp:100:44:100:62 | call to buffer | asio_streams.cpp:103:29:103:39 | send_buffer | provenance | Sink:MaD:4 |
+| asio_streams.cpp:100:64:100:71 | *send_str | asio_streams.cpp:100:44:100:62 | call to buffer | provenance | MaD:104 |
| azure.cpp:253:48:253:60 | *call to GetBodyStream | azure.cpp:257:5:257:8 | *resp | provenance | |
| azure.cpp:253:48:253:60 | *call to GetBodyStream | azure.cpp:262:5:262:8 | *resp | provenance | |
| azure.cpp:253:48:253:60 | *call to GetBodyStream | azure.cpp:266:38:266:41 | *resp | provenance | |
-| azure.cpp:257:5:257:8 | *resp | azure.cpp:257:16:257:21 | Read output argument | provenance | MaD:79 |
+| azure.cpp:253:48:253:60 | call to GetBodyStream | azure.cpp:253:48:253:60 | *call to GetBodyStream | provenance | Src:MaD:53 |
+| azure.cpp:257:5:257:8 | *resp | azure.cpp:257:16:257:21 | Read output argument | provenance | MaD:93 |
| azure.cpp:257:16:257:21 | Read output argument | azure.cpp:258:10:258:16 | * ... | provenance | |
-| azure.cpp:262:5:262:8 | *resp | azure.cpp:262:23:262:28 | ReadToCount output argument | provenance | MaD:80 |
+| azure.cpp:262:5:262:8 | *resp | azure.cpp:262:23:262:28 | ReadToCount output argument | provenance | MaD:94 |
| azure.cpp:262:23:262:28 | ReadToCount output argument | azure.cpp:263:10:263:16 | * ... | provenance | |
-| azure.cpp:266:38:266:41 | *resp | azure.cpp:266:44:266:52 | call to ReadToEnd [element] | provenance | MaD:81 |
+| azure.cpp:266:38:266:41 | *resp | azure.cpp:266:44:266:52 | call to ReadToEnd [element] | provenance | MaD:95 |
| azure.cpp:266:44:266:52 | call to ReadToEnd [element] | azure.cpp:266:44:266:52 | call to ReadToEnd [element] | provenance | |
| azure.cpp:266:44:266:52 | call to ReadToEnd [element] | azure.cpp:267:10:267:12 | vec [element] | provenance | |
| azure.cpp:267:10:267:12 | vec [element] | azure.cpp:267:10:267:12 | vec | provenance | |
-| azure.cpp:273:62:273:64 | call to GetHeaders | azure.cpp:273:62:273:64 | call to GetHeaders | provenance | Src:MaD:38 |
+| azure.cpp:273:52:273:61 | call to GetHeaders | azure.cpp:273:62:273:64 | call to GetHeaders | provenance | Src:MaD:52 |
| azure.cpp:273:62:273:64 | call to GetHeaders | azure.cpp:274:14:274:29 | call to operator[] | provenance | TaintFunction |
| azure.cpp:273:62:273:64 | call to GetHeaders | azure.cpp:274:14:274:29 | call to operator[] | provenance | TaintFunction |
| azure.cpp:273:62:273:64 | call to GetHeaders | azure.cpp:274:14:274:29 | call to operator[] | provenance | TaintFunction |
| azure.cpp:274:14:274:29 | call to operator[] | azure.cpp:274:10:274:29 | call to operator[] | provenance | |
| azure.cpp:274:14:274:29 | call to operator[] | azure.cpp:274:14:274:29 | call to operator[] | provenance | |
-| azure.cpp:277:45:277:47 | call to GetBody | azure.cpp:277:45:277:47 | call to GetBody | provenance | Src:MaD:37 |
+| azure.cpp:277:38:277:44 | call to GetBody | azure.cpp:277:45:277:47 | call to GetBody | provenance | Src:MaD:51 |
| azure.cpp:277:45:277:47 | call to GetBody | azure.cpp:278:10:278:13 | body | provenance | |
| azure.cpp:277:45:277:47 | call to GetBody | azure.cpp:278:10:278:13 | body | provenance | |
| azure.cpp:278:10:278:13 | body | azure.cpp:278:10:278:13 | body | provenance | |
-| azure.cpp:281:68:281:84 | *call to ExtractBodyStream | azure.cpp:281:68:281:84 | *call to ExtractBodyStream | provenance | Src:MaD:36 |
| azure.cpp:281:68:281:84 | *call to ExtractBodyStream | azure.cpp:282:21:282:23 | *call to get | provenance | |
-| azure.cpp:282:21:282:23 | *call to get | azure.cpp:282:28:282:36 | call to ReadToEnd [element] | provenance | MaD:81 |
+| azure.cpp:281:68:281:84 | call to ExtractBodyStream | azure.cpp:281:68:281:84 | *call to ExtractBodyStream | provenance | Src:MaD:50 |
+| azure.cpp:282:21:282:23 | *call to get | azure.cpp:282:28:282:36 | call to ReadToEnd [element] | provenance | MaD:95 |
| azure.cpp:282:28:282:36 | call to ReadToEnd [element] | azure.cpp:282:10:282:38 | call to ReadToEnd | provenance | |
| azure.cpp:282:28:282:36 | call to ReadToEnd [element] | azure.cpp:282:28:282:36 | call to ReadToEnd [element] | provenance | |
-| azure.cpp:289:24:289:56 | call to GetHeader | azure.cpp:289:63:289:65 | call to Value | provenance | MaD:82 |
+| azure.cpp:289:24:289:56 | call to GetHeader | azure.cpp:289:63:289:65 | call to Value | provenance | MaD:96 |
| azure.cpp:289:32:289:40 | call to GetHeader | azure.cpp:289:24:289:56 | call to GetHeader | provenance | |
-| azure.cpp:289:32:289:40 | call to GetHeader | azure.cpp:289:32:289:40 | call to GetHeader | provenance | Src:MaD:40 |
+| azure.cpp:289:32:289:40 | call to GetHeader | azure.cpp:289:32:289:40 | call to GetHeader | provenance | Src:MaD:54 |
| azure.cpp:289:63:289:65 | call to Value | azure.cpp:289:63:289:65 | call to Value | provenance | |
| azure.cpp:289:63:289:65 | call to Value | azure.cpp:290:10:290:20 | headerValue | provenance | |
| azure.cpp:289:63:289:65 | call to Value | azure.cpp:290:10:290:20 | headerValue | provenance | |
| azure.cpp:290:10:290:20 | headerValue | azure.cpp:290:10:290:20 | headerValue | provenance | |
-| azure.cpp:293:58:293:67 | call to GetHeaders | azure.cpp:293:58:293:67 | call to GetHeaders | provenance | Src:MaD:41 |
+| azure.cpp:293:58:293:67 | call to GetHeaders | azure.cpp:293:58:293:67 | call to GetHeaders | provenance | Src:MaD:55 |
| azure.cpp:293:58:293:67 | call to GetHeaders | azure.cpp:294:38:294:53 | call to operator[] | provenance | TaintFunction |
| azure.cpp:294:38:294:53 | call to operator[] | azure.cpp:295:10:295:20 | contentType | provenance | |
| azure.cpp:294:38:294:53 | call to operator[] | azure.cpp:295:10:295:20 | contentType | provenance | |
| azure.cpp:295:10:295:20 | contentType | azure.cpp:295:10:295:20 | contentType | provenance | |
+| bdlbb.cpp:54:16:54:23 | call to source | bdlbb.cpp:56:49:56:52 | *call to data | provenance | TaintFunction |
+| bdlbb.cpp:56:37:56:41 | copy output argument | bdlbb.cpp:58:42:58:45 | *blob | provenance | |
+| bdlbb.cpp:56:49:56:52 | *call to data | bdlbb.cpp:56:37:56:41 | copy output argument | provenance | MaD:101 |
+| bdlbb.cpp:58:37:58:39 | copy output argument | bdlbb.cpp:59:7:59:10 | * ... | provenance | |
+| bdlbb.cpp:58:42:58:45 | *blob | bdlbb.cpp:58:37:58:39 | copy output argument | provenance | MaD:102 |
+| bdlbb.cpp:63:16:63:23 | call to source | bdlbb.cpp:65:49:65:52 | *call to data | provenance | TaintFunction |
+| bdlbb.cpp:65:37:65:41 | copy output argument | bdlbb.cpp:66:18:66:21 | *blob | provenance | |
+| bdlbb.cpp:65:49:65:52 | *call to data | bdlbb.cpp:65:37:65:41 | copy output argument | provenance | MaD:101 |
+| bdlbb.cpp:66:18:66:21 | *blob | bdlbb.cpp:66:29:66:32 | *call to buffer | provenance | MaD:97 |
+| bdlbb.cpp:66:18:66:38 | *call to data | bdlbb.cpp:66:18:66:38 | *call to data | provenance | |
+| bdlbb.cpp:66:18:66:38 | *call to data | bdlbb.cpp:67:7:67:8 | * ... | provenance | |
+| bdlbb.cpp:66:29:66:32 | *call to buffer | bdlbb.cpp:66:18:66:38 | *call to data | provenance | MaD:99 |
+| bdlbb.cpp:72:16:72:23 | call to source | bdlbb.cpp:74:49:74:52 | *call to data | provenance | TaintFunction |
+| bdlbb.cpp:74:37:74:41 | copy output argument | bdlbb.cpp:75:18:75:21 | *blob | provenance | |
+| bdlbb.cpp:74:49:74:52 | *call to data | bdlbb.cpp:74:37:74:41 | copy output argument | provenance | MaD:101 |
+| bdlbb.cpp:75:18:75:21 | *blob | bdlbb.cpp:75:29:75:32 | *call to buffer | provenance | MaD:97 |
+| bdlbb.cpp:75:18:75:46 | call to get | bdlbb.cpp:76:7:76:8 | * ... | provenance | |
+| bdlbb.cpp:75:29:75:32 | *call to buffer | bdlbb.cpp:75:39:75:41 | *call to buffer | provenance | MaD:98 |
+| bdlbb.cpp:75:39:75:41 | *call to buffer | bdlbb.cpp:75:18:75:46 | call to get | provenance | DataFlowFunction |
+| bdlbb.cpp:80:16:80:23 | call to source | bdlbb.cpp:82:49:82:52 | *call to data | provenance | TaintFunction |
+| bdlbb.cpp:82:37:82:41 | copy output argument | bdlbb.cpp:84:72:84:75 | *blob | provenance | |
+| bdlbb.cpp:82:49:82:52 | *call to data | bdlbb.cpp:82:37:82:41 | copy output argument | provenance | MaD:101 |
+| bdlbb.cpp:84:12:84:65 | *call to getContiguousRangeOrCopy | bdlbb.cpp:84:12:84:65 | *call to getContiguousRangeOrCopy | provenance | |
+| bdlbb.cpp:84:12:84:65 | *call to getContiguousRangeOrCopy | bdlbb.cpp:85:7:85:8 | * ... | provenance | |
+| bdlbb.cpp:84:72:84:75 | *blob | bdlbb.cpp:84:12:84:65 | *call to getContiguousRangeOrCopy | provenance | MaD:103 |
+| bdlbb.cpp:90:16:90:23 | call to source | bdlbb.cpp:92:48:92:51 | *call to data | provenance | TaintFunction |
+| bdlbb.cpp:92:37:92:40 | copy output argument | bdlbb.cpp:94:46:94:48 | *src | provenance | |
+| bdlbb.cpp:92:48:92:51 | *call to data | bdlbb.cpp:92:37:92:40 | copy output argument | provenance | MaD:101 |
+| bdlbb.cpp:94:37:94:40 | copy output argument | bdlbb.cpp:96:42:96:44 | *dst | provenance | |
+| bdlbb.cpp:94:46:94:48 | *src | bdlbb.cpp:94:37:94:40 | copy output argument | provenance | MaD:100 |
+| bdlbb.cpp:96:37:96:39 | copy output argument | bdlbb.cpp:97:7:97:10 | * ... | provenance | |
+| bdlbb.cpp:96:42:96:44 | *dst | bdlbb.cpp:96:37:96:39 | copy output argument | provenance | MaD:102 |
| test.cpp:7:47:7:52 | value2 | test.cpp:7:64:7:69 | value2 | provenance | |
| test.cpp:7:64:7:69 | value2 | test.cpp:7:5:7:30 | *ymlStepGenerated_with_body | provenance | |
-| test.cpp:10:10:10:18 | call to ymlSource | test.cpp:10:10:10:18 | call to ymlSource | provenance | Src:MaD:35 |
-| test.cpp:10:10:10:18 | call to ymlSource | test.cpp:14:10:14:10 | x | provenance | Sink:MaD:1 |
+| test.cpp:10:10:10:18 | call to ymlSource | test.cpp:10:10:10:18 | call to ymlSource | provenance | Src:MaD:48 |
+| test.cpp:10:10:10:18 | call to ymlSource | test.cpp:14:10:14:10 | x | provenance | Sink:MaD:3 |
| test.cpp:10:10:10:18 | call to ymlSource | test.cpp:17:24:17:24 | x | provenance | |
| test.cpp:10:10:10:18 | call to ymlSource | test.cpp:21:27:21:27 | x | provenance | |
| test.cpp:10:10:10:18 | call to ymlSource | test.cpp:25:35:25:35 | x | provenance | |
| test.cpp:10:10:10:18 | call to ymlSource | test.cpp:32:41:32:41 | x | provenance | |
| test.cpp:17:10:17:22 | call to ymlStepManual | test.cpp:17:10:17:22 | call to ymlStepManual | provenance | |
-| test.cpp:17:10:17:22 | call to ymlStepManual | test.cpp:18:10:18:10 | y | provenance | Sink:MaD:1 |
-| test.cpp:17:24:17:24 | x | test.cpp:17:10:17:22 | call to ymlStepManual | provenance | MaD:71 |
+| test.cpp:17:10:17:22 | call to ymlStepManual | test.cpp:18:10:18:10 | y | provenance | Sink:MaD:3 |
+| test.cpp:17:24:17:24 | x | test.cpp:17:10:17:22 | call to ymlStepManual | provenance | MaD:85 |
| test.cpp:21:10:21:25 | call to ymlStepGenerated | test.cpp:21:10:21:25 | call to ymlStepGenerated | provenance | |
-| test.cpp:21:10:21:25 | call to ymlStepGenerated | test.cpp:22:10:22:10 | z | provenance | Sink:MaD:1 |
-| test.cpp:21:27:21:27 | x | test.cpp:21:10:21:25 | call to ymlStepGenerated | provenance | MaD:70 |
+| test.cpp:21:10:21:25 | call to ymlStepGenerated | test.cpp:22:10:22:10 | z | provenance | Sink:MaD:3 |
+| test.cpp:21:27:21:27 | x | test.cpp:21:10:21:25 | call to ymlStepGenerated | provenance | MaD:84 |
| test.cpp:25:11:25:33 | call to ymlStepManual_with_body | test.cpp:25:11:25:33 | call to ymlStepManual_with_body | provenance | |
-| test.cpp:25:11:25:33 | call to ymlStepManual_with_body | test.cpp:26:10:26:11 | y2 | provenance | Sink:MaD:1 |
-| test.cpp:25:35:25:35 | x | test.cpp:25:11:25:33 | call to ymlStepManual_with_body | provenance | MaD:72 |
+| test.cpp:25:11:25:33 | call to ymlStepManual_with_body | test.cpp:26:10:26:11 | y2 | provenance | Sink:MaD:3 |
+| test.cpp:25:35:25:35 | x | test.cpp:25:11:25:33 | call to ymlStepManual_with_body | provenance | MaD:86 |
| test.cpp:32:11:32:36 | call to ymlStepGenerated_with_body | test.cpp:32:11:32:36 | call to ymlStepGenerated_with_body | provenance | |
-| test.cpp:32:11:32:36 | call to ymlStepGenerated_with_body | test.cpp:33:10:33:11 | z2 | provenance | Sink:MaD:1 |
+| test.cpp:32:11:32:36 | call to ymlStepGenerated_with_body | test.cpp:33:10:33:11 | z2 | provenance | Sink:MaD:3 |
| test.cpp:32:41:32:41 | x | test.cpp:7:47:7:52 | value2 | provenance | |
| test.cpp:32:41:32:41 | x | test.cpp:32:11:32:36 | call to ymlStepGenerated_with_body | provenance | |
| test.cpp:46:30:46:32 | *arg [x] | test.cpp:47:12:47:19 | *arg [x] | provenance | |
| test.cpp:47:12:47:19 | *arg [x] | test.cpp:48:13:48:13 | *s [x] | provenance | |
-| test.cpp:48:13:48:13 | *s [x] | test.cpp:48:16:48:16 | x | provenance | Sink:MaD:1 |
+| test.cpp:48:13:48:13 | *s [x] | test.cpp:48:16:48:16 | x | provenance | |
+| test.cpp:48:16:48:16 | x | test.cpp:48:16:48:16 | x | provenance | Sink:MaD:3 |
| test.cpp:56:2:56:2 | *s [post update] [x] | test.cpp:59:55:59:64 | *& ... [x] | provenance | |
| test.cpp:56:2:56:18 | ... = ... | test.cpp:56:2:56:2 | *s [post update] [x] | provenance | |
-| test.cpp:56:8:56:16 | call to ymlSource | test.cpp:56:2:56:18 | ... = ... | provenance | Src:MaD:35 |
-| test.cpp:59:55:59:64 | *& ... [x] | test.cpp:46:30:46:32 | *arg [x] | provenance | MaD:67 |
-| test.cpp:68:22:68:22 | y | test.cpp:69:11:69:11 | y | provenance | Sink:MaD:1 |
-| test.cpp:74:22:74:22 | y | test.cpp:75:11:75:11 | y | provenance | Sink:MaD:1 |
-| test.cpp:82:22:82:22 | y | test.cpp:83:11:83:11 | y | provenance | Sink:MaD:1 |
-| test.cpp:88:22:88:22 | y | test.cpp:89:11:89:11 | y | provenance | Sink:MaD:1 |
-| test.cpp:94:10:94:18 | call to ymlSource | test.cpp:94:10:94:18 | call to ymlSource | provenance | Src:MaD:35 |
+| test.cpp:56:8:56:16 | call to ymlSource | test.cpp:56:2:56:18 | ... = ... | provenance | Src:MaD:48 |
+| test.cpp:59:55:59:64 | *& ... [x] | test.cpp:46:30:46:32 | *arg [x] | provenance | MaD:81 |
+| test.cpp:68:22:68:22 | y | test.cpp:69:11:69:11 | y | provenance | Sink:MaD:3 |
+| test.cpp:74:22:74:22 | y | test.cpp:75:11:75:11 | y | provenance | Sink:MaD:3 |
+| test.cpp:82:22:82:22 | y | test.cpp:83:11:83:11 | y | provenance | Sink:MaD:3 |
+| test.cpp:88:22:88:22 | y | test.cpp:89:11:89:11 | y | provenance | Sink:MaD:3 |
+| test.cpp:94:10:94:18 | call to ymlSource | test.cpp:94:10:94:18 | call to ymlSource | provenance | Src:MaD:48 |
| test.cpp:94:10:94:18 | call to ymlSource | test.cpp:97:26:97:26 | x | provenance | |
| test.cpp:94:10:94:18 | call to ymlSource | test.cpp:101:26:101:26 | x | provenance | |
| test.cpp:94:10:94:18 | call to ymlSource | test.cpp:103:63:103:63 | x | provenance | |
| test.cpp:94:10:94:18 | call to ymlSource | test.cpp:104:62:104:62 | x | provenance | |
-| test.cpp:97:26:97:26 | x | test.cpp:68:22:68:22 | y | provenance | MaD:65 |
-| test.cpp:101:26:101:26 | x | test.cpp:74:22:74:22 | y | provenance | MaD:65 |
-| test.cpp:103:63:103:63 | x | test.cpp:82:22:82:22 | y | provenance | MaD:65 |
-| test.cpp:104:62:104:62 | x | test.cpp:88:22:88:22 | y | provenance | MaD:65 |
-| test.cpp:114:10:114:18 | call to ymlSource | test.cpp:114:10:114:18 | call to ymlSource | provenance | Src:MaD:35 |
+| test.cpp:97:26:97:26 | x | test.cpp:68:22:68:22 | y | provenance | MaD:79 |
+| test.cpp:101:26:101:26 | x | test.cpp:74:22:74:22 | y | provenance | MaD:79 |
+| test.cpp:103:63:103:63 | x | test.cpp:82:22:82:22 | y | provenance | MaD:79 |
+| test.cpp:104:62:104:62 | x | test.cpp:88:22:88:22 | y | provenance | MaD:79 |
+| test.cpp:114:10:114:18 | call to ymlSource | test.cpp:114:10:114:18 | call to ymlSource | provenance | Src:MaD:48 |
| test.cpp:114:10:114:18 | call to ymlSource | test.cpp:118:44:118:44 | *x | provenance | |
| test.cpp:118:11:118:42 | call to callWithNonTypeTemplate | test.cpp:118:11:118:42 | call to callWithNonTypeTemplate | provenance | |
-| test.cpp:118:11:118:42 | call to callWithNonTypeTemplate | test.cpp:119:10:119:11 | y2 | provenance | Sink:MaD:1 |
-| test.cpp:118:44:118:44 | *x | test.cpp:118:11:118:42 | call to callWithNonTypeTemplate | provenance | MaD:66 |
-| test.cpp:133:10:133:18 | call to ymlSource | test.cpp:133:10:133:18 | call to ymlSource | provenance | Src:MaD:35 |
+| test.cpp:118:11:118:42 | call to callWithNonTypeTemplate | test.cpp:119:10:119:11 | y2 | provenance | Sink:MaD:3 |
+| test.cpp:118:44:118:44 | *x | test.cpp:118:11:118:42 | call to callWithNonTypeTemplate | provenance | MaD:80 |
+| test.cpp:133:10:133:18 | call to ymlSource | test.cpp:133:10:133:18 | call to ymlSource | provenance | Src:MaD:48 |
| test.cpp:133:10:133:18 | call to ymlSource | test.cpp:134:45:134:45 | x | provenance | |
| test.cpp:134:13:134:43 | call to templateFunction | test.cpp:134:13:134:43 | call to templateFunction | provenance | |
-| test.cpp:134:13:134:43 | call to templateFunction | test.cpp:135:10:135:10 | y | provenance | Sink:MaD:1 |
-| test.cpp:134:45:134:45 | x | test.cpp:134:13:134:43 | call to templateFunction | provenance | MaD:77 |
-| test.cpp:146:10:146:18 | call to ymlSource | test.cpp:146:10:146:18 | call to ymlSource | provenance | Src:MaD:35 |
+| test.cpp:134:13:134:43 | call to templateFunction | test.cpp:135:10:135:10 | y | provenance | Sink:MaD:3 |
+| test.cpp:134:45:134:45 | x | test.cpp:134:13:134:43 | call to templateFunction | provenance | MaD:91 |
+| test.cpp:146:10:146:18 | call to ymlSource | test.cpp:146:10:146:18 | call to ymlSource | provenance | Src:MaD:48 |
| test.cpp:146:10:146:18 | call to ymlSource | test.cpp:148:26:148:26 | x | provenance | |
| test.cpp:148:10:148:27 | call to function | test.cpp:148:10:148:27 | call to function | provenance | |
-| test.cpp:148:10:148:27 | call to function | test.cpp:149:10:149:10 | z | provenance | Sink:MaD:1 |
-| test.cpp:148:26:148:26 | x | test.cpp:148:10:148:27 | call to function | provenance | MaD:78 |
-| test.cpp:155:10:155:18 | call to ymlSource | test.cpp:155:10:155:18 | call to ymlSource | provenance | Src:MaD:35 |
+| test.cpp:148:10:148:27 | call to function | test.cpp:149:10:149:10 | z | provenance | Sink:MaD:3 |
+| test.cpp:148:26:148:26 | x | test.cpp:148:10:148:27 | call to function | provenance | MaD:92 |
+| test.cpp:155:10:155:18 | call to ymlSource | test.cpp:155:10:155:18 | call to ymlSource | provenance | Src:MaD:48 |
| test.cpp:155:10:155:18 | call to ymlSource | test.cpp:157:26:157:26 | x | provenance | |
| test.cpp:157:13:157:20 | call to function | test.cpp:157:13:157:20 | call to function | provenance | |
-| test.cpp:157:13:157:20 | call to function | test.cpp:158:10:158:10 | z | provenance | Sink:MaD:1 |
-| test.cpp:157:26:157:26 | x | test.cpp:157:13:157:20 | call to function | provenance | MaD:78 |
+| test.cpp:157:13:157:20 | call to function | test.cpp:158:10:158:10 | z | provenance | Sink:MaD:3 |
+| test.cpp:157:26:157:26 | x | test.cpp:157:13:157:20 | call to function | provenance | MaD:92 |
| test.cpp:164:34:164:34 | x | test.cpp:165:69:165:69 | x | provenance | |
| test.cpp:165:12:165:64 | call to templateFunction2 | test.cpp:164:7:164:7 | *templateFunction3 | provenance | |
| test.cpp:165:12:165:64 | call to templateFunction2 | test.cpp:165:12:165:64 | call to templateFunction2 | provenance | |
-| test.cpp:165:69:165:69 | x | test.cpp:165:12:165:64 | call to templateFunction2 | provenance | MaD:76 |
-| test.cpp:170:10:170:18 | call to ymlSource | test.cpp:170:10:170:18 | call to ymlSource | provenance | Src:MaD:35 |
+| test.cpp:165:69:165:69 | x | test.cpp:165:12:165:64 | call to templateFunction2 | provenance | MaD:90 |
+| test.cpp:170:10:170:18 | call to ymlSource | test.cpp:170:10:170:18 | call to ymlSource | provenance | Src:MaD:48 |
| test.cpp:170:10:170:18 | call to ymlSource | test.cpp:172:51:172:51 | x | provenance | |
| test.cpp:172:13:172:44 | call to templateFunction3 | test.cpp:172:13:172:44 | call to templateFunction3 | provenance | |
-| test.cpp:172:13:172:44 | call to templateFunction3 | test.cpp:173:10:173:10 | y | provenance | Sink:MaD:1 |
+| test.cpp:172:13:172:44 | call to templateFunction3 | test.cpp:173:10:173:10 | y | provenance | Sink:MaD:3 |
| test.cpp:172:51:172:51 | x | test.cpp:164:34:164:34 | x | provenance | |
-| test.cpp:172:51:172:51 | x | test.cpp:172:13:172:44 | call to templateFunction3 | provenance | MaD:76 |
+| test.cpp:172:51:172:51 | x | test.cpp:172:13:172:44 | call to templateFunction3 | provenance | MaD:90 |
| test.cpp:186:2:186:2 | *s [post update] [myField] | test.cpp:187:33:187:34 | *& ... [myField] | provenance | |
| test.cpp:186:2:186:24 | ... = ... | test.cpp:186:2:186:2 | *s [post update] [myField] | provenance | |
-| test.cpp:186:14:186:22 | call to ymlSource | test.cpp:186:2:186:24 | ... = ... | provenance | Src:MaD:35 |
+| test.cpp:186:14:186:22 | call to ymlSource | test.cpp:186:2:186:24 | ... = ... | provenance | Src:MaD:48 |
| test.cpp:187:10:187:31 | call to read_field_from_struct | test.cpp:187:10:187:31 | call to read_field_from_struct | provenance | |
-| test.cpp:187:10:187:31 | call to read_field_from_struct | test.cpp:188:10:188:10 | x | provenance | Sink:MaD:1 |
-| test.cpp:187:33:187:34 | *& ... [myField] | test.cpp:187:10:187:31 | call to read_field_from_struct | provenance | MaD:68 |
+| test.cpp:187:10:187:31 | call to read_field_from_struct | test.cpp:188:10:188:10 | x | provenance | Sink:MaD:3 |
+| test.cpp:187:33:187:34 | *& ... [myField] | test.cpp:187:10:187:31 | call to read_field_from_struct | provenance | MaD:82 |
| test.cpp:199:2:199:2 | *s [post update] [myField] | test.cpp:200:35:200:36 | *& ... [myField] | provenance | |
| test.cpp:199:2:199:24 | ... = ... | test.cpp:199:2:199:2 | *s [post update] [myField] | provenance | |
-| test.cpp:199:14:199:22 | call to ymlSource | test.cpp:199:2:199:24 | ... = ... | provenance | Src:MaD:35 |
+| test.cpp:199:14:199:22 | call to ymlSource | test.cpp:199:2:199:24 | ... = ... | provenance | Src:MaD:48 |
| test.cpp:200:10:200:33 | call to read_field_from_struct_2 | test.cpp:200:10:200:33 | call to read_field_from_struct_2 | provenance | |
-| test.cpp:200:10:200:33 | call to read_field_from_struct_2 | test.cpp:201:10:201:10 | x | provenance | Sink:MaD:1 |
-| test.cpp:200:35:200:36 | *& ... [myField] | test.cpp:200:10:200:33 | call to read_field_from_struct_2 | provenance | MaD:69 |
+| test.cpp:200:10:200:33 | call to read_field_from_struct_2 | test.cpp:201:10:201:10 | x | provenance | Sink:MaD:3 |
+| test.cpp:200:35:200:36 | *& ... [myField] | test.cpp:200:10:200:33 | call to read_field_from_struct_2 | provenance | MaD:83 |
| test.cpp:216:3:216:4 | get_ptr output argument [value] | test.cpp:217:11:217:12 | *rf [value] | provenance | |
-| test.cpp:216:3:216:28 | ... = ... | test.cpp:216:3:216:4 | get_ptr output argument [value] | provenance | MaD:75 |
-| test.cpp:216:18:216:26 | call to ymlSource | test.cpp:216:3:216:28 | ... = ... | provenance | Src:MaD:35 |
+| test.cpp:216:3:216:28 | ... = ... | test.cpp:216:3:216:4 | get_ptr output argument [value] | provenance | MaD:89 |
+| test.cpp:216:18:216:26 | call to ymlSource | test.cpp:216:3:216:28 | ... = ... | provenance | Src:MaD:48 |
| test.cpp:217:11:217:12 | *rf [value] | test.cpp:217:14:217:18 | value | provenance | |
| test.cpp:217:14:217:18 | value | test.cpp:217:14:217:18 | value | provenance | |
-| test.cpp:217:14:217:18 | value | test.cpp:218:11:218:11 | x | provenance | Sink:MaD:1 |
+| test.cpp:217:14:217:18 | value | test.cpp:218:11:218:11 | x | provenance | Sink:MaD:3 |
| test.cpp:222:3:222:3 | operator[] output argument | test.cpp:223:12:223:12 | *s | provenance | |
-| test.cpp:222:3:222:20 | ... = ... | test.cpp:222:3:222:3 | operator[] output argument | provenance | MaD:74 |
-| test.cpp:222:10:222:20 | call to ymlSource | test.cpp:222:3:222:20 | ... = ... | provenance | Src:MaD:35 |
-| test.cpp:223:12:223:12 | *s | test.cpp:223:13:223:15 | call to operator[] | provenance | MaD:73 |
+| test.cpp:222:3:222:20 | ... = ... | test.cpp:222:3:222:3 | operator[] output argument | provenance | MaD:88 |
+| test.cpp:222:10:222:18 | call to ymlSource | test.cpp:222:3:222:20 | ... = ... | provenance | Src:MaD:48 |
+| test.cpp:223:12:223:12 | *s | test.cpp:223:13:223:15 | call to operator[] | provenance | MaD:87 |
| test.cpp:223:13:223:15 | call to operator[] | test.cpp:223:13:223:15 | call to operator[] | provenance | |
-| test.cpp:223:13:223:15 | call to operator[] | test.cpp:224:11:224:11 | c | provenance | Sink:MaD:1 |
-| windows.cpp:22:15:22:29 | *call to GetCommandLineA | windows.cpp:22:15:22:29 | *call to GetCommandLineA | provenance | Src:MaD:3 |
+| test.cpp:223:13:223:15 | call to operator[] | test.cpp:224:11:224:11 | c | provenance | Sink:MaD:3 |
+| test.cpp:242:29:242:29 | *s [value] | test.cpp:243:10:243:10 | *s [value] | provenance | |
+| test.cpp:243:10:243:10 | *s [value] | test.cpp:243:13:243:17 | value | provenance | |
+| test.cpp:243:13:243:17 | value | test.cpp:243:13:243:17 | value | provenance | Sink:MaD:3 |
+| test.cpp:247:26:247:39 | call to ymlFieldSource | test.cpp:247:26:247:39 | call to ymlFieldSource [value] | provenance | Src:MaD:47 |
+| test.cpp:247:26:247:39 | call to ymlFieldSource [value] | test.cpp:248:10:248:16 | *wrapper [value] | provenance | |
+| test.cpp:248:10:248:16 | *wrapper [value] | test.cpp:248:18:248:22 | value | provenance | |
+| test.cpp:248:18:248:22 | value | test.cpp:248:18:248:22 | value | provenance | Sink:MaD:3 |
+| test.cpp:250:32:250:32 | source_from_callback_template output argument | test.cpp:250:32:250:32 | source_from_callback_template output argument [value] | provenance | Src:MaD:40 |
+| test.cpp:250:32:250:32 | source_from_callback_template output argument [value] | test.cpp:242:29:242:29 | *s [value] | provenance | |
+| test.cpp:251:27:251:27 | source_from_callback_ptr output argument | test.cpp:251:27:251:27 | source_from_callback_ptr output argument [value] | provenance | Src:MaD:37 |
+| test.cpp:251:27:251:27 | source_from_callback_ptr output argument [value] | test.cpp:242:29:242:29 | *s [value] | provenance | |
+| test.cpp:257:35:257:35 | *s [value] | test.cpp:258:12:258:12 | *s [value] | provenance | |
+| test.cpp:258:12:258:12 | *s [value] | test.cpp:258:15:258:19 | value | provenance | |
+| test.cpp:258:15:258:19 | value | test.cpp:258:15:258:19 | value | provenance | Sink:MaD:3 |
+| test.cpp:262:27:262:31 | source_from_callback_ptr output argument | test.cpp:262:27:262:31 | source_from_callback_ptr output argument [value] | provenance | Src:MaD:37 |
+| test.cpp:262:27:262:31 | source_from_callback_ptr output argument [value] | test.cpp:242:29:242:29 | *s [value] | provenance | |
+| test.cpp:262:27:262:31 | source_from_callback_ptr output argument [value] | test.cpp:257:35:257:35 | *s [value] | provenance | |
+| test.cpp:264:32:266:2 | source_from_callback_template output argument | test.cpp:264:32:266:2 | source_from_callback_template output argument [value] | provenance | Src:MaD:40 |
+| test.cpp:264:32:266:2 | source_from_callback_template output argument [value] | test.cpp:264:56:264:56 | *s [value] | provenance | |
+| test.cpp:264:56:264:56 | *s [value] | test.cpp:265:11:265:11 | *s [value] | provenance | |
+| test.cpp:265:11:265:11 | *s [value] | test.cpp:265:14:265:18 | value | provenance | |
+| test.cpp:265:14:265:18 | value | test.cpp:265:14:265:18 | value | provenance | Sink:MaD:3 |
+| test.cpp:268:27:268:27 | source_from_callback_ptr output argument | test.cpp:268:27:268:27 | source_from_callback_ptr output argument [value] | provenance | Src:MaD:37 |
+| test.cpp:268:27:268:27 | source_from_callback_ptr output argument [value] | test.cpp:268:51:268:51 | *s [value] | provenance | |
+| test.cpp:268:51:268:51 | *s [value] | test.cpp:269:11:269:11 | *s [value] | provenance | |
+| test.cpp:269:11:269:11 | *s [value] | test.cpp:269:14:269:18 | value | provenance | |
+| test.cpp:269:14:269:18 | value | test.cpp:269:14:269:18 | value | provenance | Sink:MaD:3 |
+| test.cpp:273:40:273:40 | *s [value] | test.cpp:274:12:274:12 | *s [value] | provenance | |
+| test.cpp:274:12:274:12 | *s [value] | test.cpp:274:15:274:19 | value | provenance | |
+| test.cpp:274:15:274:19 | value | test.cpp:274:15:274:19 | value | provenance | Sink:MaD:3 |
+| test.cpp:278:32:278:34 | source_from_callback_template output argument | test.cpp:278:32:278:34 | source_from_callback_template output argument [value] | provenance | Src:MaD:40 |
+| test.cpp:278:32:278:34 | source_from_callback_template output argument [value] | test.cpp:273:40:273:40 | *s [value] | provenance | |
+| test.cpp:293:5:293:26 | *callback_returning_int | test.cpp:307:10:307:31 | call to callback_returning_int | provenance | |
+| test.cpp:294:5:294:28 | *callback_returning_int_2 | test.cpp:310:10:310:33 | call to callback_returning_int_2 | provenance | |
+| test.cpp:295:6:295:31 | **callback_returning_ptr_int | test.cpp:313:13:313:38 | *call to callback_returning_ptr_int | provenance | |
+| test.cpp:297:5:297:20 | *return_ymlSource | test.cpp:318:37:318:52 | return_ymlSource | provenance | Sink:MaD:1 |
+| test.cpp:297:33:297:41 | call to ymlSource | test.cpp:297:5:297:20 | *return_ymlSource | provenance | |
+| test.cpp:297:33:297:41 | call to ymlSource | test.cpp:297:5:297:20 | *return_ymlSource | provenance | Src:MaD:48 |
+| test.cpp:297:33:297:41 | call to ymlSource | test.cpp:297:33:297:41 | call to ymlSource | provenance | Src:MaD:48 |
+| test.cpp:299:6:299:28 | **return_ptr_to_ymlSource | test.cpp:321:36:321:58 | return_ptr_to_ymlSource | provenance | Sink:MaD:2 |
+| test.cpp:299:41:299:52 | *call to ymlSourcePtr | test.cpp:299:6:299:28 | **return_ptr_to_ymlSource | provenance | |
+| test.cpp:299:41:299:52 | call to ymlSourcePtr | test.cpp:299:6:299:28 | **return_ptr_to_ymlSource | provenance | Src:MaD:49 |
+| test.cpp:299:41:299:52 | call to ymlSourcePtr | test.cpp:299:41:299:52 | *call to ymlSourcePtr | provenance | Src:MaD:49 |
+| test.cpp:304:11:304:22 | call to ymlSourcePtr | test.cpp:304:10:304:24 | * ... | provenance | Src:MaD:49 Sink:MaD:3 |
+| test.cpp:306:39:306:60 | source_from_callback_return_template output argument | test.cpp:293:5:293:26 | *callback_returning_int | provenance | Src:MaD:39 |
+| test.cpp:307:10:307:31 | call to callback_returning_int | test.cpp:307:10:307:31 | call to callback_returning_int | provenance | Sink:MaD:3 |
+| test.cpp:309:34:309:57 | source_from_callback_return_ptr output argument | test.cpp:294:5:294:28 | *callback_returning_int_2 | provenance | Src:MaD:38 |
+| test.cpp:310:10:310:33 | call to callback_returning_int_2 | test.cpp:310:10:310:33 | call to callback_returning_int_2 | provenance | Sink:MaD:3 |
+| test.cpp:312:38:312:63 | source_ptr_from_callback_return_ptr output argument | test.cpp:295:6:295:31 | **callback_returning_ptr_int | provenance | Src:MaD:41 |
+| test.cpp:313:13:313:38 | *call to callback_returning_ptr_int | test.cpp:313:13:313:38 | *call to callback_returning_ptr_int | provenance | |
+| test.cpp:313:13:313:38 | *call to callback_returning_ptr_int | test.cpp:315:10:315:13 | * ... | provenance | Sink:MaD:3 |
+| test.cpp:317:39:317:39 | *operator() | test.cpp:317:37:317:64 | [...](...){...} | provenance | Sink:MaD:1 |
+| test.cpp:317:51:317:59 | call to ymlSource | test.cpp:317:39:317:39 | *operator() | provenance | |
+| test.cpp:317:51:317:59 | call to ymlSource | test.cpp:317:39:317:39 | *operator() | provenance | Src:MaD:48 |
+| test.cpp:317:51:317:59 | call to ymlSource | test.cpp:317:51:317:59 | call to ymlSource | provenance | Src:MaD:48 |
+| test.cpp:320:38:320:38 | **operator() | test.cpp:320:36:320:36 | call to operator int *(*)() | provenance | Sink:MaD:2 |
+| test.cpp:320:50:320:61 | *call to ymlSourcePtr | test.cpp:320:38:320:38 | **operator() | provenance | |
+| test.cpp:320:50:320:61 | call to ymlSourcePtr | test.cpp:320:38:320:38 | **operator() | provenance | Src:MaD:49 |
+| test.cpp:320:50:320:61 | call to ymlSourcePtr | test.cpp:320:50:320:61 | *call to ymlSourcePtr | provenance | Src:MaD:49 |
+| test.cpp:324:36:324:36 | p | test.cpp:325:10:325:10 | *p [value] | provenance | Src:MaD:43 |
+| test.cpp:324:36:324:36 | p | test.cpp:327:11:327:11 | *p [*pointer] | provenance | Src:MaD:42 |
+| test.cpp:324:53:324:53 | s | test.cpp:329:10:329:10 | *s [value] | provenance | Src:MaD:46 |
+| test.cpp:324:53:324:53 | s | test.cpp:331:11:331:11 | *s [*pointer] | provenance | Src:MaD:45 |
+| test.cpp:324:61:324:66 | source | test.cpp:334:10:334:16 | * ... | provenance | Src:MaD:44 Sink:MaD:3 |
+| test.cpp:325:10:325:10 | *p [value] | test.cpp:325:13:325:17 | value | provenance | |
+| test.cpp:325:13:325:17 | value | test.cpp:325:13:325:17 | value | provenance | Sink:MaD:3 |
+| test.cpp:327:10:327:20 | * ... | test.cpp:327:10:327:20 | * ... | provenance | Sink:MaD:3 |
+| test.cpp:327:11:327:11 | *p [*pointer] | test.cpp:327:10:327:20 | * ... | provenance | |
+| test.cpp:329:10:329:10 | *s [value] | test.cpp:329:12:329:16 | value | provenance | |
+| test.cpp:329:12:329:16 | value | test.cpp:329:12:329:16 | value | provenance | Sink:MaD:3 |
+| test.cpp:331:10:331:19 | * ... | test.cpp:331:10:331:19 | * ... | provenance | Sink:MaD:3 |
+| test.cpp:331:11:331:11 | *s [*pointer] | test.cpp:331:10:331:19 | * ... | provenance | |
| windows.cpp:22:15:22:29 | *call to GetCommandLineA | windows.cpp:24:8:24:11 | * ... | provenance | |
| windows.cpp:22:15:22:29 | *call to GetCommandLineA | windows.cpp:27:36:27:38 | *cmd | provenance | |
+| windows.cpp:22:15:22:29 | call to GetCommandLineA | windows.cpp:22:15:22:29 | *call to GetCommandLineA | provenance | Src:MaD:5 |
| windows.cpp:27:17:27:34 | **call to CommandLineToArgvA | windows.cpp:27:17:27:34 | **call to CommandLineToArgvA | provenance | |
| windows.cpp:27:17:27:34 | **call to CommandLineToArgvA | windows.cpp:30:8:30:15 | * ... | provenance | |
-| windows.cpp:27:36:27:38 | *cmd | windows.cpp:27:17:27:34 | **call to CommandLineToArgvA | provenance | MaD:45 |
-| windows.cpp:34:17:34:38 | *call to GetEnvironmentStringsA | windows.cpp:34:17:34:38 | *call to GetEnvironmentStringsA | provenance | Src:MaD:4 |
+| windows.cpp:27:36:27:38 | *cmd | windows.cpp:27:17:27:34 | **call to CommandLineToArgvA | provenance | MaD:59 |
| windows.cpp:34:17:34:38 | *call to GetEnvironmentStringsA | windows.cpp:36:10:36:13 | * ... | provenance | |
-| windows.cpp:39:36:39:38 | GetEnvironmentVariableA output argument | windows.cpp:41:10:41:13 | * ... | provenance | Src:MaD:5 |
+| windows.cpp:34:17:34:38 | call to GetEnvironmentStringsA | windows.cpp:34:17:34:38 | *call to GetEnvironmentStringsA | provenance | Src:MaD:6 |
+| windows.cpp:39:36:39:38 | GetEnvironmentVariableA output argument | windows.cpp:41:10:41:13 | * ... | provenance | Src:MaD:7 |
| windows.cpp:147:16:147:27 | *lpOverlapped [*hEvent] | windows.cpp:149:42:149:53 | *lpOverlapped [*hEvent] | provenance | |
| windows.cpp:149:18:149:62 | *hEvent | windows.cpp:149:18:149:62 | *hEvent | provenance | |
| windows.cpp:149:18:149:62 | *hEvent | windows.cpp:151:8:151:14 | * ... | provenance | |
@@ -245,37 +371,37 @@ edges
| windows.cpp:159:12:159:55 | hEvent | windows.cpp:160:8:160:8 | c | provenance | |
| windows.cpp:159:35:159:46 | *lpOverlapped [hEvent] | windows.cpp:159:12:159:55 | hEvent | provenance | |
| windows.cpp:159:35:159:46 | *lpOverlapped [hEvent] | windows.cpp:159:12:159:55 | hEvent | provenance | |
-| windows.cpp:168:35:168:40 | ReadFile output argument | windows.cpp:170:10:170:16 | * ... | provenance | Src:MaD:17 |
-| windows.cpp:177:23:177:28 | ReadFileEx output argument | windows.cpp:179:10:179:16 | * ... | provenance | Src:MaD:18 |
-| windows.cpp:189:21:189:26 | ReadFile output argument | windows.cpp:190:5:190:56 | *... = ... | provenance | Src:MaD:17 |
+| windows.cpp:168:35:168:40 | ReadFile output argument | windows.cpp:170:10:170:16 | * ... | provenance | Src:MaD:19 |
+| windows.cpp:177:23:177:28 | ReadFileEx output argument | windows.cpp:179:10:179:16 | * ... | provenance | Src:MaD:20 |
+| windows.cpp:189:21:189:26 | ReadFile output argument | windows.cpp:190:5:190:56 | *... = ... | provenance | Src:MaD:19 |
| windows.cpp:190:5:190:14 | *overlapped [post update] [*hEvent] | windows.cpp:192:53:192:63 | *& ... [*hEvent] | provenance | |
| windows.cpp:190:5:190:56 | *... = ... | windows.cpp:190:5:190:14 | *overlapped [post update] [*hEvent] | provenance | |
-| windows.cpp:192:53:192:63 | *& ... [*hEvent] | windows.cpp:147:16:147:27 | *lpOverlapped [*hEvent] | provenance | MaD:52 |
-| windows.cpp:198:21:198:26 | ReadFile output argument | windows.cpp:199:5:199:57 | ... = ... | provenance | Src:MaD:17 |
+| windows.cpp:192:53:192:63 | *& ... [*hEvent] | windows.cpp:147:16:147:27 | *lpOverlapped [*hEvent] | provenance | MaD:66 |
+| windows.cpp:198:21:198:26 | ReadFile output argument | windows.cpp:199:5:199:57 | ... = ... | provenance | Src:MaD:19 |
| windows.cpp:199:5:199:14 | *overlapped [post update] [hEvent] | windows.cpp:201:53:201:63 | *& ... [hEvent] | provenance | |
| windows.cpp:199:5:199:57 | ... = ... | windows.cpp:199:5:199:14 | *overlapped [post update] [hEvent] | provenance | |
-| windows.cpp:201:53:201:63 | *& ... [hEvent] | windows.cpp:157:16:157:27 | *lpOverlapped [hEvent] | provenance | MaD:52 |
-| windows.cpp:209:84:209:89 | NtReadFile output argument | windows.cpp:211:10:211:16 | * ... | provenance | Src:MaD:16 |
-| windows.cpp:286:23:286:35 | *call to MapViewOfFile | windows.cpp:286:23:286:35 | *call to MapViewOfFile | provenance | Src:MaD:12 |
+| windows.cpp:201:53:201:63 | *& ... [hEvent] | windows.cpp:157:16:157:27 | *lpOverlapped [hEvent] | provenance | MaD:66 |
+| windows.cpp:209:84:209:89 | NtReadFile output argument | windows.cpp:211:10:211:16 | * ... | provenance | Src:MaD:18 |
| windows.cpp:286:23:286:35 | *call to MapViewOfFile | windows.cpp:287:20:287:52 | *pMapView | provenance | |
+| windows.cpp:286:23:286:35 | call to MapViewOfFile | windows.cpp:286:23:286:35 | *call to MapViewOfFile | provenance | Src:MaD:14 |
| windows.cpp:287:20:287:52 | *pMapView | windows.cpp:289:10:289:16 | * ... | provenance | |
-| windows.cpp:293:23:293:36 | *call to MapViewOfFile2 | windows.cpp:293:23:293:36 | *call to MapViewOfFile2 | provenance | Src:MaD:9 |
| windows.cpp:293:23:293:36 | *call to MapViewOfFile2 | windows.cpp:294:20:294:52 | *pMapView | provenance | |
+| windows.cpp:293:23:293:36 | call to MapViewOfFile2 | windows.cpp:293:23:293:36 | *call to MapViewOfFile2 | provenance | Src:MaD:11 |
| windows.cpp:294:20:294:52 | *pMapView | windows.cpp:296:10:296:16 | * ... | provenance | |
-| windows.cpp:302:23:302:36 | *call to MapViewOfFile3 | windows.cpp:302:23:302:36 | *call to MapViewOfFile3 | provenance | Src:MaD:10 |
| windows.cpp:302:23:302:36 | *call to MapViewOfFile3 | windows.cpp:303:20:303:52 | *pMapView | provenance | |
+| windows.cpp:302:23:302:36 | call to MapViewOfFile3 | windows.cpp:302:23:302:36 | *call to MapViewOfFile3 | provenance | Src:MaD:12 |
| windows.cpp:303:20:303:52 | *pMapView | windows.cpp:305:10:305:16 | * ... | provenance | |
-| windows.cpp:311:23:311:43 | *call to MapViewOfFile3FromApp | windows.cpp:311:23:311:43 | *call to MapViewOfFile3FromApp | provenance | Src:MaD:11 |
| windows.cpp:311:23:311:43 | *call to MapViewOfFile3FromApp | windows.cpp:312:20:312:52 | *pMapView | provenance | |
+| windows.cpp:311:23:311:43 | call to MapViewOfFile3FromApp | windows.cpp:311:23:311:43 | *call to MapViewOfFile3FromApp | provenance | Src:MaD:13 |
| windows.cpp:312:20:312:52 | *pMapView | windows.cpp:314:10:314:16 | * ... | provenance | |
-| windows.cpp:318:23:318:37 | *call to MapViewOfFileEx | windows.cpp:318:23:318:37 | *call to MapViewOfFileEx | provenance | Src:MaD:13 |
| windows.cpp:318:23:318:37 | *call to MapViewOfFileEx | windows.cpp:319:20:319:52 | *pMapView | provenance | |
+| windows.cpp:318:23:318:37 | call to MapViewOfFileEx | windows.cpp:318:23:318:37 | *call to MapViewOfFileEx | provenance | Src:MaD:15 |
| windows.cpp:319:20:319:52 | *pMapView | windows.cpp:321:10:321:16 | * ... | provenance | |
-| windows.cpp:325:23:325:42 | *call to MapViewOfFileFromApp | windows.cpp:325:23:325:42 | *call to MapViewOfFileFromApp | provenance | Src:MaD:14 |
| windows.cpp:325:23:325:42 | *call to MapViewOfFileFromApp | windows.cpp:326:20:326:52 | *pMapView | provenance | |
+| windows.cpp:325:23:325:42 | call to MapViewOfFileFromApp | windows.cpp:325:23:325:42 | *call to MapViewOfFileFromApp | provenance | Src:MaD:16 |
| windows.cpp:326:20:326:52 | *pMapView | windows.cpp:328:10:328:16 | * ... | provenance | |
-| windows.cpp:332:23:332:40 | *call to MapViewOfFileNuma2 | windows.cpp:332:23:332:40 | *call to MapViewOfFileNuma2 | provenance | Src:MaD:15 |
| windows.cpp:332:23:332:40 | *call to MapViewOfFileNuma2 | windows.cpp:333:20:333:52 | *pMapView | provenance | |
+| windows.cpp:332:23:332:40 | call to MapViewOfFileNuma2 | windows.cpp:332:23:332:40 | *call to MapViewOfFileNuma2 | provenance | Src:MaD:17 |
| windows.cpp:333:20:333:52 | *pMapView | windows.cpp:335:10:335:16 | * ... | provenance | |
| windows.cpp:403:26:403:36 | *lpParameter [x] | windows.cpp:405:10:405:25 | *lpParameter [x] | provenance | |
| windows.cpp:405:10:405:25 | *lpParameter [x] | windows.cpp:406:8:406:8 | *s [x] | provenance | |
@@ -291,9 +417,9 @@ edges
| windows.cpp:431:3:431:3 | *s [post update] [x] | windows.cpp:464:7:464:8 | *& ... [x] | provenance | |
| windows.cpp:431:3:431:16 | ... = ... | windows.cpp:431:3:431:3 | *s [post update] [x] | provenance | |
| windows.cpp:431:9:431:14 | call to source | windows.cpp:431:3:431:16 | ... = ... | provenance | |
-| windows.cpp:439:7:439:8 | *& ... [x] | windows.cpp:403:26:403:36 | *lpParameter [x] | provenance | MaD:48 |
-| windows.cpp:451:7:451:8 | *& ... [x] | windows.cpp:410:26:410:36 | *lpParameter [x] | provenance | MaD:46 |
-| windows.cpp:464:7:464:8 | *& ... [x] | windows.cpp:417:26:417:36 | *lpParameter [x] | provenance | MaD:47 |
+| windows.cpp:439:7:439:8 | *& ... [x] | windows.cpp:403:26:403:36 | *lpParameter [x] | provenance | MaD:62 |
+| windows.cpp:451:7:451:8 | *& ... [x] | windows.cpp:410:26:410:36 | *lpParameter [x] | provenance | MaD:60 |
+| windows.cpp:464:7:464:8 | *& ... [x] | windows.cpp:417:26:417:36 | *lpParameter [x] | provenance | MaD:61 |
| windows.cpp:533:11:533:16 | call to source | windows.cpp:533:11:533:16 | call to source | provenance | |
| windows.cpp:533:11:533:16 | call to source | windows.cpp:537:40:537:41 | *& ... | provenance | |
| windows.cpp:533:11:533:16 | call to source | windows.cpp:542:38:542:39 | *& ... | provenance | |
@@ -302,116 +428,116 @@ edges
| windows.cpp:533:11:533:16 | call to source | windows.cpp:568:32:568:33 | *& ... | provenance | |
| windows.cpp:533:11:533:16 | call to source | windows.cpp:573:40:573:41 | *& ... | provenance | |
| windows.cpp:537:27:537:37 | RtlCopyVolatileMemory output argument | windows.cpp:538:10:538:23 | access to array | provenance | |
-| windows.cpp:537:40:537:41 | *& ... | windows.cpp:537:27:537:37 | RtlCopyVolatileMemory output argument | provenance | MaD:57 |
+| windows.cpp:537:40:537:41 | *& ... | windows.cpp:537:27:537:37 | RtlCopyVolatileMemory output argument | provenance | MaD:71 |
| windows.cpp:542:25:542:35 | RtlCopyDeviceMemory output argument | windows.cpp:543:10:543:23 | access to array | provenance | |
-| windows.cpp:542:38:542:39 | *& ... | windows.cpp:542:25:542:35 | RtlCopyDeviceMemory output argument | provenance | MaD:53 |
+| windows.cpp:542:38:542:39 | *& ... | windows.cpp:542:25:542:35 | RtlCopyDeviceMemory output argument | provenance | MaD:67 |
| windows.cpp:547:19:547:29 | RtlCopyMemory output argument | windows.cpp:548:10:548:23 | access to array | provenance | |
-| windows.cpp:547:32:547:33 | *& ... | windows.cpp:547:19:547:29 | RtlCopyMemory output argument | provenance | MaD:54 |
+| windows.cpp:547:32:547:33 | *& ... | windows.cpp:547:19:547:29 | RtlCopyMemory output argument | provenance | MaD:68 |
| windows.cpp:552:30:552:40 | RtlCopyMemoryNonTemporal output argument | windows.cpp:553:10:553:23 | access to array | provenance | |
-| windows.cpp:552:43:552:44 | *& ... | windows.cpp:552:30:552:40 | RtlCopyMemoryNonTemporal output argument | provenance | MaD:55 |
+| windows.cpp:552:43:552:44 | *& ... | windows.cpp:552:30:552:40 | RtlCopyMemoryNonTemporal output argument | provenance | MaD:69 |
| windows.cpp:559:5:559:24 | ... = ... | windows.cpp:561:39:561:44 | *buffer | provenance | |
| windows.cpp:559:17:559:24 | call to source | windows.cpp:559:5:559:24 | ... = ... | provenance | |
| windows.cpp:561:26:561:36 | RtlInitUnicodeString output argument [*Buffer] | windows.cpp:562:10:562:19 | *src_string [*Buffer] | provenance | |
| windows.cpp:561:26:561:36 | RtlInitUnicodeString output argument [*Buffer] | windows.cpp:563:40:563:50 | *& ... [*Buffer] | provenance | |
-| windows.cpp:561:39:561:44 | *buffer | windows.cpp:561:26:561:36 | RtlInitUnicodeString output argument [*Buffer] | provenance | MaD:58 |
+| windows.cpp:561:39:561:44 | *buffer | windows.cpp:561:26:561:36 | RtlInitUnicodeString output argument [*Buffer] | provenance | MaD:72 |
| windows.cpp:562:10:562:19 | *src_string [*Buffer] | windows.cpp:562:10:562:29 | access to array | provenance | |
| windows.cpp:562:10:562:19 | *src_string [*Buffer] | windows.cpp:562:21:562:26 | *Buffer | provenance | |
| windows.cpp:562:21:562:26 | *Buffer | windows.cpp:562:10:562:29 | access to array | provenance | |
| windows.cpp:563:26:563:37 | RtlCopyUnicodeString output argument [*Buffer] | windows.cpp:564:10:564:20 | *dest_string [*Buffer] | provenance | |
-| windows.cpp:563:40:563:50 | *& ... [*Buffer] | windows.cpp:563:26:563:37 | RtlCopyUnicodeString output argument [*Buffer] | provenance | MaD:56 |
+| windows.cpp:563:40:563:50 | *& ... [*Buffer] | windows.cpp:563:26:563:37 | RtlCopyUnicodeString output argument [*Buffer] | provenance | MaD:70 |
| windows.cpp:564:10:564:20 | *dest_string [*Buffer] | windows.cpp:564:10:564:30 | access to array | provenance | |
| windows.cpp:564:10:564:20 | *dest_string [*Buffer] | windows.cpp:564:22:564:27 | *Buffer | provenance | |
| windows.cpp:564:22:564:27 | *Buffer | windows.cpp:564:10:564:30 | access to array | provenance | |
| windows.cpp:568:19:568:29 | RtlMoveMemory output argument | windows.cpp:569:10:569:23 | access to array | provenance | |
-| windows.cpp:568:32:568:33 | *& ... | windows.cpp:568:19:568:29 | RtlMoveMemory output argument | provenance | MaD:59 |
+| windows.cpp:568:32:568:33 | *& ... | windows.cpp:568:19:568:29 | RtlMoveMemory output argument | provenance | MaD:73 |
| windows.cpp:573:27:573:37 | RtlMoveVolatileMemory output argument | windows.cpp:574:10:574:23 | access to array | provenance | |
-| windows.cpp:573:40:573:41 | *& ... | windows.cpp:573:27:573:37 | RtlMoveVolatileMemory output argument | provenance | MaD:60 |
-| windows.cpp:645:45:645:50 | WinHttpReadData output argument | windows.cpp:647:10:647:16 | * ... | provenance | Src:MaD:33 |
-| windows.cpp:652:48:652:53 | WinHttpReadDataEx output argument | windows.cpp:654:10:654:16 | * ... | provenance | Src:MaD:34 |
-| windows.cpp:659:47:659:52 | WinHttpQueryHeaders output argument | windows.cpp:661:10:661:16 | * ... | provenance | Src:MaD:29 |
-| windows.cpp:669:70:669:79 | WinHttpQueryHeadersEx output argument | windows.cpp:673:10:673:29 | * ... | provenance | Src:MaD:31 |
-| windows.cpp:669:82:669:87 | WinHttpQueryHeadersEx output argument | windows.cpp:671:10:671:16 | * ... | provenance | Src:MaD:32 |
-| windows.cpp:669:105:669:112 | WinHttpQueryHeadersEx output argument | windows.cpp:675:10:675:27 | * ... | provenance | Src:MaD:30 |
+| windows.cpp:573:40:573:41 | *& ... | windows.cpp:573:27:573:37 | RtlMoveVolatileMemory output argument | provenance | MaD:74 |
+| windows.cpp:645:45:645:50 | WinHttpReadData output argument | windows.cpp:647:10:647:16 | * ... | provenance | Src:MaD:35 |
+| windows.cpp:652:48:652:53 | WinHttpReadDataEx output argument | windows.cpp:654:10:654:16 | * ... | provenance | Src:MaD:36 |
+| windows.cpp:659:47:659:52 | WinHttpQueryHeaders output argument | windows.cpp:661:10:661:16 | * ... | provenance | Src:MaD:31 |
+| windows.cpp:669:70:669:79 | WinHttpQueryHeadersEx output argument | windows.cpp:673:10:673:29 | * ... | provenance | Src:MaD:33 |
+| windows.cpp:669:82:669:87 | WinHttpQueryHeadersEx output argument | windows.cpp:671:10:671:16 | * ... | provenance | Src:MaD:34 |
+| windows.cpp:669:105:669:112 | WinHttpQueryHeadersEx output argument | windows.cpp:675:10:675:27 | * ... | provenance | Src:MaD:32 |
| windows.cpp:728:5:728:28 | ... = ... | windows.cpp:729:35:729:35 | *x | provenance | |
| windows.cpp:728:12:728:28 | call to source | windows.cpp:728:5:728:28 | ... = ... | provenance | |
-| windows.cpp:729:35:729:35 | *x | windows.cpp:729:44:729:57 | WinHttpCrackUrl output argument | provenance | MaD:64 |
+| windows.cpp:729:35:729:35 | *x | windows.cpp:729:44:729:57 | WinHttpCrackUrl output argument | provenance | MaD:78 |
| windows.cpp:729:44:729:57 | WinHttpCrackUrl output argument | windows.cpp:731:10:731:36 | * ... | provenance | |
| windows.cpp:729:44:729:57 | WinHttpCrackUrl output argument | windows.cpp:733:10:733:35 | * ... | provenance | |
| windows.cpp:729:44:729:57 | WinHttpCrackUrl output argument | windows.cpp:735:10:735:37 | * ... | provenance | |
-| windows.cpp:900:64:900:77 | HttpReceiveHttpRequest output argument | windows.cpp:901:15:901:53 | *& ... | provenance | Src:MaD:7 |
-| windows.cpp:900:64:900:77 | HttpReceiveHttpRequest output argument | windows.cpp:905:10:905:31 | * ... | provenance | Src:MaD:7 |
-| windows.cpp:900:64:900:77 | HttpReceiveHttpRequest output argument | windows.cpp:907:10:907:42 | * ... | provenance | Src:MaD:7 |
-| windows.cpp:900:64:900:77 | HttpReceiveHttpRequest output argument | windows.cpp:909:10:909:57 | * ... | provenance | Src:MaD:7 |
-| windows.cpp:900:64:900:77 | HttpReceiveHttpRequest output argument | windows.cpp:911:10:911:60 | * ... | provenance | Src:MaD:7 |
-| windows.cpp:900:64:900:77 | HttpReceiveHttpRequest output argument | windows.cpp:912:54:912:63 | FileHandle | provenance | Src:MaD:7 |
-| windows.cpp:900:64:900:77 | HttpReceiveHttpRequest output argument | windows.cpp:914:10:914:70 | * ... | provenance | Src:MaD:7 |
-| windows.cpp:900:64:900:77 | HttpReceiveHttpRequest output argument | windows.cpp:916:10:916:72 | * ... | provenance | Src:MaD:7 |
-| windows.cpp:900:64:900:77 | HttpReceiveHttpRequest output argument | windows.cpp:918:10:918:64 | * ... | provenance | Src:MaD:7 |
-| windows.cpp:900:64:900:77 | HttpReceiveHttpRequest output argument | windows.cpp:920:10:920:51 | * ... | provenance | Src:MaD:7 |
-| windows.cpp:900:64:900:77 | HttpReceiveHttpRequest output argument | windows.cpp:922:10:922:52 | * ... | provenance | Src:MaD:7 |
-| windows.cpp:900:64:900:77 | HttpReceiveHttpRequest output argument | windows.cpp:924:10:924:63 | * ... | provenance | Src:MaD:7 |
+| windows.cpp:900:64:900:77 | HttpReceiveHttpRequest output argument | windows.cpp:901:15:901:53 | *& ... | provenance | Src:MaD:9 |
+| windows.cpp:900:64:900:77 | HttpReceiveHttpRequest output argument | windows.cpp:905:10:905:31 | * ... | provenance | Src:MaD:9 |
+| windows.cpp:900:64:900:77 | HttpReceiveHttpRequest output argument | windows.cpp:907:10:907:42 | * ... | provenance | Src:MaD:9 |
+| windows.cpp:900:64:900:77 | HttpReceiveHttpRequest output argument | windows.cpp:909:10:909:57 | * ... | provenance | Src:MaD:9 |
+| windows.cpp:900:64:900:77 | HttpReceiveHttpRequest output argument | windows.cpp:911:10:911:60 | * ... | provenance | Src:MaD:9 |
+| windows.cpp:900:64:900:77 | HttpReceiveHttpRequest output argument | windows.cpp:912:54:912:63 | FileHandle | provenance | Src:MaD:9 |
+| windows.cpp:900:64:900:77 | HttpReceiveHttpRequest output argument | windows.cpp:914:10:914:70 | * ... | provenance | Src:MaD:9 |
+| windows.cpp:900:64:900:77 | HttpReceiveHttpRequest output argument | windows.cpp:916:10:916:72 | * ... | provenance | Src:MaD:9 |
+| windows.cpp:900:64:900:77 | HttpReceiveHttpRequest output argument | windows.cpp:918:10:918:64 | * ... | provenance | Src:MaD:9 |
+| windows.cpp:900:64:900:77 | HttpReceiveHttpRequest output argument | windows.cpp:920:10:920:51 | * ... | provenance | Src:MaD:9 |
+| windows.cpp:900:64:900:77 | HttpReceiveHttpRequest output argument | windows.cpp:922:10:922:52 | * ... | provenance | Src:MaD:9 |
+| windows.cpp:900:64:900:77 | HttpReceiveHttpRequest output argument | windows.cpp:924:10:924:63 | * ... | provenance | Src:MaD:9 |
| windows.cpp:901:15:901:53 | *& ... | windows.cpp:903:10:903:11 | * ... | provenance | |
-| windows.cpp:929:70:929:75 | HttpReceiveRequestEntityBody output argument | windows.cpp:931:10:931:16 | * ... | provenance | Src:MaD:8 |
-| windows.cpp:936:70:936:78 | HttpReceiveClientCertificate output argument | windows.cpp:937:15:937:48 | *& ... | provenance | Src:MaD:6 |
-| windows.cpp:936:70:936:78 | HttpReceiveClientCertificate output argument | windows.cpp:941:10:941:31 | * ... | provenance | Src:MaD:6 |
+| windows.cpp:929:70:929:75 | HttpReceiveRequestEntityBody output argument | windows.cpp:931:10:931:16 | * ... | provenance | Src:MaD:10 |
+| windows.cpp:936:70:936:78 | HttpReceiveClientCertificate output argument | windows.cpp:937:15:937:48 | *& ... | provenance | Src:MaD:8 |
+| windows.cpp:936:70:936:78 | HttpReceiveClientCertificate output argument | windows.cpp:941:10:941:31 | * ... | provenance | Src:MaD:8 |
| windows.cpp:937:15:937:48 | *& ... | windows.cpp:939:10:939:11 | * ... | provenance | |
-| windows.cpp:1009:35:1009:38 | RegQueryValueA output argument | windows.cpp:1011:10:1011:14 | * ... | provenance | Src:MaD:25 |
-| windows.cpp:1016:36:1016:39 | RegQueryValueW output argument | windows.cpp:1018:10:1018:14 | * ... | provenance | Src:MaD:28 |
-| windows.cpp:1024:53:1024:56 | RegQueryValueExA output argument | windows.cpp:1026:10:1026:14 | * ... | provenance | Src:MaD:26 |
-| windows.cpp:1032:54:1032:57 | RegQueryValueExW output argument | windows.cpp:1034:10:1034:14 | * ... | provenance | Src:MaD:27 |
-| windows.cpp:1040:46:1040:49 | RegQueryMultipleValuesA output argument | windows.cpp:1042:10:1042:14 | * ... | provenance | Src:MaD:23 |
-| windows.cpp:1048:46:1048:49 | RegQueryMultipleValuesW output argument | windows.cpp:1050:10:1050:14 | * ... | provenance | Src:MaD:24 |
-| windows.cpp:1056:53:1056:56 | RegGetValueA output argument | windows.cpp:1058:10:1058:14 | * ... | provenance | Src:MaD:21 |
-| windows.cpp:1065:55:1065:58 | RegGetValueW output argument | windows.cpp:1067:10:1067:14 | * ... | provenance | Src:MaD:22 |
-| windows.cpp:1075:28:1075:36 | RegEnumValueA output argument | windows.cpp:1079:10:1079:19 | * ... | provenance | Src:MaD:19 |
-| windows.cpp:1075:71:1075:74 | RegEnumValueA output argument | windows.cpp:1077:10:1077:14 | * ... | provenance | Src:MaD:19 |
-| windows.cpp:1087:28:1087:36 | RegEnumValueW output argument | windows.cpp:1091:10:1091:19 | * ... | provenance | Src:MaD:20 |
-| windows.cpp:1087:71:1087:74 | RegEnumValueW output argument | windows.cpp:1089:10:1089:14 | * ... | provenance | Src:MaD:20 |
+| windows.cpp:1009:35:1009:38 | RegQueryValueA output argument | windows.cpp:1011:10:1011:14 | * ... | provenance | Src:MaD:27 |
+| windows.cpp:1016:36:1016:39 | RegQueryValueW output argument | windows.cpp:1018:10:1018:14 | * ... | provenance | Src:MaD:30 |
+| windows.cpp:1024:53:1024:56 | RegQueryValueExA output argument | windows.cpp:1026:10:1026:14 | * ... | provenance | Src:MaD:28 |
+| windows.cpp:1032:54:1032:57 | RegQueryValueExW output argument | windows.cpp:1034:10:1034:14 | * ... | provenance | Src:MaD:29 |
+| windows.cpp:1040:46:1040:49 | RegQueryMultipleValuesA output argument | windows.cpp:1042:10:1042:14 | * ... | provenance | Src:MaD:25 |
+| windows.cpp:1048:46:1048:49 | RegQueryMultipleValuesW output argument | windows.cpp:1050:10:1050:14 | * ... | provenance | Src:MaD:26 |
+| windows.cpp:1056:53:1056:56 | RegGetValueA output argument | windows.cpp:1058:10:1058:14 | * ... | provenance | Src:MaD:23 |
+| windows.cpp:1065:55:1065:58 | RegGetValueW output argument | windows.cpp:1067:10:1067:14 | * ... | provenance | Src:MaD:24 |
+| windows.cpp:1075:28:1075:36 | RegEnumValueA output argument | windows.cpp:1079:10:1079:19 | * ... | provenance | Src:MaD:21 |
+| windows.cpp:1075:71:1075:74 | RegEnumValueA output argument | windows.cpp:1077:10:1077:14 | * ... | provenance | Src:MaD:21 |
+| windows.cpp:1087:28:1087:36 | RegEnumValueW output argument | windows.cpp:1091:10:1091:19 | * ... | provenance | Src:MaD:22 |
+| windows.cpp:1087:71:1087:74 | RegEnumValueW output argument | windows.cpp:1089:10:1089:14 | * ... | provenance | Src:MaD:22 |
| windows.cpp:1122:5:1122:27 | ... = ... | windows.cpp:1124:19:1124:21 | *str | provenance | |
| windows.cpp:1122:14:1122:27 | call to source | windows.cpp:1122:5:1122:27 | ... = ... | provenance | |
-| windows.cpp:1124:19:1124:21 | *str | windows.cpp:1124:24:1124:27 | IIDFromString output argument | provenance | MaD:50 |
+| windows.cpp:1124:19:1124:21 | *str | windows.cpp:1124:24:1124:27 | IIDFromString output argument | provenance | MaD:64 |
| windows.cpp:1124:24:1124:27 | IIDFromString output argument | windows.cpp:1125:10:1125:12 | iid | provenance | |
| windows.cpp:1128:15:1128:20 | call to source | windows.cpp:1128:15:1128:20 | call to source | provenance | |
| windows.cpp:1128:15:1128:20 | call to source | windows.cpp:1130:19:1130:21 | *iid | provenance | |
-| windows.cpp:1130:19:1130:21 | *iid | windows.cpp:1130:24:1130:27 | StringFromIID output argument | provenance | MaD:63 |
+| windows.cpp:1130:19:1130:21 | *iid | windows.cpp:1130:24:1130:27 | StringFromIID output argument | provenance | MaD:77 |
| windows.cpp:1130:24:1130:27 | StringFromIID output argument | windows.cpp:1132:10:1132:13 | * ... | provenance | |
| windows.cpp:1135:19:1135:24 | call to source | windows.cpp:1135:19:1135:24 | call to source | provenance | |
| windows.cpp:1135:19:1135:24 | call to source | windows.cpp:1137:21:1137:25 | *clsid | provenance | |
-| windows.cpp:1137:21:1137:25 | *clsid | windows.cpp:1137:28:1137:31 | ProgIDFromCLSID output argument | provenance | MaD:51 |
+| windows.cpp:1137:21:1137:25 | *clsid | windows.cpp:1137:28:1137:31 | ProgIDFromCLSID output argument | provenance | MaD:65 |
| windows.cpp:1137:28:1137:31 | ProgIDFromCLSID output argument | windows.cpp:1139:10:1139:13 | * ... | provenance | |
| windows.cpp:1143:5:1143:30 | ... = ... | windows.cpp:1145:21:1145:26 | *progID | provenance | |
| windows.cpp:1143:17:1143:30 | call to source | windows.cpp:1143:5:1143:30 | ... = ... | provenance | |
-| windows.cpp:1145:21:1145:26 | *progID | windows.cpp:1145:29:1145:34 | CLSIDFromProgID output argument | provenance | MaD:43 |
+| windows.cpp:1145:21:1145:26 | *progID | windows.cpp:1145:29:1145:34 | CLSIDFromProgID output argument | provenance | MaD:57 |
| windows.cpp:1145:29:1145:34 | CLSIDFromProgID output argument | windows.cpp:1146:10:1146:14 | clsid | provenance | |
| windows.cpp:1150:5:1150:27 | ... = ... | windows.cpp:1152:21:1152:23 | *str | provenance | |
| windows.cpp:1150:14:1150:27 | call to source | windows.cpp:1150:5:1150:27 | ... = ... | provenance | |
-| windows.cpp:1152:21:1152:23 | *str | windows.cpp:1152:26:1152:31 | CLSIDFromString output argument | provenance | MaD:44 |
+| windows.cpp:1152:21:1152:23 | *str | windows.cpp:1152:26:1152:31 | CLSIDFromString output argument | provenance | MaD:58 |
| windows.cpp:1152:26:1152:31 | CLSIDFromString output argument | windows.cpp:1153:10:1153:14 | clsid | provenance | |
| windows.cpp:1156:19:1156:24 | call to source | windows.cpp:1156:19:1156:24 | call to source | provenance | |
| windows.cpp:1156:19:1156:24 | call to source | windows.cpp:1158:21:1158:25 | *clsid | provenance | |
-| windows.cpp:1158:21:1158:25 | *clsid | windows.cpp:1158:28:1158:31 | StringFromCLSID output argument | provenance | MaD:61 |
+| windows.cpp:1158:21:1158:25 | *clsid | windows.cpp:1158:28:1158:31 | StringFromCLSID output argument | provenance | MaD:75 |
| windows.cpp:1158:28:1158:31 | StringFromCLSID output argument | windows.cpp:1160:10:1160:13 | * ... | provenance | |
| windows.cpp:1164:5:1164:27 | ... = ... | windows.cpp:1166:20:1166:22 | *str | provenance | |
| windows.cpp:1164:14:1164:27 | call to source | windows.cpp:1164:5:1164:27 | ... = ... | provenance | |
-| windows.cpp:1166:20:1166:22 | *str | windows.cpp:1166:25:1166:29 | GUIDFromString output argument | provenance | MaD:49 |
+| windows.cpp:1166:20:1166:22 | *str | windows.cpp:1166:25:1166:29 | GUIDFromString output argument | provenance | MaD:63 |
| windows.cpp:1166:25:1166:29 | GUIDFromString output argument | windows.cpp:1167:10:1167:13 | guid | provenance | |
| windows.cpp:1170:17:1170:22 | call to source | windows.cpp:1170:17:1170:22 | call to source | provenance | |
| windows.cpp:1170:17:1170:22 | call to source | windows.cpp:1172:21:1172:24 | *guid | provenance | |
-| windows.cpp:1172:21:1172:24 | *guid | windows.cpp:1172:27:1172:29 | StringFromGUID2 output argument | provenance | MaD:62 |
+| windows.cpp:1172:21:1172:24 | *guid | windows.cpp:1172:27:1172:29 | StringFromGUID2 output argument | provenance | MaD:76 |
| windows.cpp:1172:27:1172:29 | StringFromGUID2 output argument | windows.cpp:1174:10:1174:13 | * ... | provenance | |
nodes
| asio_streams.cpp:87:34:87:44 | read_until output argument | semmle.label | read_until output argument |
| asio_streams.cpp:91:7:91:17 | recv_buffer | semmle.label | recv_buffer |
-| asio_streams.cpp:93:29:93:39 | *recv_buffer | semmle.label | *recv_buffer |
+| asio_streams.cpp:93:29:93:39 | recv_buffer | semmle.label | recv_buffer |
| asio_streams.cpp:97:37:97:44 | call to source | semmle.label | call to source |
| asio_streams.cpp:98:7:98:14 | send_str | semmle.label | send_str |
| asio_streams.cpp:100:44:100:62 | call to buffer | semmle.label | call to buffer |
| asio_streams.cpp:100:44:100:62 | call to buffer | semmle.label | call to buffer |
| asio_streams.cpp:100:64:100:71 | *send_str | semmle.label | *send_str |
| asio_streams.cpp:101:7:101:17 | send_buffer | semmle.label | send_buffer |
-| asio_streams.cpp:103:29:103:39 | *send_buffer | semmle.label | *send_buffer |
-| azure.cpp:253:48:253:60 | *call to GetBodyStream | semmle.label | *call to GetBodyStream |
+| asio_streams.cpp:103:29:103:39 | send_buffer | semmle.label | send_buffer |
| azure.cpp:253:48:253:60 | *call to GetBodyStream | semmle.label | *call to GetBodyStream |
+| azure.cpp:253:48:253:60 | call to GetBodyStream | semmle.label | call to GetBodyStream |
| azure.cpp:257:5:257:8 | *resp | semmle.label | *resp |
| azure.cpp:257:16:257:21 | Read output argument | semmle.label | Read output argument |
| azure.cpp:258:10:258:16 | * ... | semmle.label | * ... |
@@ -423,19 +549,19 @@ nodes
| azure.cpp:266:44:266:52 | call to ReadToEnd [element] | semmle.label | call to ReadToEnd [element] |
| azure.cpp:267:10:267:12 | vec | semmle.label | vec |
| azure.cpp:267:10:267:12 | vec [element] | semmle.label | vec [element] |
-| azure.cpp:273:62:273:64 | call to GetHeaders | semmle.label | call to GetHeaders |
+| azure.cpp:273:52:273:61 | call to GetHeaders | semmle.label | call to GetHeaders |
| azure.cpp:273:62:273:64 | call to GetHeaders | semmle.label | call to GetHeaders |
| azure.cpp:274:10:274:29 | call to operator[] | semmle.label | call to operator[] |
| azure.cpp:274:14:274:29 | call to operator[] | semmle.label | call to operator[] |
| azure.cpp:274:14:274:29 | call to operator[] | semmle.label | call to operator[] |
| azure.cpp:274:14:274:29 | call to operator[] | semmle.label | call to operator[] |
-| azure.cpp:277:45:277:47 | call to GetBody | semmle.label | call to GetBody |
+| azure.cpp:277:38:277:44 | call to GetBody | semmle.label | call to GetBody |
| azure.cpp:277:45:277:47 | call to GetBody | semmle.label | call to GetBody |
| azure.cpp:278:10:278:13 | body | semmle.label | body |
| azure.cpp:278:10:278:13 | body | semmle.label | body |
| azure.cpp:278:10:278:13 | body | semmle.label | body |
| azure.cpp:281:68:281:84 | *call to ExtractBodyStream | semmle.label | *call to ExtractBodyStream |
-| azure.cpp:281:68:281:84 | *call to ExtractBodyStream | semmle.label | *call to ExtractBodyStream |
+| azure.cpp:281:68:281:84 | call to ExtractBodyStream | semmle.label | call to ExtractBodyStream |
| azure.cpp:282:10:282:38 | call to ReadToEnd | semmle.label | call to ReadToEnd |
| azure.cpp:282:21:282:23 | *call to get | semmle.label | *call to get |
| azure.cpp:282:28:282:36 | call to ReadToEnd [element] | semmle.label | call to ReadToEnd [element] |
@@ -454,6 +580,43 @@ nodes
| azure.cpp:295:10:295:20 | contentType | semmle.label | contentType |
| azure.cpp:295:10:295:20 | contentType | semmle.label | contentType |
| azure.cpp:295:10:295:20 | contentType | semmle.label | contentType |
+| bdlbb.cpp:54:16:54:23 | call to source | semmle.label | call to source |
+| bdlbb.cpp:56:37:56:41 | copy output argument | semmle.label | copy output argument |
+| bdlbb.cpp:56:49:56:52 | *call to data | semmle.label | *call to data |
+| bdlbb.cpp:58:37:58:39 | copy output argument | semmle.label | copy output argument |
+| bdlbb.cpp:58:42:58:45 | *blob | semmle.label | *blob |
+| bdlbb.cpp:59:7:59:10 | * ... | semmle.label | * ... |
+| bdlbb.cpp:63:16:63:23 | call to source | semmle.label | call to source |
+| bdlbb.cpp:65:37:65:41 | copy output argument | semmle.label | copy output argument |
+| bdlbb.cpp:65:49:65:52 | *call to data | semmle.label | *call to data |
+| bdlbb.cpp:66:18:66:21 | *blob | semmle.label | *blob |
+| bdlbb.cpp:66:18:66:38 | *call to data | semmle.label | *call to data |
+| bdlbb.cpp:66:18:66:38 | *call to data | semmle.label | *call to data |
+| bdlbb.cpp:66:29:66:32 | *call to buffer | semmle.label | *call to buffer |
+| bdlbb.cpp:67:7:67:8 | * ... | semmle.label | * ... |
+| bdlbb.cpp:72:16:72:23 | call to source | semmle.label | call to source |
+| bdlbb.cpp:74:37:74:41 | copy output argument | semmle.label | copy output argument |
+| bdlbb.cpp:74:49:74:52 | *call to data | semmle.label | *call to data |
+| bdlbb.cpp:75:18:75:21 | *blob | semmle.label | *blob |
+| bdlbb.cpp:75:18:75:46 | call to get | semmle.label | call to get |
+| bdlbb.cpp:75:29:75:32 | *call to buffer | semmle.label | *call to buffer |
+| bdlbb.cpp:75:39:75:41 | *call to buffer | semmle.label | *call to buffer |
+| bdlbb.cpp:76:7:76:8 | * ... | semmle.label | * ... |
+| bdlbb.cpp:80:16:80:23 | call to source | semmle.label | call to source |
+| bdlbb.cpp:82:37:82:41 | copy output argument | semmle.label | copy output argument |
+| bdlbb.cpp:82:49:82:52 | *call to data | semmle.label | *call to data |
+| bdlbb.cpp:84:12:84:65 | *call to getContiguousRangeOrCopy | semmle.label | *call to getContiguousRangeOrCopy |
+| bdlbb.cpp:84:12:84:65 | *call to getContiguousRangeOrCopy | semmle.label | *call to getContiguousRangeOrCopy |
+| bdlbb.cpp:84:72:84:75 | *blob | semmle.label | *blob |
+| bdlbb.cpp:85:7:85:8 | * ... | semmle.label | * ... |
+| bdlbb.cpp:90:16:90:23 | call to source | semmle.label | call to source |
+| bdlbb.cpp:92:37:92:40 | copy output argument | semmle.label | copy output argument |
+| bdlbb.cpp:92:48:92:51 | *call to data | semmle.label | *call to data |
+| bdlbb.cpp:94:37:94:40 | copy output argument | semmle.label | copy output argument |
+| bdlbb.cpp:94:46:94:48 | *src | semmle.label | *src |
+| bdlbb.cpp:96:37:96:39 | copy output argument | semmle.label | copy output argument |
+| bdlbb.cpp:96:42:96:44 | *dst | semmle.label | *dst |
+| bdlbb.cpp:97:7:97:10 | * ... | semmle.label | * ... |
| test.cpp:7:5:7:30 | *ymlStepGenerated_with_body | semmle.label | *ymlStepGenerated_with_body |
| test.cpp:7:47:7:52 | value2 | semmle.label | value2 |
| test.cpp:7:64:7:69 | value2 | semmle.label | value2 |
@@ -480,6 +643,7 @@ nodes
| test.cpp:47:12:47:19 | *arg [x] | semmle.label | *arg [x] |
| test.cpp:48:13:48:13 | *s [x] | semmle.label | *s [x] |
| test.cpp:48:16:48:16 | x | semmle.label | x |
+| test.cpp:48:16:48:16 | x | semmle.label | x |
| test.cpp:56:2:56:2 | *s [post update] [x] | semmle.label | *s [post update] [x] |
| test.cpp:56:2:56:18 | ... = ... | semmle.label | ... = ... |
| test.cpp:56:8:56:16 | call to ymlSource | semmle.label | call to ymlSource |
@@ -556,20 +720,106 @@ nodes
| test.cpp:218:11:218:11 | x | semmle.label | x |
| test.cpp:222:3:222:3 | operator[] output argument | semmle.label | operator[] output argument |
| test.cpp:222:3:222:20 | ... = ... | semmle.label | ... = ... |
-| test.cpp:222:10:222:20 | call to ymlSource | semmle.label | call to ymlSource |
+| test.cpp:222:10:222:18 | call to ymlSource | semmle.label | call to ymlSource |
| test.cpp:223:12:223:12 | *s | semmle.label | *s |
| test.cpp:223:13:223:15 | call to operator[] | semmle.label | call to operator[] |
| test.cpp:223:13:223:15 | call to operator[] | semmle.label | call to operator[] |
| test.cpp:224:11:224:11 | c | semmle.label | c |
+| test.cpp:242:29:242:29 | *s [value] | semmle.label | *s [value] |
+| test.cpp:243:10:243:10 | *s [value] | semmle.label | *s [value] |
+| test.cpp:243:13:243:17 | value | semmle.label | value |
+| test.cpp:243:13:243:17 | value | semmle.label | value |
+| test.cpp:247:26:247:39 | call to ymlFieldSource | semmle.label | call to ymlFieldSource |
+| test.cpp:247:26:247:39 | call to ymlFieldSource [value] | semmle.label | call to ymlFieldSource [value] |
+| test.cpp:248:10:248:16 | *wrapper [value] | semmle.label | *wrapper [value] |
+| test.cpp:248:18:248:22 | value | semmle.label | value |
+| test.cpp:248:18:248:22 | value | semmle.label | value |
+| test.cpp:250:32:250:32 | source_from_callback_template output argument | semmle.label | source_from_callback_template output argument |
+| test.cpp:250:32:250:32 | source_from_callback_template output argument [value] | semmle.label | source_from_callback_template output argument [value] |
+| test.cpp:251:27:251:27 | source_from_callback_ptr output argument | semmle.label | source_from_callback_ptr output argument |
+| test.cpp:251:27:251:27 | source_from_callback_ptr output argument [value] | semmle.label | source_from_callback_ptr output argument [value] |
+| test.cpp:257:35:257:35 | *s [value] | semmle.label | *s [value] |
+| test.cpp:258:12:258:12 | *s [value] | semmle.label | *s [value] |
+| test.cpp:258:15:258:19 | value | semmle.label | value |
+| test.cpp:258:15:258:19 | value | semmle.label | value |
+| test.cpp:262:27:262:31 | source_from_callback_ptr output argument | semmle.label | source_from_callback_ptr output argument |
+| test.cpp:262:27:262:31 | source_from_callback_ptr output argument [value] | semmle.label | source_from_callback_ptr output argument [value] |
+| test.cpp:264:32:266:2 | source_from_callback_template output argument | semmle.label | source_from_callback_template output argument |
+| test.cpp:264:32:266:2 | source_from_callback_template output argument [value] | semmle.label | source_from_callback_template output argument [value] |
+| test.cpp:264:56:264:56 | *s [value] | semmle.label | *s [value] |
+| test.cpp:265:11:265:11 | *s [value] | semmle.label | *s [value] |
+| test.cpp:265:14:265:18 | value | semmle.label | value |
+| test.cpp:265:14:265:18 | value | semmle.label | value |
+| test.cpp:268:27:268:27 | source_from_callback_ptr output argument | semmle.label | source_from_callback_ptr output argument |
+| test.cpp:268:27:268:27 | source_from_callback_ptr output argument [value] | semmle.label | source_from_callback_ptr output argument [value] |
+| test.cpp:268:51:268:51 | *s [value] | semmle.label | *s [value] |
+| test.cpp:269:11:269:11 | *s [value] | semmle.label | *s [value] |
+| test.cpp:269:14:269:18 | value | semmle.label | value |
+| test.cpp:269:14:269:18 | value | semmle.label | value |
+| test.cpp:273:40:273:40 | *s [value] | semmle.label | *s [value] |
+| test.cpp:274:12:274:12 | *s [value] | semmle.label | *s [value] |
+| test.cpp:274:15:274:19 | value | semmle.label | value |
+| test.cpp:274:15:274:19 | value | semmle.label | value |
+| test.cpp:278:32:278:34 | source_from_callback_template output argument | semmle.label | source_from_callback_template output argument |
+| test.cpp:278:32:278:34 | source_from_callback_template output argument [value] | semmle.label | source_from_callback_template output argument [value] |
+| test.cpp:293:5:293:26 | *callback_returning_int | semmle.label | *callback_returning_int |
+| test.cpp:294:5:294:28 | *callback_returning_int_2 | semmle.label | *callback_returning_int_2 |
+| test.cpp:295:6:295:31 | **callback_returning_ptr_int | semmle.label | **callback_returning_ptr_int |
+| test.cpp:297:5:297:20 | *return_ymlSource | semmle.label | *return_ymlSource |
+| test.cpp:297:33:297:41 | call to ymlSource | semmle.label | call to ymlSource |
+| test.cpp:297:33:297:41 | call to ymlSource | semmle.label | call to ymlSource |
+| test.cpp:299:6:299:28 | **return_ptr_to_ymlSource | semmle.label | **return_ptr_to_ymlSource |
+| test.cpp:299:41:299:52 | *call to ymlSourcePtr | semmle.label | *call to ymlSourcePtr |
+| test.cpp:299:41:299:52 | call to ymlSourcePtr | semmle.label | call to ymlSourcePtr |
+| test.cpp:304:10:304:24 | * ... | semmle.label | * ... |
+| test.cpp:304:11:304:22 | call to ymlSourcePtr | semmle.label | call to ymlSourcePtr |
+| test.cpp:306:39:306:60 | source_from_callback_return_template output argument | semmle.label | source_from_callback_return_template output argument |
+| test.cpp:307:10:307:31 | call to callback_returning_int | semmle.label | call to callback_returning_int |
+| test.cpp:307:10:307:31 | call to callback_returning_int | semmle.label | call to callback_returning_int |
+| test.cpp:309:34:309:57 | source_from_callback_return_ptr output argument | semmle.label | source_from_callback_return_ptr output argument |
+| test.cpp:310:10:310:33 | call to callback_returning_int_2 | semmle.label | call to callback_returning_int_2 |
+| test.cpp:310:10:310:33 | call to callback_returning_int_2 | semmle.label | call to callback_returning_int_2 |
+| test.cpp:312:38:312:63 | source_ptr_from_callback_return_ptr output argument | semmle.label | source_ptr_from_callback_return_ptr output argument |
+| test.cpp:313:13:313:38 | *call to callback_returning_ptr_int | semmle.label | *call to callback_returning_ptr_int |
+| test.cpp:313:13:313:38 | *call to callback_returning_ptr_int | semmle.label | *call to callback_returning_ptr_int |
+| test.cpp:315:10:315:13 | * ... | semmle.label | * ... |
+| test.cpp:317:37:317:64 | [...](...){...} | semmle.label | [...](...){...} |
+| test.cpp:317:39:317:39 | *operator() | semmle.label | *operator() |
+| test.cpp:317:51:317:59 | call to ymlSource | semmle.label | call to ymlSource |
+| test.cpp:317:51:317:59 | call to ymlSource | semmle.label | call to ymlSource |
+| test.cpp:318:37:318:52 | return_ymlSource | semmle.label | return_ymlSource |
+| test.cpp:320:36:320:36 | call to operator int *(*)() | semmle.label | call to operator int *(*)() |
+| test.cpp:320:38:320:38 | **operator() | semmle.label | **operator() |
+| test.cpp:320:50:320:61 | *call to ymlSourcePtr | semmle.label | *call to ymlSourcePtr |
+| test.cpp:320:50:320:61 | call to ymlSourcePtr | semmle.label | call to ymlSourcePtr |
+| test.cpp:321:36:321:58 | return_ptr_to_ymlSource | semmle.label | return_ptr_to_ymlSource |
+| test.cpp:324:36:324:36 | p | semmle.label | p |
+| test.cpp:324:36:324:36 | p | semmle.label | p |
+| test.cpp:324:53:324:53 | s | semmle.label | s |
+| test.cpp:324:53:324:53 | s | semmle.label | s |
+| test.cpp:324:61:324:66 | source | semmle.label | source |
+| test.cpp:325:10:325:10 | *p [value] | semmle.label | *p [value] |
+| test.cpp:325:13:325:17 | value | semmle.label | value |
+| test.cpp:325:13:325:17 | value | semmle.label | value |
+| test.cpp:327:10:327:20 | * ... | semmle.label | * ... |
+| test.cpp:327:10:327:20 | * ... | semmle.label | * ... |
+| test.cpp:327:11:327:11 | *p [*pointer] | semmle.label | *p [*pointer] |
+| test.cpp:329:10:329:10 | *s [value] | semmle.label | *s [value] |
+| test.cpp:329:12:329:16 | value | semmle.label | value |
+| test.cpp:329:12:329:16 | value | semmle.label | value |
+| test.cpp:331:10:331:19 | * ... | semmle.label | * ... |
+| test.cpp:331:10:331:19 | * ... | semmle.label | * ... |
+| test.cpp:331:11:331:11 | *s [*pointer] | semmle.label | *s [*pointer] |
+| test.cpp:334:10:334:16 | * ... | semmle.label | * ... |
| windows.cpp:22:15:22:29 | *call to GetCommandLineA | semmle.label | *call to GetCommandLineA |
-| windows.cpp:22:15:22:29 | *call to GetCommandLineA | semmle.label | *call to GetCommandLineA |
+| windows.cpp:22:15:22:29 | call to GetCommandLineA | semmle.label | call to GetCommandLineA |
| windows.cpp:24:8:24:11 | * ... | semmle.label | * ... |
| windows.cpp:27:17:27:34 | **call to CommandLineToArgvA | semmle.label | **call to CommandLineToArgvA |
| windows.cpp:27:17:27:34 | **call to CommandLineToArgvA | semmle.label | **call to CommandLineToArgvA |
| windows.cpp:27:36:27:38 | *cmd | semmle.label | *cmd |
| windows.cpp:30:8:30:15 | * ... | semmle.label | * ... |
| windows.cpp:34:17:34:38 | *call to GetEnvironmentStringsA | semmle.label | *call to GetEnvironmentStringsA |
-| windows.cpp:34:17:34:38 | *call to GetEnvironmentStringsA | semmle.label | *call to GetEnvironmentStringsA |
+| windows.cpp:34:17:34:38 | call to GetEnvironmentStringsA | semmle.label | call to GetEnvironmentStringsA |
| windows.cpp:36:10:36:13 | * ... | semmle.label | * ... |
| windows.cpp:39:36:39:38 | GetEnvironmentVariableA output argument | semmle.label | GetEnvironmentVariableA output argument |
| windows.cpp:41:10:41:13 | * ... | semmle.label | * ... |
@@ -599,31 +849,31 @@ nodes
| windows.cpp:209:84:209:89 | NtReadFile output argument | semmle.label | NtReadFile output argument |
| windows.cpp:211:10:211:16 | * ... | semmle.label | * ... |
| windows.cpp:286:23:286:35 | *call to MapViewOfFile | semmle.label | *call to MapViewOfFile |
-| windows.cpp:286:23:286:35 | *call to MapViewOfFile | semmle.label | *call to MapViewOfFile |
+| windows.cpp:286:23:286:35 | call to MapViewOfFile | semmle.label | call to MapViewOfFile |
| windows.cpp:287:20:287:52 | *pMapView | semmle.label | *pMapView |
| windows.cpp:289:10:289:16 | * ... | semmle.label | * ... |
| windows.cpp:293:23:293:36 | *call to MapViewOfFile2 | semmle.label | *call to MapViewOfFile2 |
-| windows.cpp:293:23:293:36 | *call to MapViewOfFile2 | semmle.label | *call to MapViewOfFile2 |
+| windows.cpp:293:23:293:36 | call to MapViewOfFile2 | semmle.label | call to MapViewOfFile2 |
| windows.cpp:294:20:294:52 | *pMapView | semmle.label | *pMapView |
| windows.cpp:296:10:296:16 | * ... | semmle.label | * ... |
| windows.cpp:302:23:302:36 | *call to MapViewOfFile3 | semmle.label | *call to MapViewOfFile3 |
-| windows.cpp:302:23:302:36 | *call to MapViewOfFile3 | semmle.label | *call to MapViewOfFile3 |
+| windows.cpp:302:23:302:36 | call to MapViewOfFile3 | semmle.label | call to MapViewOfFile3 |
| windows.cpp:303:20:303:52 | *pMapView | semmle.label | *pMapView |
| windows.cpp:305:10:305:16 | * ... | semmle.label | * ... |
| windows.cpp:311:23:311:43 | *call to MapViewOfFile3FromApp | semmle.label | *call to MapViewOfFile3FromApp |
-| windows.cpp:311:23:311:43 | *call to MapViewOfFile3FromApp | semmle.label | *call to MapViewOfFile3FromApp |
+| windows.cpp:311:23:311:43 | call to MapViewOfFile3FromApp | semmle.label | call to MapViewOfFile3FromApp |
| windows.cpp:312:20:312:52 | *pMapView | semmle.label | *pMapView |
| windows.cpp:314:10:314:16 | * ... | semmle.label | * ... |
| windows.cpp:318:23:318:37 | *call to MapViewOfFileEx | semmle.label | *call to MapViewOfFileEx |
-| windows.cpp:318:23:318:37 | *call to MapViewOfFileEx | semmle.label | *call to MapViewOfFileEx |
+| windows.cpp:318:23:318:37 | call to MapViewOfFileEx | semmle.label | call to MapViewOfFileEx |
| windows.cpp:319:20:319:52 | *pMapView | semmle.label | *pMapView |
| windows.cpp:321:10:321:16 | * ... | semmle.label | * ... |
| windows.cpp:325:23:325:42 | *call to MapViewOfFileFromApp | semmle.label | *call to MapViewOfFileFromApp |
-| windows.cpp:325:23:325:42 | *call to MapViewOfFileFromApp | semmle.label | *call to MapViewOfFileFromApp |
+| windows.cpp:325:23:325:42 | call to MapViewOfFileFromApp | semmle.label | call to MapViewOfFileFromApp |
| windows.cpp:326:20:326:52 | *pMapView | semmle.label | *pMapView |
| windows.cpp:328:10:328:16 | * ... | semmle.label | * ... |
| windows.cpp:332:23:332:40 | *call to MapViewOfFileNuma2 | semmle.label | *call to MapViewOfFileNuma2 |
-| windows.cpp:332:23:332:40 | *call to MapViewOfFileNuma2 | semmle.label | *call to MapViewOfFileNuma2 |
+| windows.cpp:332:23:332:40 | call to MapViewOfFileNuma2 | semmle.label | call to MapViewOfFileNuma2 |
| windows.cpp:333:20:333:52 | *pMapView | semmle.label | *pMapView |
| windows.cpp:335:10:335:16 | * ... | semmle.label | * ... |
| windows.cpp:403:26:403:36 | *lpParameter [x] | semmle.label | *lpParameter [x] |
diff --git a/cpp/ql/test/library-tests/dataflow/external-models/flow.ext.yml b/cpp/ql/test/library-tests/dataflow/external-models/flow.ext.yml
index 130e13a92571..0db87b5da615 100644
--- a/cpp/ql/test/library-tests/dataflow/external-models/flow.ext.yml
+++ b/cpp/ql/test/library-tests/dataflow/external-models/flow.ext.yml
@@ -4,11 +4,26 @@ extensions:
extensible: sourceModel
data: # namespace, type, subtypes, name, signature, ext, output, kind, provenance
- ["", "", False, "ymlSource", "", "", "ReturnValue", "local", "manual"]
+ - ["", "", False, "ymlSourcePtr", "", "", "ReturnValue[*]", "local", "manual"]
+ - ["", "", False, "ymlFieldSource", "", "", "ReturnValue.Field[SourceWrapper::value]", "local", "manual"]
+ - ["", "", False, "source_from_callback_template", "", "", "Argument[0].Parameter[*0].Field[SourceWrapper::value]", "local", "manual"]
+ - ["", "", False, "source_from_callback_ptr", "", "", "Argument[0].Parameter[*0].Field[SourceWrapper::value]", "local", "manual"]
+ - ["", "", False, "source_from_callback_return_template", "", "", "Argument[0].ReturnValue", "local", "manual"]
+ - ["", "", False, "source_from_callback_return_ptr", "", "", "Argument[0].ReturnValue", "local", "manual"]
+ - ["", "", False, "source_ptr_from_callback_return_ptr", "", "", "Argument[0].ReturnValue[*]", "local", "manual"]
+ - ["", "", False, "test_parameter", "", "", "Parameter[*0].Field[SourceWrapper::value]", "local", "manual"]
+ - ["", "", False, "test_parameter", "", "", "Parameter[*0].Field[*SourceWrapper::pointer]", "local", "manual"]
+ - ["", "", False, "test_parameter", "", "", "Parameter[1].Field[*SourceWrapper::pointer]", "local", "manual"]
+ - ["", "", False, "test_parameter", "", "", "Parameter[1].Field[SourceWrapper::value]", "local", "manual"]
+ - ["", "", False, "test_parameter", "", "", "Parameter[*2]", "local", "manual"]
- addsTo:
pack: codeql/cpp-all
extensible: sinkModel
data: # namespace, type, subtypes, name, signature, ext, input, kind, provenance
- ["", "", False, "ymlSink", "", "", "Argument[0]", "test-sink", "manual"]
+ - ["", "", False, "sink_from_callback_return_template", "", "", "Argument[0].ReturnValue", "test-sink", "manual"]
+ - ["", "", False, "sink_from_callback_return_ptr", "", "", "Argument[0].ReturnValue", "test-sink", "manual"]
+ - ["", "", False, "sink_ptr_from_callback_return_ptr", "", "", "Argument[0].ReturnValue[*]", "test-sink", "manual"]
- addsTo:
pack: codeql/cpp-all
extensible: summaryModel
diff --git a/cpp/ql/test/library-tests/dataflow/external-models/sinks.expected b/cpp/ql/test/library-tests/dataflow/external-models/sinks.expected
index a1f44de81589..5851e825013d 100644
--- a/cpp/ql/test/library-tests/dataflow/external-models/sinks.expected
+++ b/cpp/ql/test/library-tests/dataflow/external-models/sinks.expected
@@ -1,5 +1,5 @@
-| asio_streams.cpp:93:29:93:39 | *recv_buffer | remote-sink |
-| asio_streams.cpp:103:29:103:39 | *send_buffer | remote-sink |
+| asio_streams.cpp:93:29:93:39 | recv_buffer | remote-sink |
+| asio_streams.cpp:103:29:103:39 | send_buffer | remote-sink |
| test.cpp:12:10:12:10 | 0 | test-sink |
| test.cpp:14:10:14:10 | x | test-sink |
| test.cpp:18:10:18:10 | y | test-sink |
@@ -23,3 +23,23 @@
| test.cpp:201:10:201:10 | x | test-sink |
| test.cpp:218:11:218:11 | x | test-sink |
| test.cpp:224:11:224:11 | c | test-sink |
+| test.cpp:243:13:243:17 | value | test-sink |
+| test.cpp:248:18:248:22 | value | test-sink |
+| test.cpp:258:15:258:19 | value | test-sink |
+| test.cpp:265:14:265:18 | value | test-sink |
+| test.cpp:269:14:269:18 | value | test-sink |
+| test.cpp:274:15:274:19 | value | test-sink |
+| test.cpp:303:15:303:26 | call to ymlSourcePtr | test-sink |
+| test.cpp:304:10:304:24 | * ... | test-sink |
+| test.cpp:307:10:307:31 | call to callback_returning_int | test-sink |
+| test.cpp:310:10:310:33 | call to callback_returning_int_2 | test-sink |
+| test.cpp:314:15:314:17 | ptr | test-sink |
+| test.cpp:315:10:315:13 | * ... | test-sink |
+| test.cpp:325:13:325:17 | value | test-sink |
+| test.cpp:326:18:326:24 | pointer | test-sink |
+| test.cpp:327:10:327:20 | * ... | test-sink |
+| test.cpp:329:12:329:16 | value | test-sink |
+| test.cpp:330:17:330:23 | pointer | test-sink |
+| test.cpp:331:10:331:19 | * ... | test-sink |
+| test.cpp:333:15:333:20 | source | test-sink |
+| test.cpp:334:10:334:16 | * ... | test-sink |
diff --git a/cpp/ql/test/library-tests/dataflow/external-models/sources.expected b/cpp/ql/test/library-tests/dataflow/external-models/sources.expected
index 3556bd9d51dd..b30f1e88b99a 100644
--- a/cpp/ql/test/library-tests/dataflow/external-models/sources.expected
+++ b/cpp/ql/test/library-tests/dataflow/external-models/sources.expected
@@ -1,8 +1,8 @@
| asio_streams.cpp:87:34:87:44 | read_until output argument | remote |
-| azure.cpp:253:48:253:60 | *call to GetBodyStream | remote |
-| azure.cpp:273:62:273:64 | call to GetHeaders | remote |
-| azure.cpp:277:45:277:47 | call to GetBody | remote |
-| azure.cpp:281:68:281:84 | *call to ExtractBodyStream | remote |
+| azure.cpp:253:48:253:60 | call to GetBodyStream | remote |
+| azure.cpp:273:52:273:61 | call to GetHeaders | remote |
+| azure.cpp:277:38:277:44 | call to GetBody | remote |
+| azure.cpp:281:68:281:84 | call to ExtractBodyStream | remote |
| azure.cpp:289:32:289:40 | call to GetHeader | remote |
| azure.cpp:293:58:293:67 | call to GetHeaders | remote |
| test.cpp:10:10:10:18 | call to ymlSource | local |
@@ -16,9 +16,11 @@
| test.cpp:186:14:186:22 | call to ymlSource | local |
| test.cpp:199:14:199:22 | call to ymlSource | local |
| test.cpp:216:18:216:26 | call to ymlSource | local |
-| test.cpp:222:10:222:20 | call to ymlSource | local |
-| windows.cpp:22:15:22:29 | *call to GetCommandLineA | local |
-| windows.cpp:34:17:34:38 | *call to GetEnvironmentStringsA | local |
+| test.cpp:222:10:222:18 | call to ymlSource | local |
+| test.cpp:297:33:297:41 | call to ymlSource | local |
+| test.cpp:317:51:317:59 | call to ymlSource | local |
+| windows.cpp:22:15:22:29 | call to GetCommandLineA | local |
+| windows.cpp:34:17:34:38 | call to GetEnvironmentStringsA | local |
| windows.cpp:39:36:39:38 | GetEnvironmentVariableA output argument | local |
| windows.cpp:168:35:168:40 | ReadFile output argument | local |
| windows.cpp:177:23:177:28 | ReadFileEx output argument | local |
@@ -27,13 +29,13 @@
| windows.cpp:198:21:198:26 | ReadFile output argument | local |
| windows.cpp:201:23:201:29 | ReadFileEx output argument | local |
| windows.cpp:209:84:209:89 | NtReadFile output argument | local |
-| windows.cpp:286:23:286:35 | *call to MapViewOfFile | local |
-| windows.cpp:293:23:293:36 | *call to MapViewOfFile2 | local |
-| windows.cpp:302:23:302:36 | *call to MapViewOfFile3 | local |
-| windows.cpp:311:23:311:43 | *call to MapViewOfFile3FromApp | local |
-| windows.cpp:318:23:318:37 | *call to MapViewOfFileEx | local |
-| windows.cpp:325:23:325:42 | *call to MapViewOfFileFromApp | local |
-| windows.cpp:332:23:332:40 | *call to MapViewOfFileNuma2 | local |
+| windows.cpp:286:23:286:35 | call to MapViewOfFile | local |
+| windows.cpp:293:23:293:36 | call to MapViewOfFile2 | local |
+| windows.cpp:302:23:302:36 | call to MapViewOfFile3 | local |
+| windows.cpp:311:23:311:43 | call to MapViewOfFile3FromApp | local |
+| windows.cpp:318:23:318:37 | call to MapViewOfFileEx | local |
+| windows.cpp:325:23:325:42 | call to MapViewOfFileFromApp | local |
+| windows.cpp:332:23:332:40 | call to MapViewOfFileNuma2 | local |
| windows.cpp:645:45:645:50 | WinHttpReadData output argument | remote |
| windows.cpp:652:48:652:53 | WinHttpReadDataEx output argument | remote |
| windows.cpp:659:47:659:52 | WinHttpQueryHeaders output argument | remote |
diff --git a/cpp/ql/test/library-tests/dataflow/external-models/steps.expected b/cpp/ql/test/library-tests/dataflow/external-models/steps.expected
index 0fe13460cfbf..42d2c0183c34 100644
--- a/cpp/ql/test/library-tests/dataflow/external-models/steps.expected
+++ b/cpp/ql/test/library-tests/dataflow/external-models/steps.expected
@@ -4,6 +4,20 @@
| azure.cpp:262:5:262:8 | *resp | azure.cpp:262:23:262:28 | ReadToCount output argument |
| azure.cpp:287:79:287:98 | call to string | azure.cpp:287:62:287:99 | call to Url |
| azure.cpp:289:24:289:56 | call to GetHeader | azure.cpp:289:63:289:65 | call to Value |
+| bdlbb.cpp:56:49:56:52 | *call to data | bdlbb.cpp:56:37:56:41 | copy output argument |
+| bdlbb.cpp:58:42:58:45 | *blob | bdlbb.cpp:58:37:58:39 | copy output argument |
+| bdlbb.cpp:65:49:65:52 | *call to data | bdlbb.cpp:65:37:65:41 | copy output argument |
+| bdlbb.cpp:66:18:66:21 | *blob | bdlbb.cpp:66:29:66:32 | *call to buffer |
+| bdlbb.cpp:66:29:66:32 | *call to buffer | bdlbb.cpp:66:18:66:38 | *call to data |
+| bdlbb.cpp:74:49:74:52 | *call to data | bdlbb.cpp:74:37:74:41 | copy output argument |
+| bdlbb.cpp:75:18:75:21 | *blob | bdlbb.cpp:75:29:75:32 | *call to buffer |
+| bdlbb.cpp:75:29:75:32 | *call to buffer | bdlbb.cpp:75:39:75:41 | *call to buffer |
+| bdlbb.cpp:82:49:82:52 | *call to data | bdlbb.cpp:82:37:82:41 | copy output argument |
+| bdlbb.cpp:84:72:84:75 | *blob | bdlbb.cpp:84:12:84:65 | *call to getContiguousRangeOrCopy |
+| bdlbb.cpp:84:72:84:75 | *blob | bdlbb.cpp:84:67:84:69 | getContiguousRangeOrCopy output argument |
+| bdlbb.cpp:92:48:92:51 | *call to data | bdlbb.cpp:92:37:92:40 | copy output argument |
+| bdlbb.cpp:94:46:94:48 | *src | bdlbb.cpp:94:37:94:40 | copy output argument |
+| bdlbb.cpp:96:42:96:44 | *dst | bdlbb.cpp:96:37:96:39 | copy output argument |
| test.cpp:17:24:17:24 | x | test.cpp:17:10:17:22 | call to ymlStepManual |
| test.cpp:21:27:21:27 | x | test.cpp:21:10:21:25 | call to ymlStepGenerated |
| test.cpp:25:35:25:35 | x | test.cpp:25:11:25:33 | call to ymlStepManual_with_body |
diff --git a/cpp/ql/test/library-tests/dataflow/external-models/test.cpp b/cpp/ql/test/library-tests/dataflow/external-models/test.cpp
index ebb20bab6497..739c36bc67d3 100644
--- a/cpp/ql/test/library-tests/dataflow/external-models/test.cpp
+++ b/cpp/ql/test/library-tests/dataflow/external-models/test.cpp
@@ -1,5 +1,5 @@
-int ymlSource();
+int ymlSource(); int* ymlSourcePtr();
void ymlSink(int value);
int ymlStepManual(int value);
int ymlStepGenerated(int value);
@@ -223,4 +223,113 @@ void test_reverse_flow(unsigned i, unsigned j) {
char c = s[j];
ymlSink(c); // $ ir
}
+}
+
+
+struct SourceWrapper {
+ int value; int* pointer;
+};
+
+SourceWrapper ymlFieldSource();
+
+template
+void source_from_callback_template(F);
+
+using Callback = void(*)(const SourceWrapper*);
+
+void source_from_callback_ptr(Callback);
+
+void f(const SourceWrapper* s) {
+ ymlSink(s->value); // $ ir=250:32 ir=251:27 ir=262:27
+}
+
+void test_source_access_path(bool b) {
+ SourceWrapper wrapper = ymlFieldSource();
+ ymlSink(wrapper.value); // $ ir
+
+ source_from_callback_template(f);
+ source_from_callback_ptr(f);
+
+ Callback f_var;
+ if(b) {
+ f_var = f;
+ } else {
+ f_var = [](const SourceWrapper* s) {
+ ymlSink(s->value); // $ ir
+ };
+ }
+
+ source_from_callback_ptr(f_var);
+
+ source_from_callback_template([](const SourceWrapper* s) {
+ ymlSink(s->value); // $ ir
+ });
+
+ source_from_callback_ptr([](const SourceWrapper* s) {
+ ymlSink(s->value); // $ ir
+ });
+
+ struct S {
+ void operator()(const SourceWrapper* s) {
+ ymlSink(s->value); // $ ir
+ }
+ };
+
+ source_from_callback_template(S());
+}
+
+template void source_from_callback_return_template(F);
+template void sink_from_callback_return_template(F);
+
+using IntCallback = int(*)(void);
+using IntPtrCallback = int*(*)(void);
+
+void source_from_callback_return_ptr(IntCallback);
+void sink_from_callback_return_ptr(IntCallback);
+
+void source_ptr_from_callback_return_ptr(IntPtrCallback);
+void sink_ptr_from_callback_return_ptr(IntPtrCallback);
+
+int callback_returning_int() { return 0; }
+int callback_returning_int_2() { return 0; }
+int* callback_returning_ptr_int() { return nullptr; }
+
+int return_ymlSource() { return ymlSource(); }
+
+int* return_ptr_to_ymlSource() { return ymlSourcePtr(); }
+
+void test_callback_return_access_paths() {
+
+ ymlSink((int)ymlSourcePtr()); // clean
+ ymlSink(*ymlSourcePtr()); // $ ir
+
+ source_from_callback_return_template(callback_returning_int);
+ ymlSink(callback_returning_int()); // $ ir
+
+ source_from_callback_return_ptr(callback_returning_int_2);
+ ymlSink(callback_returning_int_2()); // $ ir
+
+ source_ptr_from_callback_return_ptr(callback_returning_ptr_int);
+ int *ptr = callback_returning_ptr_int();
+ ymlSink((int)ptr); // clean
+ ymlSink(*ptr); // $ ir
+
+ sink_from_callback_return_template([]() { return ymlSource(); }); // $ ir
+ sink_from_callback_return_template(return_ymlSource); // $ ir
+
+ sink_ptr_from_callback_return_ptr([]() { return ymlSourcePtr(); }); // $ ir
+ sink_ptr_from_callback_return_ptr(return_ptr_to_ymlSource); // $ ir
+}
+
+void test_parameter(SourceWrapper* p, SourceWrapper s, int* source) {
+ ymlSink(p->value); // $ ir
+ ymlSink((int)p->pointer); // clean
+ ymlSink(*p->pointer); // $ ir
+
+ ymlSink(s.value); // $ ir
+ ymlSink((int)s.pointer); // clean
+ ymlSink(*s.pointer); // $ ir
+
+ ymlSink((int)source); // clean
+ ymlSink(*source); // $ ir
}
\ No newline at end of file
diff --git a/cpp/ql/test/library-tests/dataflow/external-models/validatemodels.expected b/cpp/ql/test/library-tests/dataflow/external-models/validatemodels.expected
index 15ae50bddc26..1fbe5da66459 100644
--- a/cpp/ql/test/library-tests/dataflow/external-models/validatemodels.expected
+++ b/cpp/ql/test/library-tests/dataflow/external-models/validatemodels.expected
@@ -370,6 +370,8 @@
| Dubious signature "(BN_MONT_CTX *,const BIGNUM *,int,const unsigned char *,size_t,uint32_t,uint32_t)" in summary model. |
| Dubious signature "(BN_RECP_CTX *,const BIGNUM *,BN_CTX *)" in summary model. |
| Dubious signature "(BUF_MEM *,size_t)" in summary model. |
+| Dubious signature "(Blob *,int,const Blob &,int,int)" in summary model. |
+| Dubious signature "(Blob *,int,const char *,int)" in summary model. |
| Dubious signature "(BrotliBitReader *const,uint64_t,uint64_t *)" in summary model. |
| Dubious signature "(BrotliDecoderState *,BrotliDecoderStateInternal *,BrotliSharedDictionaryType,size_t,const uint8_t[])" in summary model. |
| Dubious signature "(BrotliDecoderState *,BrotliDecoderStateInternal *,brotli_decoder_metadata_start_func,brotli_decoder_metadata_chunk_func,void *)" in summary model. |
@@ -2948,6 +2950,7 @@
| Dubious signature "(char *,char *__restrict__,int,FILE *,FILE *__restrict__)" in summary model. |
| Dubious signature "(char *,char *__restrict__,size_t,const char *,const char *__restrict__,const tm *,const tm *__restrict__,locale_t)" in summary model. |
| Dubious signature "(char *,char,char **)" in summary model. |
+| Dubious signature "(char *,const Blob &,int,int)" in summary model. |
| Dubious signature "(char *,const char *)" in summary model. |
| Dubious signature "(char *,const char **,const char **,const char **,const char **,const char **)" in summary model. |
| Dubious signature "(char *,const char *,char **)" in summary model. |
diff --git a/cpp/ql/test/library-tests/dataflow/models-as-data/testModels.expected b/cpp/ql/test/library-tests/dataflow/models-as-data/testModels.expected
index 7d1e2bc9327a..ee106e24edb1 100644
--- a/cpp/ql/test/library-tests/dataflow/models-as-data/testModels.expected
+++ b/cpp/ql/test/library-tests/dataflow/models-as-data/testModels.expected
@@ -283,6 +283,42 @@ sourceCallables
| tests.cpp:433:6:433:10 | array |
| tests.cpp:434:6:434:6 | y |
flowSummaryNode
+| tests.cpp:36:7:36:20 | call to localMadSource | | | test_sources |
+| tests.cpp:37:7:37:21 | call to remoteMadSource | | | test_sources |
+| tests.cpp:39:7:39:24 | call to localMadSourceVoid | | | test_sources |
+| tests.cpp:40:7:40:27 | call to localMadSourceHasBody | | | test_sources |
+| tests.cpp:45:10:45:23 | call to localMadSource | | | test_sources |
+| tests.cpp:53:7:53:29 | call to remoteMadSourceIndirect | | | test_sources |
+| tests.cpp:54:8:54:30 | call to remoteMadSourceIndirect | | | test_sources |
+| tests.cpp:55:8:55:36 | call to remoteMadSourceDoubleIndirect | | | test_sources |
+| tests.cpp:56:9:56:37 | call to remoteMadSourceDoubleIndirect | | | test_sources |
+| tests.cpp:60:30:60:31 | remoteMadSourceIndirectArg0 output argument | | | test_sources |
+| tests.cpp:63:33:63:33 | remoteMadSourceIndirectArg1 output argument | | | test_sources |
+| tests.cpp:67:10:67:23 | call to localMadSource | | | test_sources |
+| tests.cpp:70:7:70:57 | call to namespace2LocalMadSource | | | test_sources |
+| tests.cpp:75:32:75:32 | x | | | remoteMadSourceParam0 |
+| tests.cpp:95:14:95:19 | call to source | | | test_sinks |
+| tests.cpp:97:24:97:24 | 0 | | | test_sinks |
+| tests.cpp:98:17:98:22 | call to source | | | test_sinks |
+| tests.cpp:99:15:99:20 | call to source | | | test_sinks |
+| tests.cpp:99:25:99:25 | 0 | | | test_sinks |
+| tests.cpp:100:15:100:15 | 0 | | | test_sinks |
+| tests.cpp:100:18:100:23 | call to source | | | test_sinks |
+| tests.cpp:101:15:101:15 | 0 | | | test_sinks |
+| tests.cpp:101:18:101:18 | 0 | | | test_sinks |
+| tests.cpp:102:15:102:20 | call to source | | | test_sinks |
+| tests.cpp:102:28:102:28 | 0 | | | test_sinks |
+| tests.cpp:103:15:103:15 | 0 | | | test_sinks |
+| tests.cpp:103:28:103:28 | 0 | | | test_sinks |
+| tests.cpp:104:15:104:15 | 0 | | | test_sinks |
+| tests.cpp:104:21:104:26 | call to source | | | test_sinks |
+| tests.cpp:108:22:108:23 | & ... | | | test_sinks |
+| tests.cpp:109:22:109:26 | a_ptr | | | test_sinks |
+| tests.cpp:110:28:110:33 | & ... | | | test_sinks |
+| tests.cpp:111:14:111:27 | call to localMadSource | | | test_sinks |
+| tests.cpp:111:14:111:27 | call to localMadSource | | | test_sinks |
+| tests.cpp:112:22:112:44 | call to remoteMadSourceIndirect | | | test_sinks |
+| tests.cpp:112:22:112:44 | call to remoteMadSourceIndirect | | | test_sinks |
| tests.cpp:127:5:127:19 | [summary param] 0 in madArg0ToReturn | ParameterNode | madArg0ToReturn | madArg0ToReturn |
| tests.cpp:127:5:127:19 | [summary] to write: ReturnValue in madArg0ToReturn | ReturnNode | madArg0ToReturn | madArg0ToReturn |
| tests.cpp:128:6:128:28 | [summary param] 0 in madArg0ToReturnIndirect | ParameterNode | madArg0ToReturnIndirect | madArg0ToReturnIndirect |
@@ -325,6 +361,12 @@ flowSummaryNode
| tests.cpp:148:13:148:40 | [summary param] 0 in madArg0ToReturnFieldIndirect | ParameterNode | madArg0ToReturnFieldIndirect | madArg0ToReturnFieldIndirect |
| tests.cpp:148:13:148:40 | [summary] to write: ReturnValue in madArg0ToReturnFieldIndirect | ReturnNode | madArg0ToReturnFieldIndirect | madArg0ToReturnFieldIndirect |
| tests.cpp:148:13:148:40 | [summary] to write: ReturnValue.Field[*MyContainer::ptr]/Field[*ptr] in madArg0ToReturnFieldIndirect | | madArg0ToReturnFieldIndirect | madArg0ToReturnFieldIndirect |
+| tests.cpp:225:14:225:28 | call to madArg0ToReturn | | | test_summaries |
+| tests.cpp:225:30:225:44 | call to remoteMadSource | | | test_summaries |
+| tests.cpp:226:14:226:37 | call to madArg0ToReturnValueFlow | | | test_summaries |
+| tests.cpp:226:39:226:53 | call to remoteMadSource | | | test_summaries |
+| tests.cpp:227:14:227:36 | call to madArg0IndirectToReturn | | | test_summaries |
+| tests.cpp:228:14:228:36 | call to madArg0IndirectToReturn | | | test_summaries |
| tests.cpp:250:7:250:19 | [summary param] 0 in madArg0ToSelf | ParameterNode | madArg0ToSelf | madArg0ToSelf |
| tests.cpp:250:7:250:19 | [summary param] this in madArg0ToSelf | ParameterNode | madArg0ToSelf | madArg0ToSelf |
| tests.cpp:250:7:250:19 | [summary] to write: Argument[this] in madArg0ToSelf | PostUpdateNode | madArg0ToSelf | madArg0ToSelf |
@@ -339,6 +381,27 @@ flowSummaryNode
| tests.cpp:254:6:254:21 | [summary] to write: ReturnValue in madFieldToReturn | ReturnNode | madFieldToReturn | madFieldToReturn |
| tests.cpp:277:7:277:30 | [summary param] this in namespaceMadSelfToReturn | ParameterNode | namespaceMadSelfToReturn | namespaceMadSelfToReturn |
| tests.cpp:277:7:277:30 | [summary] to write: ReturnValue in namespaceMadSelfToReturn | ReturnNode | namespaceMadSelfToReturn | namespaceMadSelfToReturn |
+| tests.cpp:292:10:292:30 | call to memberRemoteMadSource | | | test_class_members |
+| tests.cpp:295:39:295:40 | memberRemoteMadSourceIndirectArg0 output argument | | | test_class_members |
+| tests.cpp:300:11:300:31 | call to memberRemoteMadSource | | | test_class_members |
+| tests.cpp:301:11:301:33 | call to subtypeRemoteMadSource1 | | | test_class_members |
+| tests.cpp:303:11:303:33 | call to subtypeRemoteMadSource2 | | | test_class_members |
+| tests.cpp:307:23:307:28 | call to source | | | test_class_members |
+| tests.cpp:309:33:309:38 | call to source | | | test_class_members |
+| tests.cpp:310:57:310:62 | call to source | | | test_class_members |
+| tests.cpp:351:26:351:46 | call to memberRemoteMadSource | | | test_class_members |
+| tests.cpp:351:26:351:46 | call to memberRemoteMadSource | | | test_class_members |
+| tests.cpp:362:2:362:4 | mc8 | | | test_class_members |
+| tests.cpp:362:24:362:24 | 0 | | | test_class_members |
+| tests.cpp:363:2:363:4 | mc8 | | | test_class_members |
+| tests.cpp:363:24:363:29 | call to source | | | test_class_members |
+| tests.cpp:366:2:366:4 | mc9 | | | test_class_members |
+| tests.cpp:367:2:367:4 | mc9 | | | test_class_members |
+| tests.cpp:367:24:367:24 | 0 | | | test_class_members |
+| tests.cpp:369:2:369:4 | qualifierSource output argument | | | test_class_members |
+| tests.cpp:371:2:371:4 | mc8 | | | test_class_members |
+| tests.cpp:372:2:372:4 | mc9 | | | test_class_members |
+| tests.cpp:372:24:372:24 | 0 | | | test_class_members |
| tests.cpp:392:5:392:29 | [summary param] 0 in madCallArg0ReturnToReturn | ParameterNode | madCallArg0ReturnToReturn | madCallArg0ReturnToReturn |
| tests.cpp:392:5:392:29 | [summary] read: Argument[0].Parameter[this pointer] in madCallArg0ReturnToReturn | PostUpdateNode | madCallArg0ReturnToReturn | madCallArg0ReturnToReturn |
| tests.cpp:392:5:392:29 | [summary] read: Argument[0].ReturnValue in madCallArg0ReturnToReturn | OutNode | madCallArg0ReturnToReturn | madCallArg0ReturnToReturn |
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/test_mad-signatures.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/test_mad-signatures.expected
index d494c09e71d5..2da7e83cca37 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/test_mad-signatures.expected
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/test_mad-signatures.expected
@@ -1962,6 +1962,15 @@ getSignatureParameterName
| (BUF_MEM *,size_t) | | BUF_MEM_grow | 1 | size_t |
| (BUF_MEM *,size_t) | | BUF_MEM_grow_clean | 0 | BUF_MEM * |
| (BUF_MEM *,size_t) | | BUF_MEM_grow_clean | 1 | size_t |
+| (Blob *,int,const Blob &,int,int) | BlobUtil | copy | 0 | Blob * |
+| (Blob *,int,const Blob &,int,int) | BlobUtil | copy | 1 | int |
+| (Blob *,int,const Blob &,int,int) | BlobUtil | copy | 2 | const Blob & |
+| (Blob *,int,const Blob &,int,int) | BlobUtil | copy | 3 | int |
+| (Blob *,int,const Blob &,int,int) | BlobUtil | copy | 4 | int |
+| (Blob *,int,const char *,int) | BlobUtil | copy | 0 | Blob * |
+| (Blob *,int,const char *,int) | BlobUtil | copy | 1 | int |
+| (Blob *,int,const char *,int) | BlobUtil | copy | 2 | const char * |
+| (Blob *,int,const char *,int) | BlobUtil | copy | 3 | int |
| (BrotliBitReader *const,uint64_t,uint64_t *) | | BrotliSafeReadBits32Slow | 0 | BrotliBitReader *const |
| (BrotliBitReader *const,uint64_t,uint64_t *) | | BrotliSafeReadBits32Slow | 1 | uint64_t |
| (BrotliBitReader *const,uint64_t,uint64_t *) | | BrotliSafeReadBits32Slow | 2 | uint64_t * |
@@ -13127,6 +13136,10 @@ getSignatureParameterName
| (char *,char,char **) | | __old_strtok_r_1c | 0 | char * |
| (char *,char,char **) | | __old_strtok_r_1c | 1 | char |
| (char *,char,char **) | | __old_strtok_r_1c | 2 | char ** |
+| (char *,const Blob &,int,int) | BlobUtil | copy | 0 | char * |
+| (char *,const Blob &,int,int) | BlobUtil | copy | 1 | const Blob & |
+| (char *,const Blob &,int,int) | BlobUtil | copy | 2 | int |
+| (char *,const Blob &,int,int) | BlobUtil | copy | 3 | int |
| (char *,const char *) | | xstrdup | 0 | char * |
| (char *,const char *) | | xstrdup | 1 | const char * |
| (char *,const char **,const char **,const char **,const char **,const char **) | | _nl_explode_name | 0 | char * |
diff --git a/cpp/ql/test/library-tests/syntax-zoo/dataflow-consistency.expected b/cpp/ql/test/library-tests/syntax-zoo/dataflow-consistency.expected
index 76adedb8f054..2b41bcfe0cb4 100644
--- a/cpp/ql/test/library-tests/syntax-zoo/dataflow-consistency.expected
+++ b/cpp/ql/test/library-tests/syntax-zoo/dataflow-consistency.expected
@@ -1,10 +1,4 @@
uniqueEnclosingCallable
-| builtin.c:14:3:14:16 | ... * ... | Node should have one enclosing callable but has 0. |
-| builtin.c:14:3:14:16 | sizeof(int) | Node should have one enclosing callable but has 0. |
-| builtin.c:14:10:14:10 | 4 | Node should have one enclosing callable but has 0. |
-| builtin.c:15:3:15:16 | ... * ... | Node should have one enclosing callable but has 0. |
-| builtin.c:15:3:15:16 | sizeof(int) | Node should have one enclosing callable but has 0. |
-| builtin.c:15:10:15:10 | 4 | Node should have one enclosing callable but has 0. |
| enum.c:2:6:2:6 | 1 | Node should have one enclosing callable but has 0. |
| enum.c:2:6:2:10 | ... + ... | Node should have one enclosing callable but has 0. |
| enum.c:2:10:2:10 | 1 | Node should have one enclosing callable but has 0. |
diff --git a/cpp/ql/test/query-tests/Likely Bugs/Format/NonConstantFormat/NonConstantFormat.expected b/cpp/ql/test/query-tests/Likely Bugs/Format/NonConstantFormat/NonConstantFormat.expected
index a4395489d4ee..d0784987f322 100644
--- a/cpp/ql/test/query-tests/Likely Bugs/Format/NonConstantFormat/NonConstantFormat.expected
+++ b/cpp/ql/test/query-tests/Likely Bugs/Format/NonConstantFormat/NonConstantFormat.expected
@@ -1,3 +1,23 @@
+#select
+| NonConstantFormat.c:30:10:30:16 | *access to array | NonConstantFormat.c:28:27:28:30 | **argv | NonConstantFormat.c:30:10:30:16 | *access to array | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | NonConstantFormat.c:30:3:30:8 | call to printf | printf |
+| NonConstantFormat.c:41:9:41:45 | *call to any_random_function | NonConstantFormat.c:41:9:41:45 | *call to any_random_function | NonConstantFormat.c:41:9:41:45 | *call to any_random_function | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | NonConstantFormat.c:41:2:41:7 | call to printf | printf |
+| NonConstantFormat.c:45:9:45:48 | *call to gettext | NonConstantFormat.c:45:11:45:47 | *call to any_random_function | NonConstantFormat.c:45:9:45:48 | *call to gettext | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | NonConstantFormat.c:45:2:45:7 | call to printf | printf |
+| nested.cpp:21:23:21:26 | *fmt0 | nested.cpp:42:24:42:34 | *call to ext_fmt_str | nested.cpp:21:23:21:26 | *fmt0 | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | nested.cpp:21:5:21:12 | call to snprintf | snprintf |
+| nested.cpp:79:32:79:38 | *call to get_fmt | nested.cpp:79:32:79:38 | *call to get_fmt | nested.cpp:79:32:79:38 | *call to get_fmt | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | nested.cpp:79:5:79:14 | call to diagnostic | diagnostic |
+| nested.cpp:87:18:87:20 | *fmt | nested.cpp:86:19:86:46 | *call to __builtin_alloca | nested.cpp:87:18:87:20 | *fmt | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | nested.cpp:87:7:87:16 | call to diagnostic | diagnostic |
+| test.cpp:130:20:130:26 | *access to array | test.cpp:46:27:46:30 | **argv | test.cpp:130:20:130:26 | *access to array | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | test.cpp:130:2:130:10 | call to sprintf | sprintf |
+| test.cpp:170:12:170:14 | *res | test.cpp:167:31:167:34 | *data | test.cpp:170:12:170:14 | *res | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | test.cpp:170:5:170:10 | call to printf | printf |
+| test.cpp:195:31:195:33 | *str | test.cpp:193:32:193:34 | *str | test.cpp:195:31:195:33 | *str | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | test.cpp:195:3:195:18 | call to StringCchPrintfW | StringCchPrintfW |
+| test.cpp:197:11:197:14 | *wstr | test.cpp:193:32:193:34 | *str | test.cpp:197:11:197:14 | *wstr | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | test.cpp:197:3:197:9 | call to wprintf | wprintf |
+| test.cpp:205:12:205:20 | *... + ... | test.cpp:204:25:204:36 | *call to get_string | test.cpp:205:12:205:20 | *... + ... | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | test.cpp:205:5:205:10 | call to printf | printf |
+| test.cpp:206:12:206:16 | *hello | test.cpp:204:25:204:36 | *call to get_string | test.cpp:206:12:206:16 | *hello | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | test.cpp:206:5:206:10 | call to printf | printf |
+| test.cpp:211:12:211:16 | *hello | test.cpp:209:25:209:36 | *call to get_string | test.cpp:211:12:211:16 | *hello | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | test.cpp:211:5:211:10 | call to printf | printf |
+| test.cpp:217:12:217:16 | *hello | test.cpp:215:25:215:36 | *call to get_string | test.cpp:217:12:217:16 | *hello | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | test.cpp:217:5:217:10 | call to printf | printf |
+| test.cpp:223:12:223:16 | *hello | test.cpp:221:25:221:36 | *call to get_string | test.cpp:223:12:223:16 | *hello | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | test.cpp:223:5:223:10 | call to printf | printf |
+| test.cpp:228:12:228:18 | *++ ... | test.cpp:227:25:227:36 | *call to get_string | test.cpp:228:12:228:18 | *++ ... | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | test.cpp:228:5:228:10 | call to printf | printf |
+| test.cpp:235:12:235:16 | *hello | test.cpp:232:25:232:36 | *call to get_string | test.cpp:235:12:235:16 | *hello | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | test.cpp:235:5:235:10 | call to printf | printf |
+| test.cpp:242:12:242:16 | *hello | test.cpp:239:25:239:36 | *call to get_string | test.cpp:242:12:242:16 | *hello | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | test.cpp:242:5:242:10 | call to printf | printf |
+| test.cpp:247:12:247:16 | *hello | test.cpp:245:25:245:36 | *call to get_string | test.cpp:247:12:247:16 | *hello | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | test.cpp:247:5:247:10 | call to printf | printf |
edges
| NonConstantFormat.c:28:27:28:30 | **argv | NonConstantFormat.c:30:10:30:16 | *access to array | provenance | |
| NonConstantFormat.c:45:11:45:47 | *call to any_random_function | NonConstantFormat.c:45:9:45:48 | *call to gettext | provenance | DataFlowFunction |
@@ -15,7 +35,7 @@ edges
| test.cpp:193:32:193:34 | *str | test.cpp:195:31:195:33 | *str | provenance | |
| test.cpp:193:32:193:34 | *str | test.cpp:197:11:197:14 | *wstr | provenance | TaintFunction |
| test.cpp:195:20:195:23 | StringCchPrintfW output argument | test.cpp:197:11:197:14 | *wstr | provenance | |
-| test.cpp:195:31:195:33 | *str | test.cpp:195:20:195:23 | StringCchPrintfW output argument | provenance | MaD:403 |
+| test.cpp:195:31:195:33 | *str | test.cpp:195:20:195:23 | StringCchPrintfW output argument | provenance | MaD:1 |
| test.cpp:204:25:204:36 | *call to get_string | test.cpp:204:25:204:36 | *call to get_string | provenance | |
| test.cpp:204:25:204:36 | *call to get_string | test.cpp:205:12:205:20 | *... + ... | provenance | |
| test.cpp:204:25:204:36 | *call to get_string | test.cpp:206:12:206:16 | *hello | provenance | |
@@ -37,6 +57,8 @@ edges
| test.cpp:239:25:239:36 | *call to get_string | test.cpp:242:12:242:16 | *hello | provenance | |
| test.cpp:245:25:245:36 | *call to get_string | test.cpp:245:25:245:36 | *call to get_string | provenance | |
| test.cpp:245:25:245:36 | *call to get_string | test.cpp:247:12:247:16 | *hello | provenance | |
+models
+| 1 | Summary: ; ; false; StringCchPrintfW; ; ; Argument[*2..8]; Argument[*0]; taint; manual |
nodes
| NonConstantFormat.c:28:27:28:30 | **argv | semmle.label | **argv |
| NonConstantFormat.c:30:10:30:16 | *access to array | semmle.label | *access to array |
@@ -93,23 +115,3 @@ nodes
| test.cpp:245:25:245:36 | *call to get_string | semmle.label | *call to get_string |
| test.cpp:247:12:247:16 | *hello | semmle.label | *hello |
subpaths
-#select
-| NonConstantFormat.c:30:10:30:16 | *access to array | NonConstantFormat.c:28:27:28:30 | **argv | NonConstantFormat.c:30:10:30:16 | *access to array | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | NonConstantFormat.c:30:3:30:8 | call to printf | printf |
-| NonConstantFormat.c:41:9:41:45 | *call to any_random_function | NonConstantFormat.c:41:9:41:45 | *call to any_random_function | NonConstantFormat.c:41:9:41:45 | *call to any_random_function | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | NonConstantFormat.c:41:2:41:7 | call to printf | printf |
-| NonConstantFormat.c:45:9:45:48 | *call to gettext | NonConstantFormat.c:45:11:45:47 | *call to any_random_function | NonConstantFormat.c:45:9:45:48 | *call to gettext | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | NonConstantFormat.c:45:2:45:7 | call to printf | printf |
-| nested.cpp:21:23:21:26 | *fmt0 | nested.cpp:42:24:42:34 | *call to ext_fmt_str | nested.cpp:21:23:21:26 | *fmt0 | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | nested.cpp:21:5:21:12 | call to snprintf | snprintf |
-| nested.cpp:79:32:79:38 | *call to get_fmt | nested.cpp:79:32:79:38 | *call to get_fmt | nested.cpp:79:32:79:38 | *call to get_fmt | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | nested.cpp:79:5:79:14 | call to diagnostic | diagnostic |
-| nested.cpp:87:18:87:20 | *fmt | nested.cpp:86:19:86:46 | *call to __builtin_alloca | nested.cpp:87:18:87:20 | *fmt | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | nested.cpp:87:7:87:16 | call to diagnostic | diagnostic |
-| test.cpp:130:20:130:26 | *access to array | test.cpp:46:27:46:30 | **argv | test.cpp:130:20:130:26 | *access to array | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | test.cpp:130:2:130:10 | call to sprintf | sprintf |
-| test.cpp:170:12:170:14 | *res | test.cpp:167:31:167:34 | *data | test.cpp:170:12:170:14 | *res | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | test.cpp:170:5:170:10 | call to printf | printf |
-| test.cpp:195:31:195:33 | *str | test.cpp:193:32:193:34 | *str | test.cpp:195:31:195:33 | *str | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | test.cpp:195:3:195:18 | call to StringCchPrintfW | StringCchPrintfW |
-| test.cpp:197:11:197:14 | *wstr | test.cpp:193:32:193:34 | *str | test.cpp:197:11:197:14 | *wstr | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | test.cpp:197:3:197:9 | call to wprintf | wprintf |
-| test.cpp:205:12:205:20 | *... + ... | test.cpp:204:25:204:36 | *call to get_string | test.cpp:205:12:205:20 | *... + ... | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | test.cpp:205:5:205:10 | call to printf | printf |
-| test.cpp:206:12:206:16 | *hello | test.cpp:204:25:204:36 | *call to get_string | test.cpp:206:12:206:16 | *hello | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | test.cpp:206:5:206:10 | call to printf | printf |
-| test.cpp:211:12:211:16 | *hello | test.cpp:209:25:209:36 | *call to get_string | test.cpp:211:12:211:16 | *hello | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | test.cpp:211:5:211:10 | call to printf | printf |
-| test.cpp:217:12:217:16 | *hello | test.cpp:215:25:215:36 | *call to get_string | test.cpp:217:12:217:16 | *hello | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | test.cpp:217:5:217:10 | call to printf | printf |
-| test.cpp:223:12:223:16 | *hello | test.cpp:221:25:221:36 | *call to get_string | test.cpp:223:12:223:16 | *hello | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | test.cpp:223:5:223:10 | call to printf | printf |
-| test.cpp:228:12:228:18 | *++ ... | test.cpp:227:25:227:36 | *call to get_string | test.cpp:228:12:228:18 | *++ ... | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | test.cpp:228:5:228:10 | call to printf | printf |
-| test.cpp:235:12:235:16 | *hello | test.cpp:232:25:232:36 | *call to get_string | test.cpp:235:12:235:16 | *hello | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | test.cpp:235:5:235:10 | call to printf | printf |
-| test.cpp:242:12:242:16 | *hello | test.cpp:239:25:239:36 | *call to get_string | test.cpp:242:12:242:16 | *hello | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | test.cpp:242:5:242:10 | call to printf | printf |
-| test.cpp:247:12:247:16 | *hello | test.cpp:245:25:245:36 | *call to get_string | test.cpp:247:12:247:16 | *hello | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | test.cpp:247:5:247:10 | call to printf | printf |
diff --git a/cpp/ql/test/query-tests/Likely Bugs/Format/NonConstantFormat/NonConstantFormat.qlref b/cpp/ql/test/query-tests/Likely Bugs/Format/NonConstantFormat/NonConstantFormat.qlref
index cb71273232ca..e33cbe4e51e1 100644
--- a/cpp/ql/test/query-tests/Likely Bugs/Format/NonConstantFormat/NonConstantFormat.qlref
+++ b/cpp/ql/test/query-tests/Likely Bugs/Format/NonConstantFormat/NonConstantFormat.qlref
@@ -1,2 +1,4 @@
query: Likely Bugs/Format/NonConstantFormat.ql
-postprocess: utils/test/InlineExpectationsTestQuery.ql
+postprocess:
+ - utils/test/PrettyPrintModels.ql
+ - utils/test/InlineExpectationsTestQuery.ql
diff --git a/cpp/ql/test/query-tests/Likely Bugs/Leap Year/UnsafeArrayForDaysOfYear/test.cpp b/cpp/ql/test/query-tests/Likely Bugs/Leap Year/UnsafeArrayForDaysOfYear/test.cpp
index f76167c1893b..3994fce4ee88 100644
--- a/cpp/ql/test/query-tests/Likely Bugs/Leap Year/UnsafeArrayForDaysOfYear/test.cpp
+++ b/cpp/ql/test/query-tests/Likely Bugs/Leap Year/UnsafeArrayForDaysOfYear/test.cpp
@@ -68,3 +68,12 @@ void VectorOfDays_FalsePositive(int dayOfYear, int x)
items[dayOfYear - 1] = x;
}
+
+void f_______________________________________________________this_name_must_be_exactly_357_chars__________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________() {
+ // Using this magic compiler variable results in a `const char` array being
+ // initialized with the function signature. Including `void`, a space, and
+ // `()`, the signature adds up to exactly 364 characters in this case.
+ // The initializer for `__PRETTY_FUNCTION__` thus initializes an array of
+ // length 365 (because the null-terminator adds another character).
+ auto x = __PRETTY_FUNCTION__; // clean
+}
\ No newline at end of file
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-089/SqlTainted/SqlTainted.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-089/SqlTainted/SqlTainted.expected
index df780acdd8d0..1f8441edcd61 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-089/SqlTainted/SqlTainted.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-089/SqlTainted/SqlTainted.expected
@@ -1,11 +1,17 @@
#select
-| test.c:21:18:21:23 | query1 | test.c:14:27:14:30 | **argv | test.c:21:18:21:23 | *query1 | This argument to a SQL query function is derived from $@ and then passed to mysql_query(sqlArg). | test.c:14:27:14:30 | **argv | user input (a command-line argument) |
-| test.c:51:18:51:23 | query1 | test.c:14:27:14:30 | **argv | test.c:51:18:51:23 | *query1 | This argument to a SQL query function is derived from $@ and then passed to mysql_query(sqlArg). | test.c:14:27:14:30 | **argv | user input (a command-line argument) |
-| test.c:76:17:76:25 | userInput | test.c:75:8:75:16 | gets output argument | test.c:76:17:76:25 | *userInput | This argument to a SQL query function is derived from $@ and then passed to SQLPrepare(StatementText). | test.c:75:8:75:16 | gets output argument | user input (string read by gets) |
-| test.c:77:20:77:28 | userInput | test.c:75:8:75:16 | gets output argument | test.c:77:20:77:28 | *userInput | This argument to a SQL query function is derived from $@ and then passed to SQLExecDirect(StatementText). | test.c:75:8:75:16 | gets output argument | user input (string read by gets) |
-| test.c:106:24:106:29 | query1 | test.c:101:8:101:16 | gets output argument | test.c:106:24:106:29 | *query1 | This argument to a SQL query function is derived from $@. | test.c:101:8:101:16 | gets output argument | user input (string read by gets) |
-| test.c:107:28:107:33 | query1 | test.c:101:8:101:16 | gets output argument | test.c:107:28:107:33 | *query1 | This argument to a SQL query function is derived from $@. | test.c:101:8:101:16 | gets output argument | user input (string read by gets) |
-| test.cpp:43:27:43:33 | access to array | test.cpp:39:27:39:30 | **argv | test.cpp:43:27:43:33 | *access to array | This argument to a SQL query function is derived from $@ and then passed to pqxx::work::exec1((unnamed parameter 0)). | test.cpp:39:27:39:30 | **argv | user input (a command-line argument) |
+| test.c:21:18:21:23 | *query1 | test.c:14:27:14:30 | **argv | test.c:21:18:21:23 | *query1 | This argument to a SQL query function is derived from $@ and then passed to mysql_query(sqlArg). | test.c:14:27:14:30 | **argv | user input (a command-line argument) |
+| test.c:51:18:51:23 | *query1 | test.c:14:27:14:30 | **argv | test.c:51:18:51:23 | *query1 | This argument to a SQL query function is derived from $@ and then passed to mysql_query(sqlArg). | test.c:14:27:14:30 | **argv | user input (a command-line argument) |
+| test.c:76:17:76:25 | *userInput | test.c:75:8:75:16 | gets output argument | test.c:76:17:76:25 | *userInput | This argument to a SQL query function is derived from $@ and then passed to SQLPrepare(StatementText). | test.c:75:8:75:16 | gets output argument | user input (string read by gets) |
+| test.c:77:20:77:28 | *userInput | test.c:75:8:75:16 | gets output argument | test.c:77:20:77:28 | *userInput | This argument to a SQL query function is derived from $@ and then passed to SQLExecDirect(StatementText). | test.c:75:8:75:16 | gets output argument | user input (string read by gets) |
+| test.c:106:24:106:29 | query1 | test.c:101:8:101:16 | gets output argument | test.c:106:24:106:29 | query1 | This argument to a SQL query function is derived from $@. | test.c:101:8:101:16 | gets output argument | user input (string read by gets) |
+| test.c:107:28:107:33 | query1 | test.c:101:8:101:16 | gets output argument | test.c:107:28:107:33 | query1 | This argument to a SQL query function is derived from $@. | test.c:101:8:101:16 | gets output argument | user input (string read by gets) |
+| test.cpp:43:27:43:33 | *access to array | test.cpp:39:27:39:30 | **argv | test.cpp:43:27:43:33 | *access to array | This argument to a SQL query function is derived from $@ and then passed to pqxx::work::exec1((unnamed parameter 0)). | test.cpp:39:27:39:30 | **argv | user input (a command-line argument) |
+| test_libpq.c:26:16:26:24 | userInput | test_libpq.c:23:8:23:16 | gets output argument | test_libpq.c:26:16:26:24 | userInput | This argument to a SQL query function is derived from $@. | test_libpq.c:23:8:23:16 | gets output argument | user input (string read by gets) |
+| test_libpq.c:27:22:27:30 | userInput | test_libpq.c:23:8:23:16 | gets output argument | test_libpq.c:27:22:27:30 | userInput | This argument to a SQL query function is derived from $@. | test_libpq.c:23:8:23:16 | gets output argument | user input (string read by gets) |
+| test_libpq.c:28:27:28:35 | userInput | test_libpq.c:23:8:23:16 | gets output argument | test_libpq.c:28:27:28:35 | userInput | This argument to a SQL query function is derived from $@. | test_libpq.c:23:8:23:16 | gets output argument | user input (string read by gets) |
+| test_libpq.c:29:21:29:29 | userInput | test_libpq.c:23:8:23:16 | gets output argument | test_libpq.c:29:21:29:29 | userInput | This argument to a SQL query function is derived from $@. | test_libpq.c:23:8:23:16 | gets output argument | user input (string read by gets) |
+| test_libpq.c:30:27:30:35 | userInput | test_libpq.c:23:8:23:16 | gets output argument | test_libpq.c:30:27:30:35 | userInput | This argument to a SQL query function is derived from $@. | test_libpq.c:23:8:23:16 | gets output argument | user input (string read by gets) |
+| test_libpq.c:31:31:31:39 | userInput | test_libpq.c:23:8:23:16 | gets output argument | test_libpq.c:31:31:31:39 | userInput | This argument to a SQL query function is derived from $@. | test_libpq.c:23:8:23:16 | gets output argument | user input (string read by gets) |
edges
| test.c:14:27:14:30 | **argv | test.c:15:20:15:26 | *access to array | provenance | |
| test.c:15:20:15:26 | *access to array | test.c:21:18:21:23 | *query1 | provenance | TaintFunction |
@@ -17,12 +23,24 @@ edges
| test.c:48:20:48:33 | *globalUsername | test.c:51:18:51:23 | *query1 | provenance | TaintFunction |
| test.c:75:8:75:16 | gets output argument | test.c:76:17:76:25 | *userInput | provenance | |
| test.c:75:8:75:16 | gets output argument | test.c:77:20:77:28 | *userInput | provenance | |
-| test.c:101:8:101:16 | gets output argument | test.c:106:24:106:29 | *query1 | provenance | TaintFunction Sink:MaD:2 |
-| test.c:101:8:101:16 | gets output argument | test.c:107:28:107:33 | *query1 | provenance | TaintFunction Sink:MaD:1 |
+| test.c:101:8:101:16 | gets output argument | test.c:106:24:106:29 | query1 | provenance | TaintFunction Sink:MaD:2 |
+| test.c:101:8:101:16 | gets output argument | test.c:107:28:107:33 | query1 | provenance | TaintFunction Sink:MaD:1 |
| test.cpp:39:27:39:30 | **argv | test.cpp:43:27:43:33 | *access to array | provenance | |
+| test_libpq.c:23:8:23:16 | gets output argument | test_libpq.c:26:16:26:24 | userInput | provenance | Sink:MaD:3 |
+| test_libpq.c:23:8:23:16 | gets output argument | test_libpq.c:27:22:27:30 | userInput | provenance | Sink:MaD:4 |
+| test_libpq.c:23:8:23:16 | gets output argument | test_libpq.c:28:27:28:35 | userInput | provenance | Sink:MaD:5 |
+| test_libpq.c:23:8:23:16 | gets output argument | test_libpq.c:29:21:29:29 | userInput | provenance | Sink:MaD:7 |
+| test_libpq.c:23:8:23:16 | gets output argument | test_libpq.c:30:27:30:35 | userInput | provenance | Sink:MaD:8 |
+| test_libpq.c:23:8:23:16 | gets output argument | test_libpq.c:31:31:31:39 | userInput | provenance | Sink:MaD:6 |
models
| 1 | Sink: ; ; false; OCIStmtPrepare2; ; ; Argument[*3]; sql-injection; manual |
| 2 | Sink: ; ; false; OCIStmtPrepare; ; ; Argument[*2]; sql-injection; manual |
+| 3 | Sink: ; ; false; PQexec; ; ; Argument[*1]; sql-injection; manual |
+| 4 | Sink: ; ; false; PQexecParams; ; ; Argument[*1]; sql-injection; manual |
+| 5 | Sink: ; ; false; PQprepare; ; ; Argument[*2]; sql-injection; manual |
+| 6 | Sink: ; ; false; PQsendPrepare; ; ; Argument[*2]; sql-injection; manual |
+| 7 | Sink: ; ; false; PQsendQuery; ; ; Argument[*1]; sql-injection; manual |
+| 8 | Sink: ; ; false; PQsendQueryParams; ; ; Argument[*1]; sql-injection; manual |
nodes
| test.c:14:27:14:30 | **argv | semmle.label | **argv |
| test.c:15:20:15:26 | *access to array | semmle.label | *access to array |
@@ -37,8 +55,15 @@ nodes
| test.c:76:17:76:25 | *userInput | semmle.label | *userInput |
| test.c:77:20:77:28 | *userInput | semmle.label | *userInput |
| test.c:101:8:101:16 | gets output argument | semmle.label | gets output argument |
-| test.c:106:24:106:29 | *query1 | semmle.label | *query1 |
-| test.c:107:28:107:33 | *query1 | semmle.label | *query1 |
+| test.c:106:24:106:29 | query1 | semmle.label | query1 |
+| test.c:107:28:107:33 | query1 | semmle.label | query1 |
| test.cpp:39:27:39:30 | **argv | semmle.label | **argv |
| test.cpp:43:27:43:33 | *access to array | semmle.label | *access to array |
+| test_libpq.c:23:8:23:16 | gets output argument | semmle.label | gets output argument |
+| test_libpq.c:26:16:26:24 | userInput | semmle.label | userInput |
+| test_libpq.c:27:22:27:30 | userInput | semmle.label | userInput |
+| test_libpq.c:28:27:28:35 | userInput | semmle.label | userInput |
+| test_libpq.c:29:21:29:29 | userInput | semmle.label | userInput |
+| test_libpq.c:30:27:30:35 | userInput | semmle.label | userInput |
+| test_libpq.c:31:31:31:39 | userInput | semmle.label | userInput |
subpaths
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-089/SqlTainted/test_libpq.c b/cpp/ql/test/query-tests/Security/CWE/CWE-089/SqlTainted/test_libpq.c
new file mode 100644
index 000000000000..85ebe943ce62
--- /dev/null
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-089/SqlTainted/test_libpq.c
@@ -0,0 +1,35 @@
+typedef unsigned long size_t;
+typedef unsigned int Oid;
+typedef struct pg_conn PGconn;
+typedef struct pg_result PGresult;
+
+PGresult *PQexec(PGconn *conn, const char *query);
+PGresult *PQexecParams(PGconn *conn, const char *command, int nParams,
+ const Oid *paramTypes, const char *const *paramValues,
+ const int *paramLengths, const int *paramFormats, int resultFormat);
+PGresult *PQprepare(PGconn *conn, const char *stmtName, const char *query, int nParams,
+ const Oid *paramTypes);
+int PQsendQuery(PGconn *conn, const char *query);
+int PQsendQueryParams(PGconn *conn, const char *command, int nParams, const Oid *paramTypes,
+ const char *const *paramValues, const int *paramLengths,
+ const int *paramFormats, int resultFormat);
+int PQsendPrepare(PGconn *conn, const char *stmtName, const char *query, int nParams,
+ const Oid *paramTypes);
+
+char *gets(char *s);
+
+void libpqTests(PGconn *conn) {
+ char userInput[1000];
+ gets(userInput); // $ Source
+
+ // A user-controlled string is interpreted as SQL.
+ PQexec(conn, userInput); // $ Alert
+ PQexecParams(conn, userInput, 0, 0, 0, 0, 0, 0); // $ Alert
+ PQprepare(conn, "stmt", userInput, 0, 0); // $ Alert
+ PQsendQuery(conn, userInput); // $ Alert
+ PQsendQueryParams(conn, userInput, 0, 0, 0, 0, 0, 0); // $ Alert
+ PQsendPrepare(conn, "stmt", userInput, 0, 0); // $ Alert
+
+ // A constant query is safe.
+ PQexec(conn, "SELECT 1"); // GOOD
+}
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/ExposedSystemData.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/ExposedSystemData.expected
index 6b4be51fd33e..a174000efec8 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/ExposedSystemData.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/ExposedSystemData.expected
@@ -9,11 +9,11 @@
| tests2.cpp:93:14:93:17 | *str1 | tests2.cpp:91:42:91:45 | *str1 | tests2.cpp:93:14:93:17 | *str1 | This operation exposes system data from $@. | tests2.cpp:91:42:91:45 | *str1 | *str1 |
| tests2.cpp:102:14:102:15 | *pw | tests2.cpp:101:8:101:15 | *call to getpwuid | tests2.cpp:102:14:102:15 | *pw | This operation exposes system data from $@. | tests2.cpp:101:8:101:15 | *call to getpwuid | *call to getpwuid |
| tests2.cpp:111:14:111:19 | *ptr | tests2.cpp:109:12:109:17 | *call to getenv | tests2.cpp:111:14:111:19 | *ptr | This operation exposes system data from $@. | tests2.cpp:109:12:109:17 | *call to getenv | *call to getenv |
-| tests2.cpp:138:23:138:34 | *message_data | tests2.cpp:134:17:134:22 | *call to getenv | tests2.cpp:138:23:138:34 | *message_data | This operation exposes system data from $@. | tests2.cpp:134:17:134:22 | *call to getenv | *call to getenv |
-| tests2.cpp:144:33:144:40 | *& ... | tests2.cpp:134:17:134:22 | *call to getenv | tests2.cpp:144:33:144:40 | *& ... | This operation exposes system data from $@. | tests2.cpp:134:17:134:22 | *call to getenv | *call to getenv |
-| tests2.cpp:147:20:147:27 | *& ... | tests2.cpp:134:17:134:22 | *call to getenv | tests2.cpp:147:20:147:27 | *& ... | This operation exposes system data from $@. | tests2.cpp:134:17:134:22 | *call to getenv | *call to getenv |
-| tests2.cpp:155:32:155:39 | *& ... | tests2.cpp:134:17:134:22 | *call to getenv | tests2.cpp:155:32:155:39 | *& ... | This operation exposes system data from $@. | tests2.cpp:134:17:134:22 | *call to getenv | *call to getenv |
-| tests2.cpp:158:20:158:27 | *& ... | tests2.cpp:134:17:134:22 | *call to getenv | tests2.cpp:158:20:158:27 | *& ... | This operation exposes system data from $@. | tests2.cpp:134:17:134:22 | *call to getenv | *call to getenv |
+| tests2.cpp:138:23:138:34 | message_data | tests2.cpp:134:17:134:22 | *call to getenv | tests2.cpp:138:23:138:34 | message_data | This operation exposes system data from $@. | tests2.cpp:134:17:134:22 | *call to getenv | *call to getenv |
+| tests2.cpp:144:33:144:40 | & ... | tests2.cpp:134:17:134:22 | *call to getenv | tests2.cpp:144:33:144:40 | & ... | This operation exposes system data from $@. | tests2.cpp:134:17:134:22 | *call to getenv | *call to getenv |
+| tests2.cpp:147:20:147:27 | & ... | tests2.cpp:134:17:134:22 | *call to getenv | tests2.cpp:147:20:147:27 | & ... | This operation exposes system data from $@. | tests2.cpp:134:17:134:22 | *call to getenv | *call to getenv |
+| tests2.cpp:155:32:155:39 | & ... | tests2.cpp:134:17:134:22 | *call to getenv | tests2.cpp:155:32:155:39 | & ... | This operation exposes system data from $@. | tests2.cpp:134:17:134:22 | *call to getenv | *call to getenv |
+| tests2.cpp:158:20:158:27 | & ... | tests2.cpp:134:17:134:22 | *call to getenv | tests2.cpp:158:20:158:27 | & ... | This operation exposes system data from $@. | tests2.cpp:134:17:134:22 | *call to getenv | *call to getenv |
| tests_sockets.cpp:39:19:39:22 | *path | tests_sockets.cpp:26:15:26:20 | *call to getenv | tests_sockets.cpp:39:19:39:22 | *path | This operation exposes system data from $@. | tests_sockets.cpp:26:15:26:20 | *call to getenv | *call to getenv |
| tests_sockets.cpp:43:20:43:23 | *path | tests_sockets.cpp:26:15:26:20 | *call to getenv | tests_sockets.cpp:43:20:43:23 | *path | This operation exposes system data from $@. | tests_sockets.cpp:26:15:26:20 | *call to getenv | *call to getenv |
| tests_sockets.cpp:76:19:76:22 | *path | tests_sockets.cpp:63:15:63:20 | *call to getenv | tests_sockets.cpp:76:19:76:22 | *path | This operation exposes system data from $@. | tests_sockets.cpp:63:15:63:20 | *call to getenv | *call to getenv |
@@ -33,13 +33,13 @@ edges
| tests2.cpp:111:14:111:15 | *c1 [*ptr] | tests2.cpp:111:14:111:19 | *ptr | provenance | |
| tests2.cpp:111:14:111:15 | *c1 [*ptr] | tests2.cpp:111:17:111:19 | *ptr | provenance | |
| tests2.cpp:111:17:111:19 | *ptr | tests2.cpp:111:14:111:19 | *ptr | provenance | |
-| tests2.cpp:134:2:134:30 | *... = ... | tests2.cpp:138:23:138:34 | *message_data | provenance | Sink:MaD:2 |
+| tests2.cpp:134:2:134:30 | *... = ... | tests2.cpp:138:23:138:34 | message_data | provenance | Sink:MaD:2 |
| tests2.cpp:134:2:134:30 | *... = ... | tests2.cpp:143:34:143:45 | *message_data | provenance | |
| tests2.cpp:134:17:134:22 | *call to getenv | tests2.cpp:134:2:134:30 | *... = ... | provenance | |
-| tests2.cpp:143:24:143:31 | zmq_msg_init_data output argument | tests2.cpp:144:33:144:40 | *& ... | provenance | Sink:MaD:3 |
-| tests2.cpp:143:24:143:31 | zmq_msg_init_data output argument | tests2.cpp:147:20:147:27 | *& ... | provenance | Sink:MaD:1 |
-| tests2.cpp:143:24:143:31 | zmq_msg_init_data output argument | tests2.cpp:155:32:155:39 | *& ... | provenance | Sink:MaD:3 |
-| tests2.cpp:143:24:143:31 | zmq_msg_init_data output argument | tests2.cpp:158:20:158:27 | *& ... | provenance | Sink:MaD:1 |
+| tests2.cpp:143:24:143:31 | zmq_msg_init_data output argument | tests2.cpp:144:33:144:40 | & ... | provenance | Sink:MaD:3 |
+| tests2.cpp:143:24:143:31 | zmq_msg_init_data output argument | tests2.cpp:147:20:147:27 | & ... | provenance | Sink:MaD:1 |
+| tests2.cpp:143:24:143:31 | zmq_msg_init_data output argument | tests2.cpp:155:32:155:39 | & ... | provenance | Sink:MaD:3 |
+| tests2.cpp:143:24:143:31 | zmq_msg_init_data output argument | tests2.cpp:158:20:158:27 | & ... | provenance | Sink:MaD:1 |
| tests2.cpp:143:34:143:45 | *message_data | tests2.cpp:143:24:143:31 | zmq_msg_init_data output argument | provenance | MaD:4 |
| tests_sockets.cpp:26:15:26:20 | *call to getenv | tests_sockets.cpp:26:15:26:20 | *call to getenv | provenance | |
| tests_sockets.cpp:26:15:26:20 | *call to getenv | tests_sockets.cpp:39:19:39:22 | *path | provenance | |
@@ -78,13 +78,13 @@ nodes
| tests2.cpp:111:17:111:19 | *ptr | semmle.label | *ptr |
| tests2.cpp:134:2:134:30 | *... = ... | semmle.label | *... = ... |
| tests2.cpp:134:17:134:22 | *call to getenv | semmle.label | *call to getenv |
-| tests2.cpp:138:23:138:34 | *message_data | semmle.label | *message_data |
+| tests2.cpp:138:23:138:34 | message_data | semmle.label | message_data |
| tests2.cpp:143:24:143:31 | zmq_msg_init_data output argument | semmle.label | zmq_msg_init_data output argument |
| tests2.cpp:143:34:143:45 | *message_data | semmle.label | *message_data |
-| tests2.cpp:144:33:144:40 | *& ... | semmle.label | *& ... |
-| tests2.cpp:147:20:147:27 | *& ... | semmle.label | *& ... |
-| tests2.cpp:155:32:155:39 | *& ... | semmle.label | *& ... |
-| tests2.cpp:158:20:158:27 | *& ... | semmle.label | *& ... |
+| tests2.cpp:144:33:144:40 | & ... | semmle.label | & ... |
+| tests2.cpp:147:20:147:27 | & ... | semmle.label | & ... |
+| tests2.cpp:155:32:155:39 | & ... | semmle.label | & ... |
+| tests2.cpp:158:20:158:27 | & ... | semmle.label | & ... |
| tests_sockets.cpp:26:15:26:20 | *call to getenv | semmle.label | *call to getenv |
| tests_sockets.cpp:26:15:26:20 | *call to getenv | semmle.label | *call to getenv |
| tests_sockets.cpp:39:19:39:22 | *path | semmle.label | *path |
diff --git a/csharp/documentation/library-coverage/coverage.csv b/csharp/documentation/library-coverage/coverage.csv
index a70a91755624..1a2aceca44eb 100644
--- a/csharp/documentation/library-coverage/coverage.csv
+++ b/csharp/documentation/library-coverage/coverage.csv
@@ -10,9 +10,11 @@ Internal.IL,,,68,,,,,,,,,,,,,,,,,,,41,27
Internal.Pgo,,,9,,,,,,,,,,,,,,,,,,,2,7
Internal.TypeSystem,,,365,,,,,,,,,,,,,,,,,,,216,149
Microsoft.ApplicationBlocks.Data,28,,,,,,,,,,,,28,,,,,,,,,,
+Microsoft.AspNet.OData,,,5,,,,,,,,,,,,,,,,,,,5,
Microsoft.AspNetCore.Components,2,4,2,,,,,,,2,,,,,,,,,4,,,1,1
Microsoft.AspNetCore.Http,,,1,,,,,,,,,,,,,,,,,,,1,
Microsoft.AspNetCore.Mvc,,,2,,,,,,,,,,,,,,,,,,,,2
+Microsoft.AspNetCore.OData.Deltas,,,5,,,,,,,,,,,,,,,,,,,5,
Microsoft.AspNetCore.WebUtilities,,,2,,,,,,,,,,,,,,,,,,,2,
Microsoft.CSharp,,,2,,,,,,,,,,,,,,,,,,,2,
Microsoft.Data.SqlClient,7,,4,,,,,,,,,,7,,,,,,,,,4,
@@ -44,5 +46,5 @@ NHibernate,3,,,,,,,,,,,,3,,,,,,,,,,
Newtonsoft.Json,,,91,,,,,,,,,,,,,,,,,,,73,18
ServiceStack,194,,7,27,,,,,75,,,,92,,,,,,,,,7,
SourceGenerators,,,5,,,,,,,,,,,,,,,,,,,,5
-System,59,48,12495,,6,5,12,,,4,1,,31,2,,6,15,17,5,3,,6382,6113
+System,59,48,12500,,6,5,12,,,4,1,,31,2,,6,15,17,5,3,,6387,6113
Windows.Security.Cryptography.Core,1,,,,,,,1,,,,,,,,,,,,,,,
diff --git a/csharp/documentation/library-coverage/coverage.rst b/csharp/documentation/library-coverage/coverage.rst
index f487850e54b9..a5adcad4314b 100644
--- a/csharp/documentation/library-coverage/coverage.rst
+++ b/csharp/documentation/library-coverage/coverage.rst
@@ -8,7 +8,7 @@ C# framework & library support
Framework / library,Package,Flow sources,Taint & value steps,Sinks (total),`CWE-079` :sub:`Cross-site scripting`
`ServiceStack `_,"``ServiceStack.*``, ``ServiceStack``",,7,194,
- System,"``System.*``, ``System``",48,12495,59,5
- Others,"``Amazon.Lambda.APIGatewayEvents``, ``Amazon.Lambda.Core``, ``Dapper``, ``ILCompiler``, ``ILLink.RoslynAnalyzer``, ``ILLink.Shared``, ``ILLink.Tasks``, ``Internal.IL``, ``Internal.Pgo``, ``Internal.TypeSystem``, ``Microsoft.ApplicationBlocks.Data``, ``Microsoft.AspNetCore.Components``, ``Microsoft.AspNetCore.Http``, ``Microsoft.AspNetCore.Mvc``, ``Microsoft.AspNetCore.WebUtilities``, ``Microsoft.CSharp``, ``Microsoft.Data.SqlClient``, ``Microsoft.Diagnostics.Tools.Pgo``, ``Microsoft.DotNet.Build.Tasks``, ``Microsoft.DotNet.PlatformAbstractions``, ``Microsoft.EntityFrameworkCore``, ``Microsoft.Extensions.Caching.Distributed``, ``Microsoft.Extensions.Caching.Memory``, ``Microsoft.Extensions.Configuration``, ``Microsoft.Extensions.DependencyInjection``, ``Microsoft.Extensions.DependencyModel``, ``Microsoft.Extensions.Diagnostics.Metrics``, ``Microsoft.Extensions.FileProviders``, ``Microsoft.Extensions.FileSystemGlobbing``, ``Microsoft.Extensions.Hosting``, ``Microsoft.Extensions.Http``, ``Microsoft.Extensions.Logging``, ``Microsoft.Extensions.Options``, ``Microsoft.Extensions.Primitives``, ``Microsoft.Interop``, ``Microsoft.JSInterop``, ``Microsoft.NET.Build.Tasks``, ``Microsoft.VisualBasic``, ``Microsoft.Win32``, ``Mono.Linker``, ``MySql.Data.MySqlClient``, ``NHibernate``, ``Newtonsoft.Json``, ``SourceGenerators``, ``Windows.Security.Cryptography.Core``",60,2406,162,4
- Totals,,108,14908,415,9
+ System,"``System.*``, ``System``",48,12500,59,5
+ Others,"``Amazon.Lambda.APIGatewayEvents``, ``Amazon.Lambda.Core``, ``Dapper``, ``ILCompiler``, ``ILLink.RoslynAnalyzer``, ``ILLink.Shared``, ``ILLink.Tasks``, ``Internal.IL``, ``Internal.Pgo``, ``Internal.TypeSystem``, ``Microsoft.ApplicationBlocks.Data``, ``Microsoft.AspNet.OData``, ``Microsoft.AspNetCore.Components``, ``Microsoft.AspNetCore.Http``, ``Microsoft.AspNetCore.Mvc``, ``Microsoft.AspNetCore.OData.Deltas``, ``Microsoft.AspNetCore.WebUtilities``, ``Microsoft.CSharp``, ``Microsoft.Data.SqlClient``, ``Microsoft.Diagnostics.Tools.Pgo``, ``Microsoft.DotNet.Build.Tasks``, ``Microsoft.DotNet.PlatformAbstractions``, ``Microsoft.EntityFrameworkCore``, ``Microsoft.Extensions.Caching.Distributed``, ``Microsoft.Extensions.Caching.Memory``, ``Microsoft.Extensions.Configuration``, ``Microsoft.Extensions.DependencyInjection``, ``Microsoft.Extensions.DependencyModel``, ``Microsoft.Extensions.Diagnostics.Metrics``, ``Microsoft.Extensions.FileProviders``, ``Microsoft.Extensions.FileSystemGlobbing``, ``Microsoft.Extensions.Hosting``, ``Microsoft.Extensions.Http``, ``Microsoft.Extensions.Logging``, ``Microsoft.Extensions.Options``, ``Microsoft.Extensions.Primitives``, ``Microsoft.Interop``, ``Microsoft.JSInterop``, ``Microsoft.NET.Build.Tasks``, ``Microsoft.VisualBasic``, ``Microsoft.Win32``, ``Mono.Linker``, ``MySql.Data.MySqlClient``, ``NHibernate``, ``Newtonsoft.Json``, ``SourceGenerators``, ``Windows.Security.Cryptography.Core``",60,2416,162,4
+ Totals,,108,14923,415,9
diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DependabotProxy.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DependabotProxy.cs
index e06970141fed..3bf843d3fa2c 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DependabotProxy.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DependabotProxy.cs
@@ -9,7 +9,7 @@
namespace Semmle.Extraction.CSharp.DependencyFetching
{
- public class DependabotProxy : IDisposable
+ public class DependabotProxy : IDependabotProxy
{
///
/// Represents configurations for package registries.
@@ -18,93 +18,54 @@ public class DependabotProxy : IDisposable
/// The URL of the package registry.
public record class RegistryConfig(string Type, string URL);
- private readonly string host;
- private readonly string port;
+ public string Address { get; }
- ///
- /// The full address of the Dependabot proxy, if available.
- ///
- internal string Address { get; }
- ///
- /// The URLs of package registries that are configured for the proxy.
- ///
- internal HashSet RegistryURLs { get; }
- ///
- /// The path to the temporary file where the certificate is stored.
- ///
- internal string? CertificatePath { get; private set; }
- ///
- /// The certificate used for the Dependabot proxy.
- ///
- internal X509Certificate2? Certificate { get; private set; }
-
- internal static DependabotProxy? GetDependabotProxy(
- ILogger logger, IDiagnosticsWriter diagnosticsWriter, TemporaryDirectory tempWorkingDirectory)
- {
- // Setting HTTP(S)_PROXY and SSL_CERT_FILE have no effect on Windows or macOS,
- // but we would still end up using the Dependabot proxy to check for feed reachability.
- // This would result in us discovering that the feeds are reachable, but `dotnet` would
- // fail to connect to them. To prevent this from happening, we do not initialise an
- // instance of `DependabotProxy` on those platforms.
- if (SystemBuildActions.Instance.IsWindows() || SystemBuildActions.Instance.IsMacOs()) return null;
+ public HashSet RegistryURLs { get; } = [];
- // Obtain and store the address of the Dependabot proxy, if available.
- var host = Environment.GetEnvironmentVariable(EnvironmentVariableNames.ProxyHost);
- var port = Environment.GetEnvironmentVariable(EnvironmentVariableNames.ProxyPort);
-
- if (string.IsNullOrWhiteSpace(host) || string.IsNullOrWhiteSpace(port))
- {
- logger.LogInfo("No Dependabot proxy credentials are configured.");
- return null;
- }
+ public string? CertificatePath { get; private set; }
- var result = new DependabotProxy(host, port);
- logger.LogInfo($"Dependabot proxy configured at {result.Address}");
+ public X509Certificate2? Certificate { get; private set; }
- // Obtain and store the proxy's certificate, if available.
- var cert = Environment.GetEnvironmentVariable(EnvironmentVariableNames.ProxyCertificate);
+ private DependabotProxy(IDependabotProxyConfiguration config, ILogger logger, TemporaryDirectory tempWorkingDirectory)
+ {
+ Address = $"http://{config.Host}:{config.Port}";
- if (!string.IsNullOrWhiteSpace(cert))
+ if (!string.IsNullOrWhiteSpace(config.Certificate))
{
var certDirPath = new DirectoryInfo(Path.Join(tempWorkingDirectory.DirInfo.FullName, ".dependabot-proxy"));
Directory.CreateDirectory(certDirPath.FullName);
- result.CertificatePath = Path.Join(certDirPath.FullName, "proxy.crt");
- var certFile = new FileInfo(result.CertificatePath);
+ CertificatePath = Path.Join(certDirPath.FullName, "proxy.crt");
+ var certFile = new FileInfo(CertificatePath);
using var writer = certFile.CreateText();
- writer.Write(cert);
+ writer.Write(config.Certificate);
writer.Close();
- logger.LogInfo($"Stored Dependabot proxy certificate at {result.CertificatePath}");
+ logger.LogInfo($"Stored Dependabot proxy certificate at {CertificatePath}");
- result.Certificate = X509Certificate2.CreateFromPem(cert);
+ Certificate = X509Certificate2.CreateFromPem(config.Certificate);
}
- // Try to obtain the list of private registry URLs.
- var registryURLs = Environment.GetEnvironmentVariable(EnvironmentVariableNames.ProxyURLs);
-
- if (!string.IsNullOrWhiteSpace(registryURLs))
+ if (!string.IsNullOrWhiteSpace(config.RegistryURLs))
{
try
{
- // The value of the environment variable should be a JSON array of objects, such as:
- // [ { "type": "nuget_feed", "url": "https://nuget.pkg.github.com/org/index.json" } ]
- var array = JsonConvert.DeserializeObject>(registryURLs);
+ var array = JsonConvert.DeserializeObject>(config.RegistryURLs);
if (array is not null)
{
- foreach (RegistryConfig config in array)
+ foreach (RegistryConfig registry in array)
{
// The array contains all configured private registries, not just ones for C#.
// We ignore the non-C# ones here.
- if (!config.Type.Equals("nuget_feed"))
+ if (!registry.Type.Equals("nuget_feed"))
{
- logger.LogDebug($"Ignoring registry at '{config.URL}' since it is not of type 'nuget_feed'.");
+ logger.LogDebug($"Ignoring registry at '{registry.URL}' since it is not of type 'nuget_feed'.");
continue;
}
- logger.LogInfo($"Found private registry at '{config.URL}'");
- result.RegistryURLs.Add(config.URL);
+ logger.LogInfo($"Found private registry at '{registry.URL}'");
+ RegistryURLs.Add(registry.URL);
}
}
}
@@ -113,6 +74,39 @@ public record class RegistryConfig(string Type, string URL);
logger.LogError($"Unable to parse '{EnvironmentVariableNames.ProxyURLs}': {ex.Message}");
}
}
+ }
+
+ internal static IDependabotProxy? Make(ILogger logger, IDiagnosticsWriter diagnosticsWriter, TemporaryDirectory tempWorkingDirectory)
+ {
+ // Setting HTTP(S)_PROXY and SSL_CERT_FILE have no effect on Windows or macOS,
+ // but we would still end up using the Dependabot proxy to check for feed reachability.
+ // This would result in us discovering that the feeds are reachable, but `dotnet` would
+ // fail to connect to them. To prevent this from happening, we do not initialise an
+ // instance of `DependabotProxy` on those platforms.
+ if (SystemBuildActions.Instance.IsWindows() || SystemBuildActions.Instance.IsMacOs())
+ {
+ return null;
+ }
+
+ return Make(new DependabotProxyConfiguration(), logger, diagnosticsWriter, tempWorkingDirectory);
+ }
+
+ ///
+ /// Creates an instance of the Dependabot proxy using the specified configuration.
+ /// Returns null if the proxy cannot be created.
+ /// This overload is exposed primarily to enable platform-independent unit testing.
+ ///
+ internal static IDependabotProxy? Make(
+ IDependabotProxyConfiguration proxyConfig, ILogger logger, IDiagnosticsWriter diagnosticsWriter, TemporaryDirectory tempWorkingDirectory)
+ {
+ if (string.IsNullOrWhiteSpace(proxyConfig.Host) || string.IsNullOrWhiteSpace(proxyConfig.Port))
+ {
+ logger.LogDebug("No Dependabot proxy credentials are configured.");
+ return null;
+ }
+
+ var result = new DependabotProxy(proxyConfig, logger, tempWorkingDirectory);
+ logger.LogInfo($"Dependabot proxy configured at {result.Address}");
// Emit a diagnostic for the discovered private registries, so that it is easy
// for users to see that they were picked up.
@@ -134,17 +128,9 @@ public record class RegistryConfig(string Type, string URL);
return result;
}
- private DependabotProxy(string host, string port)
- {
- this.host = host;
- this.port = port;
- this.Address = $"http://{this.host}:{this.port}";
- this.RegistryURLs = new HashSet();
- }
-
public void Dispose()
{
- this.Certificate?.Dispose();
+ Certificate?.Dispose();
}
}
}
diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DependabotProxyConfiguration.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DependabotProxyConfiguration.cs
new file mode 100644
index 000000000000..2d81f94aea2c
--- /dev/null
+++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DependabotProxyConfiguration.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace Semmle.Extraction.CSharp.DependencyFetching
+{
+ public class DependabotProxyConfiguration : IDependabotProxyConfiguration
+ {
+ public string? Host { get; } = Environment.GetEnvironmentVariable(EnvironmentVariableNames.ProxyHost);
+
+ public string? Port { get; } = Environment.GetEnvironmentVariable(EnvironmentVariableNames.ProxyPort);
+
+ public string? Certificate { get; } = Environment.GetEnvironmentVariable(EnvironmentVariableNames.ProxyCertificate);
+
+ public string? RegistryURLs { get; } = Environment.GetEnvironmentVariable(EnvironmentVariableNames.ProxyURLs);
+ }
+}
diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DependencyManager.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DependencyManager.cs
index 2706d5262931..a985947c0c12 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DependencyManager.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DependencyManager.cs
@@ -27,10 +27,10 @@ public sealed partial class DependencyManager : IDisposable, ICompilationInfoCon
private readonly ILogger logger;
private readonly IDiagnosticsWriter diagnosticsWriter;
private readonly NugetPackageRestorer nugetPackageRestorer;
- private readonly DependabotProxy? dependabotProxy;
+ private readonly IDependabotProxy? dependabotProxy;
private readonly IDotNet dotnet;
private readonly FileContent fileContent;
- private readonly FileProvider fileProvider;
+ private readonly IFileProvider fileProvider;
// Only used as a set, but ConcurrentDictionary is the only concurrent set in .NET.
private readonly IDictionary usedReferences = new ConcurrentDictionary();
@@ -106,7 +106,7 @@ void exitCallback(int ret, string msg, bool silent)
return BuildScript.Success;
}).Run(SystemBuildActions.Instance, startCallback, exitCallback);
- dependabotProxy = DependabotProxy.GetDependabotProxy(logger, diagnosticsWriter, tempWorkingDirectory);
+ dependabotProxy = DependabotProxy.Make(logger, diagnosticsWriter, tempWorkingDirectory);
try
{
diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DotNet.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DotNet.cs
index e02d157dc620..9958fbce4e71 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DotNet.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DotNet.cs
@@ -31,11 +31,11 @@ private DotNet(IDotNetCliInvoker dotnetCliInvoker, ILogger logger, bool runDotne
}
}
- private DotNet(ILogger logger, string? dotNetPath, TemporaryDirectory tempWorkingDirectory, DependabotProxy? dependabotProxy) : this(new DotNetCliInvoker(logger, Path.Join(dotNetPath ?? string.Empty, "dotnet"), dependabotProxy), logger, dotNetPath is null, tempWorkingDirectory) { }
+ private DotNet(ILogger logger, string? dotNetPath, TemporaryDirectory tempWorkingDirectory, IDependabotProxy? dependabotProxy) : this(new DotNetCliInvoker(logger, Path.Join(dotNetPath ?? string.Empty, "dotnet"), dependabotProxy), logger, dotNetPath is null, tempWorkingDirectory) { }
internal static IDotNet Make(IDotNetCliInvoker dotnetCliInvoker, ILogger logger, bool runDotnetInfo) => new DotNet(dotnetCliInvoker, logger, runDotnetInfo);
- public static IDotNet Make(ILogger logger, string? dotNetPath, TemporaryDirectory tempWorkingDirectory, DependabotProxy? dependabotProxy) => new DotNet(logger, dotNetPath, tempWorkingDirectory, dependabotProxy);
+ public static IDotNet Make(ILogger logger, string? dotNetPath, TemporaryDirectory tempWorkingDirectory, IDependabotProxy? dependabotProxy) => new DotNet(logger, dotNetPath, tempWorkingDirectory, dependabotProxy);
private static void HandleRetryExitCode143(string dotnet, int attempt, ILogger logger)
{
@@ -49,7 +49,7 @@ private void Info()
// Allow up to four attempts (with up to three retries) to run `dotnet --info`, to mitigate transient issues
for (int attempt = 0; attempt < 4; attempt++)
{
- var exitCode = dotnetCliInvoker.RunCommandExitCode("--info", silent: false);
+ var exitCode = dotnetCliInvoker.RunCommandExitCode(["--info"], silent: false);
switch (exitCode)
{
case 0:
@@ -63,9 +63,9 @@ private void Info()
}
}
- private string GetRestoreArgs(RestoreSettings restoreSettings)
+ private List GetRestoreArgs(RestoreSettings restoreSettings)
{
- var args = $"restore --no-dependencies \"{restoreSettings.File}\" --packages \"{restoreSettings.PackageDirectory}\" /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal";
+ List args = ["restore", "--no-dependencies", restoreSettings.File, "--packages", restoreSettings.PackageDirectory, "/p:DisableImplicitNuGetFallbackFolder=true", "--verbosity", "normal"];
if (restoreSettings.ForceDotnetRefAssemblyFetching)
{
@@ -77,23 +77,21 @@ private string GetRestoreArgs(RestoreSettings restoreSettings)
Directory.CreateDirectory(path);
}
- args += $" /p:TargetFrameworkRootPath=\"{path}\" /p:NetCoreTargetingPackRoot=\"{path}\" /p:AllowMissingPrunePackageData=true";
+ args.AddRange([$"/p:TargetFrameworkRootPath={path}", $"/p:NetCoreTargetingPackRoot={path}", "/p:AllowMissingPrunePackageData=true"]);
}
if (restoreSettings.ForceReevaluation)
{
- args += " --force";
+ args.Add("--force");
}
if (restoreSettings.TargetWindows)
{
- args += " /p:EnableWindowsTargeting=true";
+ args.Add("/p:EnableWindowsTargeting=true");
}
- if (restoreSettings.NugetSources is not null)
- {
- args += $" {restoreSettings.NugetSources}";
- }
+ var nugetSources = restoreSettings.NugetSources.SelectMany(source => ["-s", source]).ToList();
+ args.AddRange(nugetSources);
return args;
}
@@ -107,48 +105,48 @@ public RestoreResult Restore(RestoreSettings restoreSettings)
public bool New(string folder)
{
- var args = $"new console --no-restore --output \"{folder}\"";
+ List args = ["new", "console", "--no-restore", "--output", folder];
return dotnetCliInvoker.RunCommand(args);
}
public bool AddPackage(string folder, string package)
{
- var args = $"add \"{folder}\" package \"{package}\" --no-restore";
+ List args = ["add", folder, "package", package, "--no-restore"];
return dotnetCliInvoker.RunCommand(args);
}
- public IList GetListedRuntimes() => GetResultList("--list-runtimes");
+ public IList GetListedRuntimes() => GetResultList(["--list-runtimes"]);
- public IList GetListedSdks() => GetResultList("--list-sdks");
+ public IList GetListedSdks() => GetResultList(["--list-sdks"]);
- private IList GetResultList(string args, string? workingDirectory = null, bool silent = true)
+ private IList GetResultList(List args, string? workingDirectory = null, bool silent = true)
{
if (dotnetCliInvoker.RunCommand(args, workingDirectory, out var results, silent))
{
return results;
}
- logger.LogWarning($"Running 'dotnet {args}' failed.");
+ logger.LogWarning($"Running 'dotnet {string.Join(" ", args)}' failed.");
return [];
}
- public bool Exec(string execArgs)
+ public bool Exec(List execArgs)
{
- var args = $"exec {execArgs}";
+ List args = ["exec", .. execArgs];
return dotnetCliInvoker.RunCommand(args);
}
- private const string nugetListSourceCommand = "nuget list source --format Short";
+ private static readonly IReadOnlyList nugetListSourceCommandArgs = ["nuget", "list", "source", "--format", "Short"];
public IList GetNugetFeeds(string nugetConfig)
{
logger.LogInfo($"Getting NuGet feeds from '{nugetConfig}'...");
- return GetResultList($"{nugetListSourceCommand} --configfile \"{nugetConfig}\"");
+ return GetResultList([.. nugetListSourceCommandArgs, "--configfile", nugetConfig]);
}
public IList GetNugetFeedsFromFolder(string folderPath)
{
logger.LogInfo($"Getting NuGet feeds in folder '{folderPath}'...");
- return GetResultList(nugetListSourceCommand, folderPath);
+ return GetResultList(nugetListSourceCommandArgs.ToList(), folderPath);
}
// The version number should be kept in sync with the version .NET version used for building the application.
diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DotNetCliInvoker.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DotNetCliInvoker.cs
index 4c4e789973ca..c6f97c5f8be2 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DotNetCliInvoker.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/DotNetCliInvoker.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
-using System.Collections.ObjectModel;
using System.Diagnostics;
using Semmle.Util;
using Semmle.Util.Logging;
@@ -13,11 +12,11 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
internal sealed class DotNetCliInvoker : IDotNetCliInvoker
{
private readonly ILogger logger;
- private readonly DependabotProxy? proxy;
+ private readonly IDependabotProxy? proxy;
public string Exec { get; }
- public DotNetCliInvoker(ILogger logger, string exec, DependabotProxy? dependabotProxy)
+ public DotNetCliInvoker(ILogger logger, string exec, IDependabotProxy? dependabotProxy)
{
this.logger = logger;
this.proxy = dependabotProxy;
@@ -25,7 +24,7 @@ public DotNetCliInvoker(ILogger logger, string exec, DependabotProxy? dependabot
logger.LogInfo($"Using .NET CLI executable: '{Exec}'");
}
- private ProcessStartInfo MakeDotnetStartInfo(string args, string? workingDirectory)
+ private ProcessStartInfo MakeDotnetStartInfo(List args, string? workingDirectory)
{
var startInfo = new ProcessStartInfo(Exec, args)
{
@@ -57,39 +56,39 @@ private ProcessStartInfo MakeDotnetStartInfo(string args, string? workingDirecto
return startInfo;
}
- private int RunCommandExitCodeAux(string args, string? workingDirectory, out IList output, out string dirLog, bool silent)
+ private int RunCommandExitCodeAux(List args, string? workingDirectory, out IList output, out string dirLog, bool silent)
{
dirLog = string.IsNullOrWhiteSpace(workingDirectory) ? "" : $" in {workingDirectory}";
var pi = MakeDotnetStartInfo(args, workingDirectory);
var threadId = Environment.CurrentManagedThreadId;
void onOut(string s) => logger.Log(silent ? Severity.Debug : Severity.Info, s, threadId);
void onError(string s) => logger.LogError(s, threadId);
- logger.LogInfo($"Running '{Exec} {args}'{dirLog}");
+ logger.LogInfo($"Running '{Exec} {string.Join(" ", args)}'{dirLog}");
var exitCode = pi.ReadOutput(out output, onOut, onError);
return exitCode;
}
- private bool RunCommandAux(string args, string? workingDirectory, out IList output, bool silent)
+ private bool RunCommandAux(List args, string? workingDirectory, out IList output, bool silent)
{
var exitCode = RunCommandExitCodeAux(args, workingDirectory, out output, out var dirLog, silent);
if (exitCode != 0)
{
- logger.LogError($"Command '{Exec} {args}'{dirLog} failed with exit code {exitCode}");
+ logger.LogError($"Command '{Exec} {string.Join(" ", args)}'{dirLog} failed with exit code {exitCode}");
return false;
}
return true;
}
- public bool RunCommand(string args, bool silent = true) =>
+ public bool RunCommand(List args, bool silent = true) =>
RunCommandAux(args, null, out _, silent);
- public int RunCommandExitCode(string args, bool silent = true) =>
+ public int RunCommandExitCode(List args, bool silent = true) =>
RunCommandExitCodeAux(args, null, out _, out _, silent);
- public bool RunCommand(string args, out IList output, bool silent = true) =>
+ public bool RunCommand(List args, out IList output, bool silent = true) =>
RunCommandAux(args, null, out output, silent);
- public bool RunCommand(string args, string? workingDirectory, out IList output, bool silent = true) =>
+ public bool RunCommand(List args, string? workingDirectory, out IList output, bool silent = true) =>
RunCommandAux(args, workingDirectory, out output, silent);
}
}
diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FeedManager.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FeedManager.cs
index 1d5523a983cb..6c4593f3400c 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FeedManager.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FeedManager.cs
@@ -1,15 +1,8 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
-using System.IO;
using System.Linq;
-using System.Net;
-using System.Net.Http;
-using System.Security.Cryptography.X509Certificates;
-using System.Text;
using System.Text.RegularExpressions;
-using System.Threading;
-using System.Threading.Tasks;
using Semmle.Util;
using Semmle.Util.Logging;
@@ -21,10 +14,10 @@ internal sealed partial class FeedManager : IDisposable
private readonly ILogger logger;
private readonly IDotNet dotnet;
- private readonly FileProvider fileProvider;
- private readonly DependabotProxy? dependabotProxy;
+ private readonly IFileProvider fileProvider;
private readonly DependencyDirectory emptyPackageDirectory;
private readonly ImmutableHashSet privateRegistryFeeds;
+ private readonly IFeedManagerIO feedManagerIo;
///
/// Gets whether there are private package registries configured for C#.
@@ -60,17 +53,12 @@ internal sealed partial class FeedManager : IDisposable
///
public ImmutableHashSet InheritedFeeds => AllFeeds.Except(ExplicitFeeds).ToImmutableHashSet();
- private readonly Lazy<(bool, ImmutableHashSet)> lazyReachableExplicitFeeds;
-
- ///
- /// Gets whether there was a timeout when checking the reachability of the explicitly configured NuGet feeds.
- ///
- public bool ExplicitFeedTimeout => lazyReachableExplicitFeeds.Value.Item1;
+ private readonly Lazy> lazyReachableExplicitFeeds;
///
/// Gets the list of reachable NuGet feeds that are explicitly configured.
///
- public ImmutableHashSet ReachableExplicitFeeds => lazyReachableExplicitFeeds.Value.Item2;
+ public ImmutableHashSet ReachableExplicitFeeds => lazyReachableExplicitFeeds.Value;
private readonly Lazy> lazyReachableFeeds;
///
@@ -84,27 +72,23 @@ internal sealed partial class FeedManager : IDisposable
///
public ImmutableHashSet ReachableFallbackFeeds => lazyReachableFallbackFeeds.Value;
- public FeedManager(ILogger logger, IDotNet dotnet, DependabotProxy? dependabotProxy, FileProvider fileProvider)
+ public FeedManager(ILogger logger, IDotNet dotnet, IDependabotProxy? dependabotProxy, IFileProvider fileProvider, IFeedManagerIO feedManagerIo)
{
this.logger = logger;
this.dotnet = dotnet;
- this.dependabotProxy = dependabotProxy;
this.fileProvider = fileProvider;
+ this.feedManagerIo = feedManagerIo;
privateRegistryFeeds = dependabotProxy?.RegistryURLs.ToImmutableHashSet() ?? [];
HasPrivateRegistryFeeds = privateRegistryFeeds.Count > 0;
emptyPackageDirectory = new DependencyDirectory("empty", "empty package", logger);
lazyExplicitFeeds = new Lazy>(GetExplicitFeeds);
lazyAllFeeds = new Lazy>(GetAllFeeds);
- lazyReachableExplicitFeeds = new Lazy<(bool, ImmutableHashSet)>(() =>
- {
- var timeout = CheckSpecifiedFeeds(ExplicitFeeds, out var reachableFeeds);
- return (timeout, reachableFeeds);
- });
+ lazyReachableExplicitFeeds = new Lazy>(() => CheckSpecifiedFeeds(ExplicitFeeds));
lazyReachableFeeds = new Lazy>(() =>
{
// Inherited feeds should only be used, if they are indeed reachable (as they may be environment specific).
- CheckSpecifiedFeeds(InheritedFeeds, out var reachableInheritedFeeds);
+ var reachableInheritedFeeds = CheckSpecifiedFeeds(InheritedFeeds);
return ReachableExplicitFeeds.Union(reachableInheritedFeeds).ToImmutableHashSet();
});
lazyReachableFallbackFeeds = new Lazy>(() =>
@@ -114,17 +98,9 @@ public FeedManager(ILogger logger, IDotNet dotnet, DependabotProxy? dependabotPr
});
}
- private string? GetDirectoryName(string path)
+ public FeedManager(ILogger logger, IDotNet dotnet, IDependabotProxy? dependabotProxy, IFileProvider fileProvider)
+ : this(logger, dotnet, dependabotProxy, fileProvider, new FeedManagerIO(logger, dependabotProxy))
{
- try
- {
- return new FileInfo(path).Directory?.FullName;
- }
- catch (Exception exc)
- {
- logger.LogWarning($"Failed to get directory of '{path}': {exc}");
- }
- return null;
}
private IEnumerable GetFeeds(Func> getNugetFeeds)
@@ -166,25 +142,16 @@ private IEnumerable GetFeedsFromNugetConfig(string nugetConfigPath) =>
/// If there are no feeds, a dummy source argument is added to override any default feeds that `restore` would use.
///
/// The list of feeds to use for the restore command.
- /// The prefix to use for each source argument (e.g., "-s").
- /// The constructed NuGet sources argument for the restore command.
- public string FeedsToRestoreArgument(IEnumerable feeds, string sourceArgumentPrefix)
+ /// The list of NuGet sources arguments for the restore command.
+ public List RestoreFeeds(IEnumerable feeds)
{
// If there are no feeds, we want to override any default feeds that `restore` would use by passing a dummy source argument.
if (!feeds.Any())
{
- return $" {sourceArgumentPrefix} \"{emptyPackageDirectory.DirInfo.FullName}\"";
+ return [emptyPackageDirectory.DirInfo.FullName];
}
- // Add package sources. If any are present, they override all sources specified in
- // the configuration file(s).
- var feedArgs = new StringBuilder();
- foreach (var feed in feeds)
- {
- feedArgs.Append($" {sourceArgumentPrefix} \"{feed}\"");
- }
-
- return feedArgs.ToString();
+ return feeds.ToList();
}
private IEnumerable FeedsToUseAux(HashSet feedsToConsider)
@@ -211,40 +178,30 @@ private IEnumerable FeedsToUseAux(HashSet feedsToConsider)
public IEnumerable FeedsToUse(string path)
{
// Find the path specific feeds.
- var folder = GetDirectoryName(path);
+ var folder = feedManagerIo.GetDirectoryName(path);
var feedsToConsider = folder is not null ? GetFeedsFromFolder(folder).ToHashSet() : new HashSet();
return FeedsToUseAux(feedsToConsider);
}
- ///
- /// Constructs the NuGet sources argument for the `dotnet restore` command based on the given feeds.
- ///
- /// The list of NuGet feeds to use for the restore command.
- /// A string representing the NuGet sources argument for the `dotnet restore` command.
- public string FeedsToDotnetRestoreArgument(IEnumerable feeds)
- {
- return FeedsToRestoreArgument(feeds, "-s");
- }
-
///
/// Constructs the list of NuGet sources to use for dotnet restore.
/// (1) Use the feeds we get from `dotnet nuget list source`
/// (2) Use private registries, if they are configured
///
/// Path to project/solution
- /// A string representing the NuGet sources argument for the `dotnet restore` command.
- public string? MakeDotnetRestoreSourcesArgument(string path)
+ /// A list representing the NuGet sources arguments for the `dotnet restore` command.
+ public List MakeRestoreFeeds(string path)
{
// Do not construct a set of explicit NuGet sources to use for restore.
if (!CheckNugetFeedResponsiveness && !HasPrivateRegistryFeeds)
{
- return null;
+ return [];
}
var feedsToUse = FeedsToUse(path);
- return FeedsToDotnetRestoreArgument(feedsToUse);
+ return RestoreFeeds(feedsToUse);
}
private (int initialTimeout, int tryCount) GetFeedRequestSettings(bool isFallback)
@@ -266,79 +223,6 @@ public string FeedsToDotnetRestoreArgument(IEnumerable feeds)
return (timeoutMilliSeconds, tryCount);
}
- private static async Task ExecuteGetRequest(string address, HttpClient httpClient, CancellationToken cancellationToken)
- {
- return await httpClient.GetAsync(address, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
- }
-
- private bool IsFeedReachable(string feed, int timeoutMilliSeconds, int tryCount, out bool isTimeout)
- {
- logger.LogInfo($"Checking if NuGet feed '{feed}' is reachable...");
-
- // Configure the HttpClient to be aware of the Dependabot Proxy, if used.
- HttpClientHandler httpClientHandler = new();
- if (dependabotProxy != null)
- {
- httpClientHandler.Proxy = new WebProxy(dependabotProxy.Address);
-
- if (dependabotProxy.Certificate != null)
- {
- httpClientHandler.ServerCertificateCustomValidationCallback = (message, cert, chain, _) =>
- {
- if (chain is null || cert is null)
- {
- var msg = cert is null && chain is null
- ? "certificate and chain"
- : chain is null
- ? "chain"
- : "certificate";
- logger.LogWarning($"Dependabot proxy certificate validation failed due to missing {msg}");
- return false;
- }
- chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
- chain.ChainPolicy.CustomTrustStore.Add(dependabotProxy.Certificate);
- return chain.Build(cert);
- };
- }
- }
-
- using HttpClient client = new(httpClientHandler);
-
- isTimeout = false;
-
- for (var i = 0; i < tryCount; i++)
- {
- using var cts = new CancellationTokenSource();
- cts.CancelAfter(timeoutMilliSeconds);
- try
- {
- logger.LogInfo($"Attempt {i + 1}/{tryCount} to reach NuGet feed '{feed}'.");
- using var response = ExecuteGetRequest(feed, client, cts.Token).GetAwaiter().GetResult();
- response.EnsureSuccessStatusCode();
- logger.LogInfo($"Querying NuGet feed '{feed}' succeeded.");
- return true;
- }
- catch (Exception exc)
- {
- if (exc is TaskCanceledException tce &&
- tce.CancellationToken == cts.Token &&
- cts.Token.IsCancellationRequested)
- {
- logger.LogInfo($"Didn't receive answer from NuGet feed '{feed}' in {timeoutMilliSeconds}ms.");
- timeoutMilliSeconds *= 2;
- continue;
- }
-
- logger.LogInfo($"Querying NuGet feed '{feed}' failed. The reason for the failure: {exc.Message}");
- return false;
- }
- }
-
- logger.LogWarning($"Didn't receive answer from NuGet feed '{feed}'. Tried it {tryCount} times.");
- isTimeout = true;
- return false;
- }
-
///
/// Retrieves a list of excluded NuGet feeds from the corresponding environment variable.
///
@@ -359,12 +243,8 @@ private HashSet GetExcludedFeeds()
/// Checks that we can connect to the specified NuGet feeds.
///
/// The set of package feeds to check.
- /// The list of feeds that were reachable.
- ///
- /// True if there is a timeout when trying to reach the feeds (excluding any feeds that are configured
- /// to be excluded from the check) or false otherwise.
- ///
- private bool CheckSpecifiedFeeds(ImmutableHashSet feeds, out ImmutableHashSet reachableFeeds)
+ /// The list of feeds that were reachable.
+ private ImmutableHashSet CheckSpecifiedFeeds(ImmutableHashSet feeds)
{
// Exclude any feeds from the feed check that are configured by the corresponding environment variable.
// These feeds are always assumed to be reachable.
@@ -380,12 +260,10 @@ private bool CheckSpecifiedFeeds(ImmutableHashSet feeds, out ImmutableHa
return true;
}).ToHashSet();
- var reachable = GetReachableNuGetFeeds(feedsToCheck, isFallback: false, out var isTimeout);
+ var reachable = GetReachableNuGetFeeds(feedsToCheck, isFallback: false);
// Always consider feeds excluded for the reachability check as reachable.
- reachableFeeds = reachable.Union(feeds.Where(feed => excludedFeeds.Contains(feed))).ToImmutableHashSet();
-
- return isTimeout;
+ return reachable.Union(feeds.Where(feed => excludedFeeds.Contains(feed))).ToImmutableHashSet();
}
///
@@ -398,7 +276,7 @@ public bool IsDefaultFeedReachable()
if (CheckNugetFeedResponsiveness)
{
var (initialTimeout, tryCount) = GetFeedRequestSettings(isFallback: false);
- return IsFeedReachable(PublicNugetOrgFeed, initialTimeout, tryCount, out var _);
+ return feedManagerIo.IsFeedReachable(PublicNugetOrgFeed, initialTimeout, tryCount);
}
return true;
@@ -409,22 +287,15 @@ public bool IsDefaultFeedReachable()
///
/// The feeds to check.
/// Whether the feeds are fallback feeds or not.
- /// Whether a timeout occurred while checking the feeds.
/// The list of feeds that could be reached.
- private List GetReachableNuGetFeeds(HashSet feedsToCheck, bool isFallback, out bool isTimeout)
+ private List GetReachableNuGetFeeds(HashSet feedsToCheck, bool isFallback)
{
var fallbackStr = isFallback ? "fallback " : "";
logger.LogInfo($"Checking {fallbackStr}NuGet feed reachability on feeds: {string.Join(", ", feedsToCheck.OrderBy(f => f))}");
var (initialTimeout, tryCount) = GetFeedRequestSettings(isFallback);
- var timeout = false;
var reachableFeeds = feedsToCheck
- .Where(feed =>
- {
- var reachable = IsFeedReachable(feed, initialTimeout, tryCount, out var feedTimeout);
- timeout |= feedTimeout;
- return reachable;
- })
+ .Where(feed => feedManagerIo.IsFeedReachable(feed, initialTimeout, tryCount))
.ToList();
if (reachableFeeds.Count == 0)
@@ -436,7 +307,6 @@ private List GetReachableNuGetFeeds(HashSet feedsToCheck, bool i
logger.LogInfo($"Reachable {fallbackStr}NuGet feeds: {string.Join(", ", reachableFeeds.OrderBy(f => f))}");
}
- isTimeout = timeout;
return reachableFeeds;
}
@@ -460,7 +330,7 @@ private List GetReachableFallbackNugetFeeds()
}
}
- return GetReachableNuGetFeeds(fallbackFeeds, isFallback: true, out var _);
+ return GetReachableNuGetFeeds(fallbackFeeds, isFallback: true);
}
private ImmutableHashSet GetExplicitFeeds()
@@ -509,7 +379,7 @@ private ImmutableHashSet GetAllFeeds()
if (nugetConfigs.Count > 0)
{
var nugetConfigFeeds = nugetConfigs
- .Select(GetDirectoryName)
+ .Select(feedManagerIo.GetDirectoryName)
.Where(folder => folder != null)
.SelectMany(folder => GetFeedsFromFolder(folder!))
.ToHashSet();
diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FeedManagerIO.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FeedManagerIO.cs
new file mode 100644
index 000000000000..8e771f6037a4
--- /dev/null
+++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FeedManagerIO.cs
@@ -0,0 +1,109 @@
+
+using System;
+using System.IO;
+using Semmle.Util.Logging;
+using System.Net.Http;
+using System.Net;
+using System.Security.Cryptography.X509Certificates;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Semmle.Extraction.CSharp.DependencyFetching
+{
+ public class FeedManagerIO : IFeedManagerIO
+ {
+ private readonly ILogger logger;
+ private readonly IDependabotProxy? dependabotProxy;
+
+ public FeedManagerIO(ILogger logger, IDependabotProxy? dependabotProxy)
+ {
+ this.logger = logger;
+ this.dependabotProxy = dependabotProxy;
+ }
+
+ public string? GetDirectoryName(string path)
+ {
+ try
+ {
+ return new FileInfo(path).Directory?.FullName;
+ }
+ catch (Exception exc)
+ {
+ logger.LogWarning($"Failed to get directory of '{path}': {exc}");
+ }
+ return null;
+ }
+
+ private static async Task ExecuteGetRequest(string address, HttpClient httpClient, CancellationToken cancellationToken)
+ {
+ return await httpClient.GetAsync(address, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
+ }
+
+ public bool IsFeedReachable(string feed, int timeoutMilliSeconds, int tryCount)
+ {
+ logger.LogInfo($"Checking if NuGet feed '{feed}' is reachable...");
+
+ // Configure the HttpClient to be aware of the Dependabot Proxy, if used.
+ HttpClientHandler httpClientHandler = new();
+ if (dependabotProxy != null)
+ {
+ httpClientHandler.Proxy = new WebProxy(dependabotProxy.Address);
+
+ if (dependabotProxy.Certificate != null)
+ {
+ httpClientHandler.ServerCertificateCustomValidationCallback = (message, cert, chain, _) =>
+ {
+ if (chain is null || cert is null)
+ {
+ var msg = cert is null && chain is null
+ ? "certificate and chain"
+ : chain is null
+ ? "chain"
+ : "certificate";
+ logger.LogWarning($"Dependabot proxy certificate validation failed due to missing {msg}");
+ return false;
+ }
+ chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
+ chain.ChainPolicy.CustomTrustStore.Add(dependabotProxy.Certificate);
+ return chain.Build(cert);
+ };
+ }
+ }
+
+ using HttpClient client = new(httpClientHandler);
+
+ for (var i = 0; i < tryCount; i++)
+ {
+ using var cts = new CancellationTokenSource();
+ cts.CancelAfter(timeoutMilliSeconds);
+ try
+ {
+ logger.LogInfo($"Attempt {i + 1}/{tryCount} to reach NuGet feed '{feed}'.");
+ using var response = ExecuteGetRequest(feed, client, cts.Token).GetAwaiter().GetResult();
+ response.EnsureSuccessStatusCode();
+ logger.LogInfo($"Querying NuGet feed '{feed}' succeeded.");
+ return true;
+ }
+ catch (Exception exc)
+ {
+ if (exc is TaskCanceledException tce &&
+ tce.CancellationToken == cts.Token &&
+ cts.Token.IsCancellationRequested)
+ {
+ logger.LogInfo($"Didn't receive answer from NuGet feed '{feed}' in {timeoutMilliSeconds}ms.");
+ timeoutMilliSeconds *= 2;
+ continue;
+ }
+
+ logger.LogInfo($"Querying NuGet feed '{feed}' failed. The reason for the failure: {exc.Message}");
+ return false;
+ }
+ }
+
+ logger.LogWarning($"Didn't receive answer from NuGet feed '{feed}'. Tried it {tryCount} times.");
+ return false;
+ }
+
+
+ }
+}
diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FileProvider.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FileProvider.cs
index 9e6b810b95ec..4f55bbff7f27 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FileProvider.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/FileProvider.cs
@@ -2,12 +2,11 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using System.Security.Policy;
using Semmle.Util.Logging;
namespace Semmle.Extraction.CSharp.DependencyFetching
{
- public class FileProvider
+ public class FileProvider : IFileProvider
{
private static readonly HashSet binaryFileExtensions = [".dll", ".exe"]; // TODO: add more binary file extensions.
diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/IDependabotProxy.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/IDependabotProxy.cs
new file mode 100644
index 000000000000..37a11900fddf
--- /dev/null
+++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/IDependabotProxy.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Security.Cryptography.X509Certificates;
+
+namespace Semmle.Extraction.CSharp.DependencyFetching
+{
+ public interface IDependabotProxy : IDisposable
+ {
+ ///
+ /// The full address of the Dependabot proxy, if available.
+ ///
+ string Address { get; }
+
+ ///
+ /// The URLs of package registries that are configured for the proxy.
+ ///
+ HashSet RegistryURLs { get; }
+
+ ///
+ /// The path to the temporary file where the certificate is stored.
+ ///
+ string? CertificatePath { get; }
+
+ ///
+ /// The certificate used for the Dependabot proxy.
+ ///
+ X509Certificate2? Certificate { get; }
+ }
+}
diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/IDependabotProxyConfiguration.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/IDependabotProxyConfiguration.cs
new file mode 100644
index 000000000000..c67ee4fc39df
--- /dev/null
+++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/IDependabotProxyConfiguration.cs
@@ -0,0 +1,21 @@
+using System;
+
+namespace Semmle.Extraction.CSharp.DependencyFetching
+{
+ public interface IDependabotProxyConfiguration
+ {
+ // The host of the Dependabot proxy, if available.
+ string? Host { get; }
+
+ // The port of the Dependabot proxy, if available.
+ string? Port { get; }
+
+ // The certificate of the Dependabot proxy, if available.
+ string? Certificate { get; }
+
+ // The list of package registries that are configured for the proxy, if any.
+ // The value of the environment variable should be a JSON array of objects, such as:
+ // [ { "type": "nuget_feed", "url": "https://nuget.pkg.github.com/org/index.json" } ]
+ string? RegistryURLs { get; }
+ }
+}
diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/IDotNet.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/IDotNet.cs
index 394a05e9e596..0e93fa92813a 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/IDotNet.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/IDotNet.cs
@@ -12,12 +12,12 @@ public interface IDotNet
bool AddPackage(string folder, string package);
IList GetListedRuntimes();
IList GetListedSdks();
- bool Exec(string execArgs);
+ bool Exec(List execArgs);
IList GetNugetFeeds(string nugetConfig);
IList GetNugetFeedsFromFolder(string folderPath);
}
- public record class RestoreSettings(string File, string PackageDirectory, bool ForceDotnetRefAssemblyFetching, string? NugetSources = null, bool ForceReevaluation = false, bool TargetWindows = false);
+ public record class RestoreSettings(string File, string PackageDirectory, bool ForceDotnetRefAssemblyFetching, List NugetSources, bool ForceReevaluation = false, bool TargetWindows = false);
public partial record class RestoreResult(bool Success, IList Output)
{
diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/IDotNetCliInvoker.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/IDotNetCliInvoker.cs
index ef5bcd4753bb..b5400ec63319 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/IDotNetCliInvoker.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/IDotNetCliInvoker.cs
@@ -30,26 +30,26 @@ internal interface IDotNetCliInvoker
/// Execute `dotnet ` and return true if the command succeeded, otherwise false.
/// If `silent` is true the output of the command is logged as `debug` otherwise as `info`.
///
- bool RunCommand(string args, bool silent = true);
+ bool RunCommand(List args, bool silent = true);
///
/// Execute `dotnet ` and return the exit code.
/// If `silent` is true the output of the command is logged as `debug` otherwise as `info`.
///
- int RunCommandExitCode(string args, bool silent = true);
+ int RunCommandExitCode(List args, bool silent = true);
///
/// Execute `dotnet ` and return true if the command succeeded, otherwise false.
/// The output of the command is returned in `output`.
/// If `silent` is true the output of the command is logged as `debug` otherwise as `info`.
///
- bool RunCommand(string args, out IList output, bool silent = true);
+ bool RunCommand(List args, out IList output, bool silent = true);
///
/// Execute `dotnet ` in `` and return true if the command succeeded, otherwise false.
/// The output of the command is returned in `output`.
/// If `silent` is true the output of the command is logged as `debug` otherwise as `info`.
///
- bool RunCommand(string args, string? workingDirectory, out IList output, bool silent = true);
+ bool RunCommand(List args, string? workingDirectory, out IList output, bool silent = true);
}
}
diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/IFeedManagerIO.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/IFeedManagerIO.cs
new file mode 100644
index 000000000000..380c5f3b5f28
--- /dev/null
+++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/IFeedManagerIO.cs
@@ -0,0 +1,16 @@
+
+namespace Semmle.Extraction.CSharp.DependencyFetching
+{
+ public interface IFeedManagerIO
+ {
+ ///
+ /// Gets the directory name of the specified path.
+ ///
+ string? GetDirectoryName(string path);
+
+ ///
+ /// Returns true if the feed is reachable within the specified timeout and try count.
+ ///
+ bool IsFeedReachable(string feed, int timeoutMilliSeconds, int tryCount);
+ }
+}
diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/IFileProvider.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/IFileProvider.cs
new file mode 100644
index 000000000000..919be0af61a7
--- /dev/null
+++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/IFileProvider.cs
@@ -0,0 +1,22 @@
+using System.Collections.Generic;
+using System.IO;
+
+namespace Semmle.Extraction.CSharp.DependencyFetching
+{
+ public interface IFileProvider
+ {
+ DirectoryInfo SourceDir { get; }
+ IEnumerable SmallNonBinary { get; }
+ IEnumerable Sources { get; }
+ ICollection Projects { get; }
+ ICollection Solutions { get; }
+ IEnumerable Dlls { get; }
+ ICollection NugetConfigs { get; }
+ ICollection NugetExes { get; }
+ string? RootNugetConfig { get; }
+ IEnumerable GlobalJsons { get; }
+ ICollection PackagesConfigs { get; }
+ ICollection RazorViews { get; }
+ ICollection Resources { get; }
+ }
+}
diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs
index aeedbc176867..85d6056d7218 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/NugetPackageRestorer.cs
@@ -15,7 +15,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
{
internal sealed partial class NugetPackageRestorer : IDisposable
{
- private readonly FileProvider fileProvider;
+ private readonly IFileProvider fileProvider;
private readonly FileContent fileContent;
private readonly IDotNet dotnet;
private readonly IDiagnosticsWriter diagnosticsWriter;
@@ -29,10 +29,10 @@ internal sealed partial class NugetPackageRestorer : IDisposable
public NugetPackageRestorer(
- FileProvider fileProvider,
+ IFileProvider fileProvider,
FileContent fileContent,
IDotNet dotnet,
- DependabotProxy? dependabotProxy,
+ IDependabotProxy? dependabotProxy,
IDiagnosticsWriter diagnosticsWriter,
ILogger logger,
ICompilationInfoContainer compilationInfoContainer)
@@ -53,7 +53,7 @@ public NugetPackageRestorer(
public string? TryRestore(string package)
{
var feeds = feedManager.CheckNugetFeedResponsiveness ? feedManager.ReachableFeeds : feedManager.AllFeeds;
- var nugetSources = feedManager.FeedsToDotnetRestoreArgument(feeds);
+ var nugetSources = feedManager.RestoreFeeds(feeds);
if (TryRestorePackageManually(package, nugetSources))
{
var packageDir = DependencyManager.GetPackageDirectory(package, missingPackageDirectory.DirInfo);
@@ -127,19 +127,8 @@ public HashSet Restore()
compilationInfoContainer.CompilationInfos.Add(("Inherited NuGet feed count", inheritedFeeds.Count.ToString()));
}
- var allExplicitReachable = explicitFeeds.Count == feedManager.ReachableExplicitFeeds.Count;
- EmitUnreachableFeedsDiagnostics(allExplicitReachable);
-
- if (feedManager.ExplicitFeedTimeout)
- {
- // If we experience a timeout, we use this fallback.
- // todo: we could also check the reachability of the inherited nuget feeds, but to use those in the fallback we would need to handle authentication too.
- var unresponsiveMissingPackageLocation = DownloadMissingPackages([]);
- return unresponsiveMissingPackageLocation is null
- ? []
- : [unresponsiveMissingPackageLocation];
- }
-
+ var unreachableExplicitFeeds = explicitFeeds.Except(feedManager.ReachableExplicitFeeds).ToImmutableHashSet();
+ EmitFeedReachabilityDiagnostics(unreachableExplicitFeeds);
}
try
@@ -227,7 +216,7 @@ private IEnumerable RestoreSolutions(out DependencyContainer dependencie
var projects = fileProvider.Solutions.SelectMany(solution =>
{
logger.LogInfo($"Restoring solution {solution}...");
- var nugetSources = feedManager.MakeDotnetRestoreSourcesArgument(solution);
+ var nugetSources = feedManager.MakeRestoreFeeds(solution);
var res = dotnet.Restore(new(solution, PackageDirectory.DirInfo.FullName, ForceDotnetRefAssemblyFetching: true, NugetSources: nugetSources, TargetWindows: isWindows));
if (res.Success)
{
@@ -275,7 +264,7 @@ private void RestoreProjects(IEnumerable projects, out ConcurrentBag projects, out ConcurrentBag p.ToLowerInvariant());
var alreadyDownloadedLegacyPackages = GetRestoredLegacyPackageNames();
@@ -443,7 +432,7 @@ private static IEnumerable GetRestoredPackageDirectoryNames(DirectoryInf
.Select(d => Path.GetFileName(d).ToLowerInvariant());
}
- private bool TryRestorePackageManually(string package, string? nugetSources, PackageReferenceSource packageReferenceSource = PackageReferenceSource.SdkCsProj, bool tryPrereleaseVersion = true)
+ private bool TryRestorePackageManually(string package, List nugetSources, PackageReferenceSource packageReferenceSource = PackageReferenceSource.SdkCsProj, bool tryPrereleaseVersion = true)
{
logger.LogInfo($"Restoring package {package}...");
using var tempDir = new TemporaryDirectory(
@@ -471,11 +460,11 @@ private bool TryRestorePackageManually(string package, string? nugetSources, Pac
return true;
}
- if (!feedManager.CheckNugetFeedResponsiveness && res.HasNugetPackageSourceError && nugetSources is not null)
+ if (!feedManager.CheckNugetFeedResponsiveness && res.HasNugetPackageSourceError && nugetSources.Count > 0)
{
logger.LogDebug($"Trying to restore '{package}' without explicitly providing NuGet sources.");
// Restore could not be completed because the listed source is unavailable. Try without an explicit restore source argument.
- res = TryRestorePackageManually(package, nugetSources: null, tempDir, tryPrereleaseVersion);
+ res = TryRestorePackageManually(package, [], tempDir, tryPrereleaseVersion);
if (res.Success)
{
return true;
@@ -486,16 +475,16 @@ private bool TryRestorePackageManually(string package, string? nugetSources, Pac
return false;
}
- private RestoreResult TryRestorePackageManually(string package, string? nugetSources, TemporaryDirectory tempDir, bool tryPrereleaseVersion)
+ private RestoreResult TryRestorePackageManually(string package, List nugetSources, TemporaryDirectory tempDir, bool tryPrereleaseVersion)
{
- var res = dotnet.Restore(new(tempDir.DirInfo.FullName, missingPackageDirectory.DirInfo.FullName, ForceDotnetRefAssemblyFetching: false, NugetSources: nugetSources, ForceReevaluation: true));
+ var res = dotnet.Restore(new(tempDir.DirInfo.FullName, missingPackageDirectory.DirInfo.FullName, ForceDotnetRefAssemblyFetching: false, nugetSources, ForceReevaluation: true));
if (!res.Success && tryPrereleaseVersion && res.HasNugetNoStablePackageVersionError)
{
logger.LogDebug($"Failed to restore nuget package {package} because no stable version was found.");
TryChangePackageVersion(tempDir.DirInfo, "*-*");
- res = dotnet.Restore(new(tempDir.DirInfo.FullName, missingPackageDirectory.DirInfo.FullName, ForceDotnetRefAssemblyFetching: false, NugetSources: nugetSources, ForceReevaluation: true));
+ res = dotnet.Restore(new(tempDir.DirInfo.FullName, missingPackageDirectory.DirInfo.FullName, ForceDotnetRefAssemblyFetching: false, nugetSources, ForceReevaluation: true));
if (!res.Success)
{
TryChangePackageVersion(tempDir.DirInfo, "*");
@@ -546,26 +535,51 @@ private void TryChangeProjectFile(DirectoryInfo projectDir, Regex pattern, strin
}
}
+ private string SanitizeFeedForLogging(string feed)
+ {
+
+ try
+ {
+ // If the feed is a URL, log only the scheme, host, port, and absolute path to avoid logging sensitive information such as credentials or tokens.
+ var uri = new Uri(feed);
+ var port = uri.IsDefaultPort ? string.Empty : $":{uri.Port}";
+ return $"{uri.Scheme}://{uri.Host}{port}{uri.AbsolutePath}";
+ }
+ catch
+ {
+ return feed;
+ }
+ }
+
///
- /// If is `false`, logs this and emits a diagnostic.
+ /// If is not empty, logs this and emits a diagnostic.
/// Adds a `CompilationInfos` entry either way.
///
- /// Whether all feeds were reachable or not.
- private void EmitUnreachableFeedsDiagnostics(bool allFeedsReachable)
+ /// The feeds that were not reachable.
+ private void EmitFeedReachabilityDiagnostics(ImmutableHashSet unreachableFeeds)
{
- if (!allFeedsReachable)
+ if (unreachableFeeds.Count > 0)
{
- logger.LogWarning("Found unreachable NuGet feed in C# analysis with build-mode 'none'. This may cause missing dependencies in the analysis.");
+ var orderedUnreachableFeeds = unreachableFeeds
+ .Select(SanitizeFeedForLogging)
+ .OrderBy(feed => feed)
+ .ToList();
+ var unreachableFeedList = string.Join(", ", orderedUnreachableFeeds);
+ logger.LogWarning($"Found unreachable NuGet feeds in C# analysis with build-mode 'none': {unreachableFeedList}. This may cause missing dependencies in the analysis.");
+ compilationInfoContainer.CompilationInfos.Add(("Unreachable NuGet feeds", unreachableFeedList));
diagnosticsWriter.AddEntry(new DiagnosticMessage(
Language.CSharp,
"buildless/unreachable-feed",
- "Found unreachable NuGet feed in C# analysis with build-mode 'none'",
+ "Found unreachable NuGet feeds in C# analysis with build-mode 'none'",
visibility: new DiagnosticMessage.TspVisibility(statusPage: true, cliSummaryTable: true, telemetry: true),
- markdownMessage: "Found unreachable NuGet feed in C# analysis with build-mode 'none'. This may cause missing dependencies in the analysis.",
+ markdownMessage: string.Format(
+ "Found unreachable NuGet feeds in C# analysis with build-mode 'none':\n\n{0}\n\nThis may cause missing dependencies in the analysis.",
+ string.Join("\n", orderedUnreachableFeeds.Select(feed => $"- `{feed}`"))
+ ),
severity: DiagnosticMessage.TspSeverity.Note
));
}
- compilationInfoContainer.CompilationInfos.Add(("All NuGet feeds reachable", allFeedsReachable ? "1" : "0"));
+ compilationInfoContainer.CompilationInfos.Add(("All NuGet feeds reachable", unreachableFeeds.Count == 0 ? "1" : "0"));
}
private void EmitNugetConfigDiagnostics()
@@ -625,14 +639,6 @@ public void Dispose()
feedManager.Dispose();
}
- ///
- /// Returns the full path to a temporary directory with the given subfolder name.
- ///
- private static string ComputeTempDirectoryPath(string subfolderName)
- {
- return Path.Join(FileUtils.GetTemporaryWorkingDirectory(out _), subfolderName);
- }
-
///
/// Computes a unique temporary directory path based on the source directory and the subfolder name.
///
diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/PackagesConfigRestorer.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/PackagesConfigRestorer.cs
index 05c10cfb2bf3..d4403bb955ef 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/PackagesConfigRestorer.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/PackagesConfigRestorer.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
-using System.Collections.Immutable;
using System.Diagnostics;
using System.IO;
using System.Linq;
@@ -34,7 +33,7 @@ internal interface IPackagesConfigRestore
///
internal class PackagesConfigRestoreFactory
{
- public static IPackagesConfigRestore Create(FileProvider fileProvider, DependencyDirectory packageDirectory, Semmle.Util.Logging.ILogger logger, FeedManager feedManager)
+ public static IPackagesConfigRestore Create(IFileProvider fileProvider, DependencyDirectory packageDirectory, Semmle.Util.Logging.ILogger logger, FeedManager feedManager)
{
if (SystemBuildActions.Instance.IsWindows() || SystemBuildActions.Instance.IsMonoInstalled())
{
@@ -56,7 +55,7 @@ private class NugetExeWrapper : IPackagesConfigRestore
public int PackageCount => fileProvider.PackagesConfigs.Count;
- private readonly FileProvider fileProvider;
+ private readonly IFileProvider fileProvider;
///
/// The packages directory.
@@ -75,7 +74,7 @@ private class NugetExeWrapper : IPackagesConfigRestore
///
/// Create the package manager for a specified source tree.
///
- public NugetExeWrapper(FileProvider fileProvider, DependencyDirectory packageDirectory, Semmle.Util.Logging.ILogger logger, FeedManager feedManager)
+ public NugetExeWrapper(IFileProvider fileProvider, DependencyDirectory packageDirectory, Semmle.Util.Logging.ILogger logger, FeedManager feedManager)
{
this.fileProvider = fileProvider;
this.packageDirectory = packageDirectory;
@@ -168,7 +167,7 @@ private bool TryRestoreNugetPackage(string packagesConfig)
{
logger.LogInfo($"Restoring file \"{packagesConfig}\"...");
- var sourcesArgument = "";
+ List sourcesArgument = [];
var feedsToUse = feedManager.FeedsToUse(packagesConfig).ToList();
var useDefaultFeed = feedsToUse.Count == 0 && IsDefaultFeedReachable;
@@ -180,7 +179,8 @@ private bool TryRestoreNugetPackage(string packagesConfig)
{
feedsToUse.Add(FeedManager.PublicNugetOrgFeed);
}
- sourcesArgument = feedManager.FeedsToRestoreArgument(feedsToUse, "-Source");
+ var restoreFeeds = feedManager.RestoreFeeds(feedsToUse);
+ sourcesArgument = restoreFeeds.SelectMany(feed => ["-Source", feed]).ToList();
}
/* Use nuget.exe to install a package.
@@ -189,16 +189,18 @@ private bool TryRestoreNugetPackage(string packagesConfig)
* really unwieldy and this solution works for now.
*/
- string exe, args;
+ string exe;
+ List args;
+
if (RunWithMono)
{
exe = "mono";
- args = $"\"{nugetExe}\" install -OutputDirectory \"{packageDirectory}\" {sourcesArgument} \"{packagesConfig}\"";
+ args = [nugetExe!, "install", "-OutputDirectory", packageDirectory.ToString(), .. sourcesArgument, packagesConfig];
}
else
{
exe = nugetExe!;
- args = $"install -OutputDirectory \"{packageDirectory}\" {sourcesArgument} \"{packagesConfig}\"";
+ args = ["install", "-OutputDirectory", packageDirectory.ToString(), .. sourcesArgument, packagesConfig];
}
var pi = new ProcessStartInfo(exe, args)
@@ -214,7 +216,7 @@ private bool TryRestoreNugetPackage(string packagesConfig)
var exitCode = pi.ReadOutput(out _, onOut, onError);
if (exitCode != 0)
{
- logger.LogError($"Command {pi.FileName} {pi.Arguments} failed with exit code {exitCode}");
+ logger.LogError($"Command {pi.FileName} {string.Join(" ", pi.ArgumentList)} failed with exit code {exitCode}");
return false;
}
else
diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/SourceGenerators/DotnetSourceGeneratorBase.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/SourceGenerators/DotnetSourceGeneratorBase.cs
index c17981803ddb..6a150e8d542a 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/SourceGenerators/DotnetSourceGeneratorBase.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/SourceGenerators/DotnetSourceGeneratorBase.cs
@@ -9,14 +9,14 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
{
internal abstract class DotnetSourceGeneratorBase : SourceGeneratorBase where T : DotnetSourceGeneratorWrapper
{
- protected readonly FileProvider fileProvider;
+ protected readonly IFileProvider fileProvider;
protected readonly FileContent fileContent;
protected readonly IDotNet dotnet;
protected readonly ICompilationInfoContainer compilationInfoContainer;
protected readonly IEnumerable references;
public DotnetSourceGeneratorBase(
- FileProvider fileProvider,
+ IFileProvider fileProvider,
FileContent fileContent,
IDotNet dotnet,
ICompilationInfoContainer compilationInfoContainer,
diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/SourceGenerators/DotnetSourceGeneratorWrapper/DotnetSourceGeneratorWrapper.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/SourceGenerators/DotnetSourceGeneratorWrapper/DotnetSourceGeneratorWrapper.cs
index 9518afac4008..8ecf13d53a6a 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/SourceGenerators/DotnetSourceGeneratorWrapper/DotnetSourceGeneratorWrapper.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/SourceGenerators/DotnetSourceGeneratorWrapper/DotnetSourceGeneratorWrapper.cs
@@ -79,7 +79,7 @@ public IEnumerable RunSourceGenerator(IEnumerable additionalFile
sw.Write(argsString);
}
- dotnet.Exec($"\"{cscPath}\" /noconfig @\"{cscArgsPath}\"");
+ dotnet.Exec([cscPath, "/noconfig", $"@{cscArgsPath}"]);
var files = Directory.GetFiles(outputFolder, "*.*", new EnumerationOptions { RecurseSubdirectories = true });
diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/SourceGenerators/RazorGenerator.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/SourceGenerators/RazorGenerator.cs
index 3f9a17dc6b30..8d4536b4503c 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/SourceGenerators/RazorGenerator.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/SourceGenerators/RazorGenerator.cs
@@ -8,7 +8,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
internal class RazorGenerator : DotnetSourceGeneratorBase
{
public RazorGenerator(
- FileProvider fileProvider,
+ IFileProvider fileProvider,
FileContent fileContent,
IDotNet dotnet,
ICompilationInfoContainer compilationInfoContainer,
diff --git a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/SourceGenerators/ResxGenerator.cs b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/SourceGenerators/ResxGenerator.cs
index da66ef275442..e6e27ea27534 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/SourceGenerators/ResxGenerator.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp.DependencyFetching/SourceGenerators/ResxGenerator.cs
@@ -10,7 +10,7 @@ internal class ResxGenerator : DotnetSourceGeneratorBase
private readonly string? sourceGeneratorFolder = null;
public ResxGenerator(
- FileProvider fileProvider,
+ IFileProvider fileProvider,
FileContent fileContent,
IDotNet dotnet,
ICompilationInfoContainer compilationInfoContainer,
diff --git a/csharp/extractor/Semmle.Extraction.Tests/DependabotProxy.cs b/csharp/extractor/Semmle.Extraction.Tests/DependabotProxy.cs
new file mode 100644
index 000000000000..9c8c762f5989
--- /dev/null
+++ b/csharp/extractor/Semmle.Extraction.Tests/DependabotProxy.cs
@@ -0,0 +1,185 @@
+using Xunit;
+using System;
+using System.IO;
+using Semmle.Extraction.CSharp.DependencyFetching;
+using Semmle.Util;
+
+namespace Semmle.Extraction.Tests
+{
+ public class DependabotConfigurationStub : IDependabotProxyConfiguration
+ {
+ public string? Host { get; set; }
+ public string? Port { get; set; }
+ public string? Certificate { get; set; }
+ public string? RegistryURLs { get; set; }
+ }
+
+ public class DiagnosticsWriterStub : IDiagnosticsWriter
+ {
+ public void AddEntry(Semmle.Util.DiagnosticMessage entry) { }
+ public void Dispose() { }
+ }
+
+ public class DependabotProxyTests
+ {
+ private static TemporaryDirectory MakeTemporaryDirectory()
+ {
+ var tmp = Path.Join(Path.GetTempPath(), "DependabotProxyTests", Guid.NewGuid().ToString());
+ return new TemporaryDirectory(tmp, "testing", new LoggerStub());
+ }
+
+ [Fact]
+ public void TestDependabotProxyCreation1()
+ {
+ // Setup
+ var config = new DependabotConfigurationStub
+ {
+ Host = "localhost",
+ Port = "",
+ };
+
+ // Execute
+ using var tempWorkingDirectory = MakeTemporaryDirectory();
+ using var proxy = DependabotProxy.Make(config, new LoggerStub(), new DiagnosticsWriterStub(), tempWorkingDirectory);
+
+ // Verify
+ Assert.Null(proxy);
+ }
+
+ [Fact]
+ public void TestDependabotProxyCreation2()
+ {
+ // Setup
+ var config = new DependabotConfigurationStub
+ {
+ Port = "8080",
+ };
+
+ // Execute
+ using var tempWorkingDirectory = MakeTemporaryDirectory();
+ using var proxy = DependabotProxy.Make(config, new LoggerStub(), new DiagnosticsWriterStub(), tempWorkingDirectory);
+
+ // Verify
+ Assert.Null(proxy);
+ }
+
+ private const string ExampleCertificate = """
+ -----BEGIN CERTIFICATE-----
+ MIIFJTCCAw2gAwIBAgIUDImU6YnuAqJ1QuRp+OpJQPnPu6wwDQYJKoZIhvcNAQEL
+ BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI2MDkwMTEyMjUzMVoXDTI3MDkw
+ MTEyMjUzMVowFDESMBAGA1UEAwwJbG9jYWxob3N0MIICIjANBgkqhkiG9w0BAQEF
+ AAOCAg8AMIICCgKCAgEAnlp7yQ1VuocMwIZWlCle3bEM86+1ED6BFfPFpIrRhfUT
+ c+5IvPng8TIZPO4mROp5G9YDZfOtXW2bwktyZNUhsBcxqUT1lmXit21vc5W9Gxx5
+ 4G8nyF4/FcjFkmxkZifxiUCdBceDcE7+kx2itq/a7gLPlyTzvz5etu1nHEC3Jg/y
+ TVhAwdwysgAo9WymFCczDa2ga6nOPBOaxwLnoPl9041KSu5oIo9QC0Im+US1R18Q
+ /mXa+wkmjf+bYAkE/pZie8z8Q7h9yppTngGzkoDEebFYyaMr8MXlFdWS8f/eMwSp
+ iMFSsmlCqgUbA672APxzOcuSMMYrblzGkvZp23qbNjwQuQKlgAYBTSGltLv4U8JF
+ ePNcgDCY6RG55rNvF1gk1L2h25jcw1LX6fSvQGCOkzNmP03AhqZBUigO1Zt0zLwi
+ K4m0bH7nPLJFEN6tI3tybyZeC2RVyiSHvOkgx35Qj8RQ3XMVkImJNBYOMc2MkmMZ
+ ux6XMiHqXCON4zaWuWSovciZeMAQAspCrzVDLH6p2DWEfw/zDfQNU3iLk21sZGei
+ 0GKzs8zrxUcqOU9V4Cnm+7JJ6eqS72f1+wX0ROb3djC6KgCE/NaHqo4apiI3K+CH
+ T0rVRsJIHyT39YO1c1I1vhAKRSH5kQVe3qRfIT/AuaDLQY6WqGPzrOkem78sjtsC
+ AwEAAaNvMG0wHQYDVR0OBBYEFK0DP5MD6mhEcdcm346uwoPL2NFGMB8GA1UdIwQY
+ MBaAFK0DP5MD6mhEcdcm346uwoPL2NFGMA8GA1UdEwEB/wQFMAMBAf8wGgYDVR0R
+ BBMwEYIJbG9jYWxob3N0hwR/AAABMA0GCSqGSIb3DQEBCwUAA4ICAQCc5u8qNHHG
+ kONjfvq7Denq6QaEt4dZZDDODAvgUzZnnBjEhgrp7zfxtbyU/I0+DWnKQMKA9wPM
+ ktiFGd0lldEqoT+E7b0kN124lBGqZ/uYkhsWZ0Nc5dD+UB9oJszwOc5KNuquOnr6
+ SbsfXVm4yLvVLXl67c0jvqvRgGg9/6Q6eMzohW6abMdbYhS28/DsJhCea/dV3+L1
+ oVJ3O/A8e86m174ZCGE8s9UtnVYylBkAryDqaaQLdOBQ2C7uxdRAUNHSIa2JlqUc
+ 5+cod8lFojKb74hbgj6wkXyajsFttqYMh7CeASsnjZXDQ4MC3DqqDVCZuNvJ85Rt
+ ya3Tljp4Ln2AAAoKC3REUeU8PQqpk1vVIj0FSr3RvBTvwzyNfWFVqyBiXTATuV9n
+ 6AemqqXo5MZrHHeRaSTF8A70Jxbt9yx75xQxp3O3tdEL1Mxbl9X7c/hizOfLbeHH
+ IkAgzALQgi87Zbf2tOhRwH5NrB4ijyUUfovRHUwzsZOoTNqlVeNzbDRVbegx9V99
+ /3vwNZgpStGl/JYhN9qY5hJKnC64ltMvuNGpLeJCGyFkrtFS8gKkgR7VKrGo7h3+
+ Zo8rz8TFjP7RmSgQbrmFuPqNOGXzPidu2sMMFacKV7Rn4bEtHzW3MDhqVD4w/pGD
+ L0xpnWjzLYltVjz8mo07yh+zQ10G71Cl1w==
+ -----END CERTIFICATE-----
+ """;
+
+ [Fact]
+ public void TestDependabotProxyCertificate()
+ {
+ // Setup
+ var config = new DependabotConfigurationStub
+ {
+ Port = "8080",
+ Host = "localhost",
+ Certificate = ExampleCertificate
+ };
+
+ // Execute
+ using var tempWorkingDirectory = MakeTemporaryDirectory();
+ using var proxy = DependabotProxy.Make(config, new LoggerStub(), new DiagnosticsWriterStub(), tempWorkingDirectory);
+
+ // Verify
+ Assert.NotNull(proxy);
+ Assert.Equal("http://localhost:8080", proxy.Address);
+ Assert.NotNull(proxy.Certificate);
+ Assert.NotNull(proxy.CertificatePath);
+ }
+
+ [Fact]
+ public void TestDependabotRegistryUrls1()
+ {
+ // Setup
+ var config = new DependabotConfigurationStub
+ {
+ Port = "8080",
+ Host = "localhost",
+ RegistryURLs = "Doesn't parse as a JSON list"
+ };
+
+ // Execute
+ using var tempWorkingDirectory = MakeTemporaryDirectory();
+ using var proxy = DependabotProxy.Make(config, new LoggerStub(), new DiagnosticsWriterStub(), tempWorkingDirectory);
+
+ // Verify
+ Assert.NotNull(proxy);
+ Assert.Equal([], proxy.RegistryURLs);
+ }
+
+ [Fact]
+ public void TestDependabotRegistryUrls2()
+ {
+ // Setup
+ var config = new DependabotConfigurationStub
+ {
+ Port = "8080",
+ Host = "localhost",
+ RegistryURLs = "[ { \"type\": \"nuget_feed\", \"url\": \"https://nuget.pkg.github.com/org/index.json\" } ]"
+ };
+
+ // Execute
+ using var tempWorkingDirectory = MakeTemporaryDirectory();
+ using var proxy = DependabotProxy.Make(config, new LoggerStub(), new DiagnosticsWriterStub(), tempWorkingDirectory);
+
+ // Verify
+ Assert.NotNull(proxy);
+ Assert.Equal([
+ "https://nuget.pkg.github.com/org/index.json"
+ ], proxy.RegistryURLs);
+ }
+
+ [Fact]
+ public void TestDependabotRegistryUrls3()
+ {
+ // Setup
+ var config = new DependabotConfigurationStub
+ {
+ Port = "8080",
+ Host = "localhost",
+ RegistryURLs = "[ { \"type\": \"nuget_feed\", \"url\": \"https://example.com/org/index.json\" }, { \"type\": \"wrong_type\", \"url\": \"https://nuget.pkg.github.com/org/index.json\" } ]"
+ };
+
+ // Execute
+ using var tempWorkingDirectory = MakeTemporaryDirectory();
+ using var proxy = DependabotProxy.Make(config, new LoggerStub(), new DiagnosticsWriterStub(), tempWorkingDirectory);
+
+ // Verify
+ Assert.NotNull(proxy);
+ Assert.Equal([
+ "https://example.com/org/index.json"
+ ], proxy.RegistryURLs);
+ }
+ }
+}
diff --git a/csharp/extractor/Semmle.Extraction.Tests/DotNet.cs b/csharp/extractor/Semmle.Extraction.Tests/DotNet.cs
index 74939b61af5e..77e88a58443a 100644
--- a/csharp/extractor/Semmle.Extraction.Tests/DotNet.cs
+++ b/csharp/extractor/Semmle.Extraction.Tests/DotNet.cs
@@ -8,45 +8,45 @@ namespace Semmle.Extraction.Tests
{
internal class DotNetCliInvokerStub : IDotNetCliInvoker
{
- private readonly IList output;
- private string lastArgs = "";
+ private readonly IList returnOutput;
+ private List lastArgs = [];
public string WorkingDirectory { get; private set; } = "";
public bool Success { get; set; } = true;
public int ExitCode { get; set; } = 0;
- public DotNetCliInvokerStub(IList output)
+ public DotNetCliInvokerStub(IList returnOutput)
{
- this.output = output;
+ this.returnOutput = returnOutput;
}
public string Exec => "dotnet";
- public bool RunCommand(string args, bool silent)
+ public bool RunCommand(List args, bool silent)
{
lastArgs = args;
return Success;
}
- public int RunCommandExitCode(string args, bool silent)
+ public int RunCommandExitCode(List args, bool silent)
{
lastArgs = args;
return ExitCode;
}
- public bool RunCommand(string args, out IList output, bool silent)
+ public bool RunCommand(List args, out IList output, bool silent)
{
lastArgs = args;
- output = this.output;
+ output = this.returnOutput;
return Success;
}
- public bool RunCommand(string args, string? workingDirectory, out IList output, bool silent)
+ public bool RunCommand(List args, string? workingDirectory, out IList output, bool silent)
{
WorkingDirectory = workingDirectory ?? "";
return RunCommand(args, out output, silent);
}
- public string GetLastArgs() => lastArgs;
+ public List GetLastArgs() => lastArgs;
}
public class DotNetTests
@@ -83,7 +83,7 @@ public void TestDotnetInfo()
// Verify
var lastArgs = dotnetCliInvoker.GetLastArgs();
- Assert.Equal("--info", lastArgs);
+ Assert.Equal(["--info"], lastArgs);
}
[Fact]
@@ -115,11 +115,11 @@ public void TestDotnetRestoreProjectToDirectory1()
var dotnet = MakeDotnet(dotnetCliInvoker);
// Execute
- dotnet.Restore(new("myproject.csproj", "mypackages", false));
+ dotnet.Restore(new("myproject.csproj", "mypackages", false, []));
// Verify
var lastArgs = dotnetCliInvoker.GetLastArgs();
- Assert.Equal("restore --no-dependencies \"myproject.csproj\" --packages \"mypackages\" /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal", lastArgs);
+ Assert.Equal(["restore", "--no-dependencies", "myproject.csproj", "--packages", "mypackages", "/p:DisableImplicitNuGetFallbackFolder=true", "--verbosity", "normal"], lastArgs);
}
[Fact]
@@ -130,11 +130,11 @@ public void TestDotnetRestoreProjectToDirectory2()
var dotnet = MakeDotnet(dotnetCliInvoker);
// Execute
- var res = dotnet.Restore(new("myproject.csproj", "mypackages", false, null));
+ var res = dotnet.Restore(new("myproject.csproj", "mypackages", false, []));
// Verify
var lastArgs = dotnetCliInvoker.GetLastArgs();
- Assert.Equal("restore --no-dependencies \"myproject.csproj\" --packages \"mypackages\" /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal", lastArgs);
+ Assert.Equal(["restore", "--no-dependencies", "myproject.csproj", "--packages", "mypackages", "/p:DisableImplicitNuGetFallbackFolder=true", "--verbosity", "normal"], lastArgs);
Assert.Equal(2, res.AssetsFilePaths.Count());
Assert.Contains("/path/to/project.assets.json", res.AssetsFilePaths);
Assert.Contains("/path/to/project2.assets.json", res.AssetsFilePaths);
@@ -148,11 +148,11 @@ public void TestDotnetRestoreProjectToDirectory3()
var dotnet = MakeDotnet(dotnetCliInvoker);
// Execute
- var res = dotnet.Restore(new("myproject.csproj", "mypackages", false, null, true));
+ var res = dotnet.Restore(new("myproject.csproj", "mypackages", false, ["https://my.nuget.source1"], true));
// Verify
var lastArgs = dotnetCliInvoker.GetLastArgs();
- Assert.Equal("restore --no-dependencies \"myproject.csproj\" --packages \"mypackages\" /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal --force", lastArgs);
+ Assert.Equal(["restore", "--no-dependencies", "myproject.csproj", "--packages", "mypackages", "/p:DisableImplicitNuGetFallbackFolder=true", "--verbosity", "normal", "--force", "-s", "https://my.nuget.source1"], lastArgs);
Assert.Equal(2, res.AssetsFilePaths.Count());
Assert.Contains("/path/to/project.assets.json", res.AssetsFilePaths);
Assert.Contains("/path/to/project2.assets.json", res.AssetsFilePaths);
@@ -166,11 +166,11 @@ public void TestDotnetRestoreSolutionToDirectory1()
var dotnet = MakeDotnet(dotnetCliInvoker);
// Execute
- var res = dotnet.Restore(new("mysolution.sln", "mypackages", false));
+ var res = dotnet.Restore(new("mysolution.sln", "mypackages", false, ["https://my.nuget.source1", "https://my.nuget.source2"]));
// Verify
var lastArgs = dotnetCliInvoker.GetLastArgs();
- Assert.Equal("restore --no-dependencies \"mysolution.sln\" --packages \"mypackages\" /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal", lastArgs);
+ Assert.Equal(["restore", "--no-dependencies", "mysolution.sln", "--packages", "mypackages", "/p:DisableImplicitNuGetFallbackFolder=true", "--verbosity", "normal", "-s", "https://my.nuget.source1", "-s", "https://my.nuget.source2"], lastArgs);
Assert.Equal(2, res.RestoredProjects.Count());
Assert.Contains("/path/to/project.csproj", res.RestoredProjects);
Assert.Contains("/path/to/project2.csproj", res.RestoredProjects);
@@ -188,11 +188,11 @@ public void TestDotnetRestoreSolutionToDirectory2()
dotnetCliInvoker.Success = false;
// Execute
- var res = dotnet.Restore(new("mysolution.sln", "mypackages", false));
+ var res = dotnet.Restore(new("mysolution.sln", "mypackages", false, []));
// Verify
var lastArgs = dotnetCliInvoker.GetLastArgs();
- Assert.Equal("restore --no-dependencies \"mysolution.sln\" --packages \"mypackages\" /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal", lastArgs);
+ Assert.Equal(["restore", "--no-dependencies", "mysolution.sln", "--packages", "mypackages", "/p:DisableImplicitNuGetFallbackFolder=true", "--verbosity", "normal"], lastArgs);
Assert.Empty(res.RestoredProjects);
Assert.Empty(res.AssetsFilePaths);
}
@@ -209,7 +209,7 @@ public void TestDotnetNew()
// Verify
var lastArgs = dotnetCliInvoker.GetLastArgs();
- Assert.Equal("new console --no-restore --output \"myfolder\"", lastArgs);
+ Assert.Equal(["new", "console", "--no-restore", "--output", "myfolder"], lastArgs);
}
[Fact]
@@ -224,7 +224,7 @@ public void TestDotnetAddPackage()
// Verify
var lastArgs = dotnetCliInvoker.GetLastArgs();
- Assert.Equal("add \"myfolder\" package \"mypackage\" --no-restore", lastArgs);
+ Assert.Equal(["add", "myfolder", "package", "mypackage", "--no-restore"], lastArgs);
}
[Fact]
@@ -239,7 +239,7 @@ public void TestDotnetGetListedRuntimes1()
// Verify
var lastArgs = dotnetCliInvoker.GetLastArgs();
- Assert.Equal("--list-runtimes", lastArgs);
+ Assert.Equal(["--list-runtimes"], lastArgs);
Assert.Equal(2, runtimes.Count);
Assert.Contains("Microsoft.AspNetCore.App 7.0.2 [/path/dotnet/shared/Microsoft.AspNetCore.App]", runtimes);
Assert.Contains("Microsoft.NETCore.App 7.0.2 [/path/dotnet/shared/Microsoft.NETCore.App]", runtimes);
@@ -258,7 +258,7 @@ public void TestDotnetGetListedRuntimes2()
// Verify
var lastArgs = dotnetCliInvoker.GetLastArgs();
- Assert.Equal("--list-runtimes", lastArgs);
+ Assert.Equal(["--list-runtimes"], lastArgs);
Assert.Empty(runtimes);
}
@@ -270,11 +270,11 @@ public void TestDotnetExec()
var dotnet = MakeDotnet(dotnetCliInvoker);
// Execute
- dotnet.Exec("myarg1 myarg2");
+ dotnet.Exec(["myarg1", "myarg2"]);
// Verify
var lastArgs = dotnetCliInvoker.GetLastArgs();
- Assert.Equal("exec myarg1 myarg2", lastArgs);
+ Assert.Equal(["exec", "myarg1", "myarg2"], lastArgs);
}
[Fact]
@@ -289,7 +289,7 @@ public void TestNugetFeeds()
// Verify
var lastArgs = dotnetCliInvoker.GetLastArgs();
- Assert.Equal("nuget list source --format Short --configfile \"abc\"", lastArgs);
+ Assert.Equal(["nuget", "list", "source", "--format", "Short", "--configfile", "abc"], lastArgs);
}
[Fact]
@@ -304,7 +304,7 @@ public void TestNugetFeedsFromFolder()
// Verify
var lastArgs = dotnetCliInvoker.GetLastArgs();
- Assert.Equal("nuget list source --format Short", lastArgs);
+ Assert.Equal(["nuget", "list", "source", "--format", "Short"], lastArgs);
Assert.Equal("abc", dotnetCliInvoker.WorkingDirectory);
}
}
diff --git a/csharp/extractor/Semmle.Extraction.Tests/DotNetStub.cs b/csharp/extractor/Semmle.Extraction.Tests/DotNetStub.cs
new file mode 100644
index 000000000000..119e39fd0974
--- /dev/null
+++ b/csharp/extractor/Semmle.Extraction.Tests/DotNetStub.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using Semmle.Extraction.CSharp.DependencyFetching;
+
+namespace Semmle.Extraction.Tests
+{
+ internal class DotNetStub : IDotNet
+ {
+ private readonly IList runtimes;
+ private readonly IList sdks;
+ private readonly IList nugetFeedsFromConfig;
+ private readonly IList nugetFeedsFromFolder;
+
+ public DotNetStub(IList runtimes, IList sdks, IList nugetFeedsFromConfig, IList nugetFeedsFromFolder)
+ {
+ this.runtimes = runtimes;
+ this.sdks = sdks;
+ this.nugetFeedsFromConfig = nugetFeedsFromConfig;
+ this.nugetFeedsFromFolder = nugetFeedsFromFolder;
+ }
+ public bool AddPackage(string folder, string package) => true;
+
+ public bool New(string folder) => true;
+
+ public RestoreResult Restore(RestoreSettings restoreSettings) => new(true, Array.Empty());
+
+ public IList GetListedRuntimes() => runtimes;
+
+ public IList GetListedSdks() => sdks;
+
+ public bool Exec(List execArgs) => true;
+
+ public IList GetNugetFeeds(string nugetConfig) => nugetFeedsFromConfig;
+
+ public IList GetNugetFeedsFromFolder(string folderPath) => nugetFeedsFromFolder;
+ }
+}
diff --git a/csharp/extractor/Semmle.Extraction.Tests/FeedManager.cs b/csharp/extractor/Semmle.Extraction.Tests/FeedManager.cs
new file mode 100644
index 000000000000..f70efdb4cdcc
--- /dev/null
+++ b/csharp/extractor/Semmle.Extraction.Tests/FeedManager.cs
@@ -0,0 +1,187 @@
+using Xunit;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Semmle.Extraction.CSharp.DependencyFetching;
+
+namespace Semmle.Extraction.Tests
+{
+ public class DependabotProxyStub : IDependabotProxy
+ {
+ public string Address { get; } = "";
+ public HashSet RegistryURLs { get; } = ["https://example.com/registry1", "https://example.com/registry2"];
+ public string? CertificatePath { get; } = null;
+ public System.Security.Cryptography.X509Certificates.X509Certificate2? Certificate { get; } = null;
+
+ public void Dispose() { }
+ }
+
+ public class FeedManagerIOStub : IFeedManagerIO
+ {
+ private readonly List unreachableFeeds;
+
+ public FeedManagerIOStub(List unreachableFeeds)
+ {
+ this.unreachableFeeds = unreachableFeeds;
+ }
+
+ public string? GetDirectoryName(string path)
+ {
+ return "/path/to/folder";
+ }
+
+ public bool IsFeedReachable(string feed, int timeoutMilliSeconds, int tryCount)
+ {
+ return !unreachableFeeds.Contains(feed);
+ }
+ }
+
+ public class FileProviderStub : IFileProvider
+ {
+ public DirectoryInfo SourceDir { get; } = new DirectoryInfo("/path/to/source");
+ public IEnumerable SmallNonBinary { get; } = Enumerable.Empty();
+ public IEnumerable Sources { get; } = Enumerable.Empty();
+ public ICollection Projects { get; } = new List();
+ public ICollection Solutions { get; } = new List();
+ public IEnumerable Dlls { get; } = Enumerable.Empty();
+ public ICollection NugetConfigs { get; } = ["/path/to/nuget.config"];
+ public ICollection NugetExes { get; } = new List();
+ public string? RootNugetConfig { get; } = null;
+ public IEnumerable GlobalJsons { get; } = Enumerable.Empty();
+ public ICollection PackagesConfigs { get; } = new List();
+ public ICollection RazorViews { get; } = new List();
+ public ICollection Resources { get; } = new List();
+ }
+
+ public class FeedManagerTests
+ {
+ private static FeedManager MakeFeedManager()
+ {
+ var logger = new LoggerStub();
+ var dotnet = new DotNetStub([], [], ["E https://feed.from/config"], ["E https://feed.from/folder1", "E https://feed.from/folder2", "D https://feed.from/folder3"]);
+ var dependabotProxy = new DependabotProxyStub();
+ var fileProvider = new FileProviderStub();
+ var feedManagerIo = new FeedManagerIOStub(["https://example.com/registry1", "https://feed.from/folder2"]);
+ return new FeedManager(logger, dotnet, dependabotProxy, fileProvider, feedManagerIo);
+ }
+
+ [Fact]
+ public void TestExplicitFeeds()
+ {
+ // Setup
+ var feedManager = MakeFeedManager();
+
+ // Execute
+ var actualFeeds = feedManager.ExplicitFeeds;
+
+ // Verify
+ Assert.Equal([
+ "https://example.com/registry1",
+ "https://example.com/registry2",
+ "https://feed.from/config"
+ ], actualFeeds);
+ }
+
+ [Fact]
+ public void TestInheritedFeeds()
+ {
+ // Setup
+ var feedManager = MakeFeedManager();
+
+ // Execute
+ var inherited = feedManager.InheritedFeeds;
+
+ // Verify
+ Assert.Equal([
+ "https://feed.from/folder1",
+ "https://feed.from/folder2"
+ ], inherited);
+ }
+
+ [Fact]
+ public void TestAllFeeds()
+ {
+ // Setup
+ var feedManager = MakeFeedManager();
+
+ // Execute
+ var all = feedManager.AllFeeds;
+
+ // Verify
+ Assert.Equal([
+ "https://example.com/registry1",
+ "https://example.com/registry2",
+ "https://feed.from/config",
+ "https://feed.from/folder1",
+ "https://feed.from/folder2"
+ ], all);
+ }
+
+ [Fact]
+ public void TestReachableFeeds()
+ {
+ // Setup
+ var feedManager = MakeFeedManager();
+
+ // Execute
+ var reachableFeeds = feedManager.ReachableFeeds;
+
+ // Verify
+ Assert.Equal([
+ "https://example.com/registry2",
+ "https://feed.from/config",
+ "https://feed.from/folder1"
+ ], reachableFeeds);
+ }
+
+ [Fact]
+ public void TestReachableExplicitFeeds()
+ {
+ // Setup
+ var feedManager = MakeFeedManager();
+
+ // Execute
+ var reachableFeeds = feedManager.ReachableExplicitFeeds;
+
+ // Verify
+ Assert.Equal([
+ "https://example.com/registry2",
+ "https://feed.from/config"
+ ], reachableFeeds);
+ }
+
+ [Fact]
+ public void TestReachableFallbackFeeds()
+ {
+ // Setup
+ var feedManager = MakeFeedManager();
+
+ // Execute
+ var reachableFallback = feedManager.ReachableFallbackFeeds;
+
+ // Verify
+ Assert.Equal([
+ "https://example.com/registry2",
+ "https://feed.from/config",
+ "https://api.nuget.org/v3/index.json"
+ ], reachableFallback);
+ }
+
+ [Fact]
+ public void TestFeedsToUse()
+ {
+ // Setup
+ var feedManager = MakeFeedManager();
+
+ // Execute
+ var feedsToUse = feedManager.FeedsToUse("/path/to/packages.config").ToHashSet();
+
+ // Verify
+ Assert.Equal([
+ "https://example.com/registry2",
+ "https://feed.from/folder1"
+ ], feedsToUse);
+ }
+ }
+}
diff --git a/csharp/extractor/Semmle.Extraction.Tests/Runtime.cs b/csharp/extractor/Semmle.Extraction.Tests/Runtime.cs
index 38101420fab2..ea2483f3faf3 100644
--- a/csharp/extractor/Semmle.Extraction.Tests/Runtime.cs
+++ b/csharp/extractor/Semmle.Extraction.Tests/Runtime.cs
@@ -5,37 +5,12 @@
namespace Semmle.Extraction.Tests
{
- internal class DotNetStub : IDotNet
- {
- private readonly IList runtimes;
- private readonly IList sdks;
-
- public DotNetStub(IList runtimes, IList sdks)
- {
- this.runtimes = runtimes;
- this.sdks = sdks;
- }
- public bool AddPackage(string folder, string package) => true;
-
- public bool New(string folder) => true;
-
- public RestoreResult Restore(RestoreSettings restoreSettings) => new(true, Array.Empty());
-
- public IList GetListedRuntimes() => runtimes;
-
- public IList GetListedSdks() => sdks;
-
- public bool Exec(string execArgs) => true;
-
- public IList GetNugetFeeds(string nugetConfig) => [];
-
- public IList GetNugetFeedsFromFolder(string folderPath) => [];
- }
-
public class RuntimeTests
{
private static string FixExpectedPathOnWindows(string path) => path.Replace('\\', '/');
+ private static DotNetStub MakeDotNetStub(IList listedRuntimes) => new DotNetStub(listedRuntimes, null!, [], []);
+
[Fact]
public void TestRuntime1()
{
@@ -50,7 +25,7 @@ public void TestRuntime1()
"Microsoft.NETCore.App 7.0.0 [/path/dotnet/shared/Microsoft.NETCore.App]",
"Microsoft.NETCore.App 7.0.2 [/path/dotnet/shared/Microsoft.NETCore.App]"
};
- var dotnet = new DotNetStub(listedRuntimes, null!);
+ var dotnet = MakeDotNetStub(listedRuntimes);
var runtime = new Runtime(dotnet);
// Execute
@@ -76,7 +51,7 @@ public void TestRuntime2()
"Microsoft.NETCore.App 8.0.0-preview.5.43280.8 [/path/dotnet/shared/Microsoft.NETCore.App]",
"Microsoft.NETCore.App 8.0.0-preview.5.23280.8 [/path/dotnet/shared/Microsoft.NETCore.App]"
};
- var dotnet = new DotNetStub(listedRuntimes, null!);
+ var dotnet = new DotNetStub(listedRuntimes, null!, [], []);
var runtime = new Runtime(dotnet);
// Execute
@@ -99,7 +74,7 @@ public void TestRuntime3()
"Microsoft.NETCore.App 8.0.0-rc.4.43280.8 [/path/dotnet/shared/Microsoft.NETCore.App]",
"Microsoft.NETCore.App 8.0.0-preview.5.23280.8 [/path/dotnet/shared/Microsoft.NETCore.App]"
};
- var dotnet = new DotNetStub(listedRuntimes, null!);
+ var dotnet = MakeDotNetStub(listedRuntimes);
var runtime = new Runtime(dotnet);
// Execute
@@ -128,7 +103,7 @@ public void TestRuntime4()
@"Microsoft.WindowsDesktop.App 6.0.20 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]",
@"Microsoft.WindowsDesktop.App 7.0.4 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]"
};
- var dotnet = new DotNetStub(listedRuntimes, null!);
+ var dotnet = MakeDotNetStub(listedRuntimes);
var runtime = new Runtime(dotnet);
// Execute
@@ -149,6 +124,7 @@ public class SdkTests
{
private static string FixExpectedPathOnWindows(string path) => path.Replace('\\', '/');
+ private static DotNetStub MakeDotNetStub(IList listedSdks) => new DotNetStub(null!, listedSdks, [], []);
[Fact]
public void TestSdk1()
{
@@ -163,7 +139,7 @@ public void TestSdk1()
"6.0.102 [/usr/local/share/dotnet/sdk6]",
"6.0.301 [/usr/local/share/dotnet/sdk7]",
};
- var dotnet = new DotNetStub(null!, listedSdks);
+ var dotnet = MakeDotNetStub(listedSdks);
var sdk = new Sdk(dotnet, new LoggerStub());
// Execute
@@ -185,7 +161,7 @@ public void TestSdk2()
"8.0.100-preview.7.23376.3 [/usr/local/share/dotnet/sdk3]",
"7.0.400 [/usr/local/share/dotnet/sdk4]",
};
- var dotnet = new DotNetStub(null!, listedSdks);
+ var dotnet = MakeDotNetStub(listedSdks);
var sdk = new Sdk(dotnet, new LoggerStub());
// Execute
diff --git a/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md b/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md
index 1897f105e373..8d3c16f6bc95 100644
--- a/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md
+++ b/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 1.7.74
+
+No user-facing changes.
+
## 1.7.73
No user-facing changes.
diff --git a/csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.7.74.md b/csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.7.74.md
new file mode 100644
index 000000000000..3af579633adc
--- /dev/null
+++ b/csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.7.74.md
@@ -0,0 +1,3 @@
+## 1.7.74
+
+No user-facing changes.
diff --git a/csharp/ql/campaigns/Solorigate/lib/codeql-pack.release.yml b/csharp/ql/campaigns/Solorigate/lib/codeql-pack.release.yml
index 95fecc284bee..34061dc1c22a 100644
--- a/csharp/ql/campaigns/Solorigate/lib/codeql-pack.release.yml
+++ b/csharp/ql/campaigns/Solorigate/lib/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 1.7.73
+lastReleaseVersion: 1.7.74
diff --git a/csharp/ql/campaigns/Solorigate/lib/qlpack.yml b/csharp/ql/campaigns/Solorigate/lib/qlpack.yml
index d045c1c7224b..07c306a65a9d 100644
--- a/csharp/ql/campaigns/Solorigate/lib/qlpack.yml
+++ b/csharp/ql/campaigns/Solorigate/lib/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/csharp-solorigate-all
-version: 1.7.74-dev
+version: 1.7.75-dev
groups:
- csharp
- solorigate
diff --git a/csharp/ql/campaigns/Solorigate/src/CHANGELOG.md b/csharp/ql/campaigns/Solorigate/src/CHANGELOG.md
index 1897f105e373..8d3c16f6bc95 100644
--- a/csharp/ql/campaigns/Solorigate/src/CHANGELOG.md
+++ b/csharp/ql/campaigns/Solorigate/src/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 1.7.74
+
+No user-facing changes.
+
## 1.7.73
No user-facing changes.
diff --git a/csharp/ql/campaigns/Solorigate/src/change-notes/released/1.7.74.md b/csharp/ql/campaigns/Solorigate/src/change-notes/released/1.7.74.md
new file mode 100644
index 000000000000..3af579633adc
--- /dev/null
+++ b/csharp/ql/campaigns/Solorigate/src/change-notes/released/1.7.74.md
@@ -0,0 +1,3 @@
+## 1.7.74
+
+No user-facing changes.
diff --git a/csharp/ql/campaigns/Solorigate/src/codeql-pack.release.yml b/csharp/ql/campaigns/Solorigate/src/codeql-pack.release.yml
index 95fecc284bee..34061dc1c22a 100644
--- a/csharp/ql/campaigns/Solorigate/src/codeql-pack.release.yml
+++ b/csharp/ql/campaigns/Solorigate/src/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 1.7.73
+lastReleaseVersion: 1.7.74
diff --git a/csharp/ql/campaigns/Solorigate/src/qlpack.yml b/csharp/ql/campaigns/Solorigate/src/qlpack.yml
index 3b5e1b3341e3..94bf9c0f7db3 100644
--- a/csharp/ql/campaigns/Solorigate/src/qlpack.yml
+++ b/csharp/ql/campaigns/Solorigate/src/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/csharp-solorigate-queries
-version: 1.7.74-dev
+version: 1.7.75-dev
groups:
- csharp
- solorigate
diff --git a/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/AttributeWebApp/AttributePocoController.cs b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/AttributeWebApp/AttributePocoController.cs
new file mode 100644
index 000000000000..f4427cc21c48
--- /dev/null
+++ b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/AttributeWebApp/AttributePocoController.cs
@@ -0,0 +1,8 @@
+using Microsoft.AspNetCore.Mvc;
+
+[Route("api/attribute")]
+public class AttributePocoController
+{
+ [HttpGet]
+ public void Action(string input) => _ = GetType().Name + input;
+}
\ No newline at end of file
diff --git a/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/AttributeWebApp/AttributeWebApp.csproj b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/AttributeWebApp/AttributeWebApp.csproj
new file mode 100644
index 000000000000..bddc74e0c784
--- /dev/null
+++ b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/AttributeWebApp/AttributeWebApp.csproj
@@ -0,0 +1,5 @@
+
+
+ net10.0
+
+
\ No newline at end of file
diff --git a/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/AttributeWebApp/ConventionOnlyController.cs b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/AttributeWebApp/ConventionOnlyController.cs
new file mode 100644
index 000000000000..f1222ad5f181
--- /dev/null
+++ b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/AttributeWebApp/ConventionOnlyController.cs
@@ -0,0 +1,4 @@
+public class ConventionOnlyController
+{
+ public void Action(string input) => _ = GetType().Name + input;
+}
\ No newline at end of file
diff --git a/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/AttributeWebApp/Program.cs b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/AttributeWebApp/Program.cs
new file mode 100644
index 000000000000..8d74291b16a8
--- /dev/null
+++ b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/AttributeWebApp/Program.cs
@@ -0,0 +1,9 @@
+using Microsoft.AspNetCore.Builder;
+using Microsoft.Extensions.DependencyInjection;
+
+var builder = WebApplication.CreateBuilder(args);
+builder.Services.AddControllers();
+
+var app = builder.Build();
+app.MapControllers();
+app.Run();
\ No newline at end of file
diff --git a/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/Controllers.expected b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/Controllers.expected
new file mode 100644
index 000000000000..d6ee73a2694f
--- /dev/null
+++ b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/Controllers.expected
@@ -0,0 +1,7 @@
+| AdminAreaFallbackController.cs | AreaFallbackController |
+| AttributePocoController.cs | AttributePocoController |
+| FallbackOnlyController.cs | FallbackOnlyController |
+| GeneratedController.cs | GeneratedController |
+| IncludedController.cs | IncludedController |
+| StructuralController.cs | StructuralController |
+| WebPocoController.cs | WebPocoController |
diff --git a/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/Controllers.ql b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/Controllers.ql
new file mode 100644
index 000000000000..325f55434f5f
--- /dev/null
+++ b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/Controllers.ql
@@ -0,0 +1,6 @@
+import csharp
+import semmle.code.csharp.frameworks.microsoft.AspNetCore
+
+from MicrosoftAspNetCoreMvcController controller
+where controller.fromSource()
+select controller.getFile().getBaseName(), controller.getName()
diff --git a/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/GeneratedControllers/GeneratedController.cs b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/GeneratedControllers/GeneratedController.cs
new file mode 100644
index 000000000000..0fe26a40d27b
--- /dev/null
+++ b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/GeneratedControllers/GeneratedController.cs
@@ -0,0 +1,6 @@
+namespace GeneratedControllers;
+
+public class GeneratedController
+{
+ public void Action(string input) => _ = GetType().Name + input;
+}
\ No newline at end of file
diff --git a/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/GeneratedControllers/GeneratedControllers.csproj b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/GeneratedControllers/GeneratedControllers.csproj
new file mode 100644
index 000000000000..10f1ac4e07e4
--- /dev/null
+++ b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/GeneratedControllers/GeneratedControllers.csproj
@@ -0,0 +1,5 @@
+
+
+ net10.0
+
+
\ No newline at end of file
diff --git a/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/IncludedControllers/IncludedController.cs b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/IncludedControllers/IncludedController.cs
new file mode 100644
index 000000000000..04704dd9b064
--- /dev/null
+++ b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/IncludedControllers/IncludedController.cs
@@ -0,0 +1,6 @@
+namespace IncludedControllers;
+
+public class IncludedController
+{
+ public void Action(string input) => _ = GetType().Name + input;
+}
\ No newline at end of file
diff --git a/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/IncludedControllers/IncludedControllers.csproj b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/IncludedControllers/IncludedControllers.csproj
new file mode 100644
index 000000000000..10f1ac4e07e4
--- /dev/null
+++ b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/IncludedControllers/IncludedControllers.csproj
@@ -0,0 +1,5 @@
+
+
+ net10.0
+
+
\ No newline at end of file
diff --git a/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/UnmappedWebApp/AdminAreaFallbackController.cs b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/UnmappedWebApp/AdminAreaFallbackController.cs
new file mode 100644
index 000000000000..c64aea73ddd1
--- /dev/null
+++ b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/UnmappedWebApp/AdminAreaFallbackController.cs
@@ -0,0 +1,9 @@
+using Microsoft.AspNetCore.Mvc;
+
+namespace AdminArea;
+
+[Area("Admin")]
+public class AreaFallbackController
+{
+ public void Index(string input) => _ = GetType().Name + input;
+}
diff --git a/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/UnmappedWebApp/FallbackOnlyController.cs b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/UnmappedWebApp/FallbackOnlyController.cs
new file mode 100644
index 000000000000..1716d49f2296
--- /dev/null
+++ b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/UnmappedWebApp/FallbackOnlyController.cs
@@ -0,0 +1,4 @@
+public class FallbackOnlyController
+{
+ public void Index(string input) => _ = GetType().Name + input;
+}
diff --git a/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/UnmappedWebApp/OtherAreaFallbackController.cs b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/UnmappedWebApp/OtherAreaFallbackController.cs
new file mode 100644
index 000000000000..10849896f531
--- /dev/null
+++ b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/UnmappedWebApp/OtherAreaFallbackController.cs
@@ -0,0 +1,9 @@
+using Microsoft.AspNetCore.Mvc;
+
+namespace OtherArea;
+
+[Area("Other")]
+public class AreaFallbackController
+{
+ public void Index(string input) => _ = GetType().Name + input;
+}
diff --git a/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/UnmappedWebApp/Program.cs b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/UnmappedWebApp/Program.cs
new file mode 100644
index 000000000000..bb27696d84b5
--- /dev/null
+++ b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/UnmappedWebApp/Program.cs
@@ -0,0 +1,10 @@
+using Microsoft.AspNetCore.Builder;
+using Microsoft.Extensions.DependencyInjection;
+
+var builder = WebApplication.CreateBuilder(args);
+builder.Services.AddControllers();
+
+var app = builder.Build();
+app.MapFallbackToController("Index", "FallbackOnly");
+app.MapFallbackToAreaController("admin/{*path:nonfile}", "Index", "AreaFallback", "Admin");
+app.Run();
diff --git a/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/UnmappedWebApp/StructuralController.cs b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/UnmappedWebApp/StructuralController.cs
new file mode 100644
index 000000000000..dd9e5e2534b5
--- /dev/null
+++ b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/UnmappedWebApp/StructuralController.cs
@@ -0,0 +1,6 @@
+using Microsoft.AspNetCore.Mvc;
+
+public class StructuralController : ControllerBase
+{
+ public void Action(string input) => _ = GetType().Name + input;
+}
\ No newline at end of file
diff --git a/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/UnmappedWebApp/UnmappedPocoController.cs b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/UnmappedWebApp/UnmappedPocoController.cs
new file mode 100644
index 000000000000..99514ceb2c3f
--- /dev/null
+++ b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/UnmappedWebApp/UnmappedPocoController.cs
@@ -0,0 +1,4 @@
+public class UnmappedPocoController
+{
+ public void Action(string input) => _ = GetType().Name + input;
+}
\ No newline at end of file
diff --git a/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/UnmappedWebApp/UnmappedWebApp.csproj b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/UnmappedWebApp/UnmappedWebApp.csproj
new file mode 100644
index 000000000000..bddc74e0c784
--- /dev/null
+++ b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/UnmappedWebApp/UnmappedWebApp.csproj
@@ -0,0 +1,5 @@
+
+
+ net10.0
+
+
\ No newline at end of file
diff --git a/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/Unrelated/ThrottlingController.cs b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/Unrelated/ThrottlingController.cs
new file mode 100644
index 000000000000..d4277061e9fa
--- /dev/null
+++ b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/Unrelated/ThrottlingController.cs
@@ -0,0 +1,4 @@
+public class ThrottlingController
+{
+ public void Initialize(string input) => _ = GetType().Name + input;
+}
\ No newline at end of file
diff --git a/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/Unrelated/Unrelated.csproj b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/Unrelated/Unrelated.csproj
new file mode 100644
index 000000000000..10f1ac4e07e4
--- /dev/null
+++ b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/Unrelated/Unrelated.csproj
@@ -0,0 +1,5 @@
+
+
+ net10.0
+
+
\ No newline at end of file
diff --git a/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/WebApp/ApplicationParts.cs b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/WebApp/ApplicationParts.cs
new file mode 100644
index 000000000000..a7622ef5f9be
--- /dev/null
+++ b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/WebApp/ApplicationParts.cs
@@ -0,0 +1,3 @@
+using Microsoft.AspNetCore.Mvc.ApplicationParts;
+
+[assembly: ApplicationPart("GeneratedControllers")]
\ No newline at end of file
diff --git a/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/WebApp/Program.cs b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/WebApp/Program.cs
new file mode 100644
index 000000000000..a58be59888d9
--- /dev/null
+++ b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/WebApp/Program.cs
@@ -0,0 +1,12 @@
+using IncludedControllers;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.Extensions.DependencyInjection;
+
+var builder = WebApplication.CreateBuilder(args);
+builder.Services
+ .AddControllers()
+ .AddApplicationPart(typeof(IncludedController).Assembly);
+
+var app = builder.Build();
+app.MapControllerRoute("default", "{controller}/{action}");
+app.Run();
diff --git a/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/WebApp/WebApp.csproj b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/WebApp/WebApp.csproj
new file mode 100644
index 000000000000..56bc24531254
--- /dev/null
+++ b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/WebApp/WebApp.csproj
@@ -0,0 +1,9 @@
+
+
+ net10.0
+
+
+
+
+
+
\ No newline at end of file
diff --git a/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/WebApp/WebPocoController.cs b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/WebApp/WebPocoController.cs
new file mode 100644
index 000000000000..338ebd5abe67
--- /dev/null
+++ b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/WebApp/WebPocoController.cs
@@ -0,0 +1,4 @@
+public class WebPocoController
+{
+ public void Action(string input) => _ = GetType().Name + input;
+}
\ No newline at end of file
diff --git a/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/global.json b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/global.json
new file mode 100644
index 000000000000..7307ac0a4729
--- /dev/null
+++ b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/global.json
@@ -0,0 +1,5 @@
+{
+ "sdk": {
+ "version": "10.0.201"
+ }
+}
\ No newline at end of file
diff --git a/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/test.py b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/test.py
new file mode 100644
index 000000000000..8e705bd10c06
--- /dev/null
+++ b/csharp/ql/integration-tests/all-platforms/aspnetcore_controller_discovery/test.py
@@ -0,0 +1,9 @@
+def test(codeql, csharp):
+ codeql.database.create(
+ command=[
+ "dotnet build -t:Rebuild WebApp/WebApp.csproj",
+ "dotnet build -t:Rebuild AttributeWebApp/AttributeWebApp.csproj",
+ "dotnet build -t:Rebuild UnmappedWebApp/UnmappedWebApp.csproj",
+ "dotnet build -t:Rebuild Unrelated/Unrelated.csproj",
+ ]
+ )
diff --git a/csharp/ql/integration-tests/all-platforms/standalone_resx/CompilationInfo.expected b/csharp/ql/integration-tests/all-platforms/standalone_resx/CompilationInfo.expected
index 6d91b2700226..d0b09f83622c 100644
--- a/csharp/ql/integration-tests/all-platforms/standalone_resx/CompilationInfo.expected
+++ b/csharp/ql/integration-tests/all-platforms/standalone_resx/CompilationInfo.expected
@@ -1,21 +1,21 @@
-| All NuGet feeds reachable | 1.0 |
-| Failed project restore with missing package error | 0.0 |
-| Failed project restore with package source error | 0.0 |
-| Failed solution restore with missing package error | 0.0 |
-| Failed solution restore with package source error | 0.0 |
-| Inherited NuGet feed count | 1.0 |
-| NuGet feed responsiveness checked | 1.0 |
-| Project files on filesystem | 1.0 |
-| Reachable fallback NuGet feed count | 1.0 |
-| Resource extraction enabled | 1.0 |
-| Restored .NET framework variants | 1.0 |
-| Restored projects through solution files | 0.0 |
-| Solution files on filesystem | 0.0 |
-| Source files generated | 2.0 |
-| Source files on filesystem | 1.0 |
-| Successfully restored project files | 1.0 |
-| Successfully restored solution files | 0.0 |
-| Unresolved references | 0.0 |
-| UseWPF set | 0.0 |
-| UseWindowsForms set | 0.0 |
-| WebView extraction enabled | 1.0 |
+| All NuGet feeds reachable | 1 |
+| Failed project restore with missing package error | 0 |
+| Failed project restore with package source error | 0 |
+| Failed solution restore with missing package error | 0 |
+| Failed solution restore with package source error | 0 |
+| Inherited NuGet feed count | 1 |
+| NuGet feed responsiveness checked | 1 |
+| Project files on filesystem | 1 |
+| Reachable fallback NuGet feed count | 1 |
+| Resource extraction enabled | 1 |
+| Restored .NET framework variants | 1 |
+| Restored projects through solution files | 0 |
+| Solution files on filesystem | 0 |
+| Source files generated | 2 |
+| Source files on filesystem | 1 |
+| Successfully restored project files | 1 |
+| Successfully restored solution files | 0 |
+| Unresolved references | 0 |
+| UseWPF set | 0 |
+| UseWindowsForms set | 0 |
+| WebView extraction enabled | 1 |
diff --git a/csharp/ql/integration-tests/all-platforms/standalone_resx/CompilationInfo.ql b/csharp/ql/integration-tests/all-platforms/standalone_resx/CompilationInfo.ql
index a96c2fd99a69..28898f02fd24 100644
--- a/csharp/ql/integration-tests/all-platforms/standalone_resx/CompilationInfo.ql
+++ b/csharp/ql/integration-tests/all-platforms/standalone_resx/CompilationInfo.ql
@@ -1,16 +1,9 @@
import csharp
import semmle.code.csharp.commons.Diagnostics
-query predicate compilationInfo(string key, float value) {
+query predicate compilationInfo(string key, string value) {
key != "Resolved references" and
key != "Resolved assembly conflicts" and
not key.matches("Compiler diagnostic count for%") and
- exists(Compilation c, string infoKey, string infoValue | infoValue = c.getInfo(infoKey) |
- key = infoKey and
- value = infoValue.toFloat()
- or
- not exists(infoValue.toFloat()) and
- key = infoKey + ": " + infoValue and
- value = 1
- )
+ value = any(Compilation c).getInfo(key)
}
diff --git a/csharp/ql/integration-tests/all-platforms/standalone_slnx/CompilationInfo.expected b/csharp/ql/integration-tests/all-platforms/standalone_slnx/CompilationInfo.expected
index 82cf0509d345..94716c3255ae 100644
--- a/csharp/ql/integration-tests/all-platforms/standalone_slnx/CompilationInfo.expected
+++ b/csharp/ql/integration-tests/all-platforms/standalone_slnx/CompilationInfo.expected
@@ -1,21 +1,21 @@
-| All NuGet feeds reachable | 1.0 |
-| Failed project restore with missing package error | 0.0 |
-| Failed project restore with package source error | 0.0 |
-| Failed solution restore with missing package error | 0.0 |
-| Failed solution restore with package source error | 0.0 |
-| Inherited NuGet feed count | 1.0 |
-| NuGet feed responsiveness checked | 1.0 |
-| Project files on filesystem | 2.0 |
-| Reachable fallback NuGet feed count | 1.0 |
-| Resource extraction enabled | 0.0 |
-| Restored .NET framework variants | 1.0 |
-| Restored projects through solution files | 2.0 |
-| Solution files on filesystem | 1.0 |
-| Source files generated | 1.0 |
-| Source files on filesystem | 2.0 |
-| Successfully restored project files | 0.0 |
-| Successfully restored solution files | 1.0 |
-| Unresolved references | 0.0 |
-| UseWPF set | 0.0 |
-| UseWindowsForms set | 0.0 |
-| WebView extraction enabled | 1.0 |
+| All NuGet feeds reachable | 1 |
+| Failed project restore with missing package error | 0 |
+| Failed project restore with package source error | 0 |
+| Failed solution restore with missing package error | 0 |
+| Failed solution restore with package source error | 0 |
+| Inherited NuGet feed count | 1 |
+| NuGet feed responsiveness checked | 1 |
+| Project files on filesystem | 2 |
+| Reachable fallback NuGet feed count | 1 |
+| Resource extraction enabled | 0 |
+| Restored .NET framework variants | 1 |
+| Restored projects through solution files | 2 |
+| Solution files on filesystem | 1 |
+| Source files generated | 1 |
+| Source files on filesystem | 2 |
+| Successfully restored project files | 0 |
+| Successfully restored solution files | 1 |
+| Unresolved references | 0 |
+| UseWPF set | 0 |
+| UseWindowsForms set | 0 |
+| WebView extraction enabled | 1 |
diff --git a/csharp/ql/integration-tests/all-platforms/standalone_slnx/CompilationInfo.ql b/csharp/ql/integration-tests/all-platforms/standalone_slnx/CompilationInfo.ql
index 078e352be4d9..e6dc52674f37 100644
--- a/csharp/ql/integration-tests/all-platforms/standalone_slnx/CompilationInfo.ql
+++ b/csharp/ql/integration-tests/all-platforms/standalone_slnx/CompilationInfo.ql
@@ -1,16 +1,9 @@
import csharp
import semmle.code.csharp.commons.Diagnostics
-query predicate compilationInfo(string key, float value) {
+query predicate compilationInfo(string key, string value) {
key != "Resolved references" and
key != "Resolved assembly conflicts" and
not key.matches(["Compiler diagnostic count for%", "Extractor message count for group%"]) and
- exists(Compilation c, string infoKey, string infoValue | infoValue = c.getInfo(infoKey) |
- key = infoKey and
- value = infoValue.toFloat()
- or
- not exists(infoValue.toFloat()) and
- key = infoKey + ": " + infoValue and
- value = 1
- )
+ value = any(Compilation c).getInfo(key)
}
diff --git a/csharp/ql/integration-tests/all-platforms/standalone_winforms/CompilationInfo.expected b/csharp/ql/integration-tests/all-platforms/standalone_winforms/CompilationInfo.expected
index 63ddd4903f3e..ee79179d4fae 100644
--- a/csharp/ql/integration-tests/all-platforms/standalone_winforms/CompilationInfo.expected
+++ b/csharp/ql/integration-tests/all-platforms/standalone_winforms/CompilationInfo.expected
@@ -1,21 +1,21 @@
-| All NuGet feeds reachable | 1.0 |
-| Failed project restore with missing package error | 0.0 |
-| Failed project restore with package source error | 0.0 |
-| Failed solution restore with missing package error | 0.0 |
-| Failed solution restore with package source error | 0.0 |
-| Inherited NuGet feed count | 1.0 |
-| NuGet feed responsiveness checked | 1.0 |
-| Project files on filesystem | 1.0 |
-| Reachable fallback NuGet feed count | 1.0 |
-| Resource extraction enabled | 0.0 |
-| Restored .NET framework variants | 1.0 |
-| Restored projects through solution files | 0.0 |
-| Solution files on filesystem | 0.0 |
-| Source files generated | 1.0 |
-| Source files on filesystem | 3.0 |
-| Successfully restored project files | 1.0 |
-| Successfully restored solution files | 0.0 |
-| Unresolved references | 0.0 |
-| UseWPF set | 0.0 |
-| UseWindowsForms set | 1.0 |
-| WebView extraction enabled | 1.0 |
+| All NuGet feeds reachable | 1 |
+| Failed project restore with missing package error | 0 |
+| Failed project restore with package source error | 0 |
+| Failed solution restore with missing package error | 0 |
+| Failed solution restore with package source error | 0 |
+| Inherited NuGet feed count | 1 |
+| NuGet feed responsiveness checked | 1 |
+| Project files on filesystem | 1 |
+| Reachable fallback NuGet feed count | 1 |
+| Resource extraction enabled | 0 |
+| Restored .NET framework variants | 1 |
+| Restored projects through solution files | 0 |
+| Solution files on filesystem | 0 |
+| Source files generated | 1 |
+| Source files on filesystem | 3 |
+| Successfully restored project files | 1 |
+| Successfully restored solution files | 0 |
+| Unresolved references | 0 |
+| UseWPF set | 0 |
+| UseWindowsForms set | 1 |
+| WebView extraction enabled | 1 |
diff --git a/csharp/ql/integration-tests/all-platforms/standalone_winforms/CompilationInfo.ql b/csharp/ql/integration-tests/all-platforms/standalone_winforms/CompilationInfo.ql
index 078e352be4d9..e6dc52674f37 100644
--- a/csharp/ql/integration-tests/all-platforms/standalone_winforms/CompilationInfo.ql
+++ b/csharp/ql/integration-tests/all-platforms/standalone_winforms/CompilationInfo.ql
@@ -1,16 +1,9 @@
import csharp
import semmle.code.csharp.commons.Diagnostics
-query predicate compilationInfo(string key, float value) {
+query predicate compilationInfo(string key, string value) {
key != "Resolved references" and
key != "Resolved assembly conflicts" and
not key.matches(["Compiler diagnostic count for%", "Extractor message count for group%"]) and
- exists(Compilation c, string infoKey, string infoValue | infoValue = c.getInfo(infoKey) |
- key = infoKey and
- value = infoValue.toFloat()
- or
- not exists(infoValue.toFloat()) and
- key = infoKey + ": " + infoValue and
- value = 1
- )
+ value = any(Compilation c).getInfo(key)
}
diff --git a/csharp/ql/integration-tests/posix/conftest.py b/csharp/ql/integration-tests/posix/conftest.py
index 543bc046c982..ff1b954d1736 100644
--- a/csharp/ql/integration-tests/posix/conftest.py
+++ b/csharp/ql/integration-tests/posix/conftest.py
@@ -5,15 +5,10 @@ def _supports_mono_nuget():
"""
Helper function to determine if the current platform supports Mono and nuget.
- Returns True if running on Linux or on macOS x86_64 (excluding macos-15 and macos-26).
- macOS ARM runners (macos-15 and macos-26) are excluded due to issues with Mono and nuget.
+ Returns True on Ubuntu before 26.04 and on macOS x86_64 before macOS 15. Linux other than
+ Ubuntu is not selected, as we do not test on it.
+ Ubuntu dropped the `mono-complete` package in 26.04, and mono is end-of-life, its own apt
+ repository publishing nothing newer than Ubuntu 20.04, so there is nothing to fall back on.
+ macOS 15 and later are ARM runners, which have issues with Mono and nuget.
"""
- return (
- runs_on.linux
- or (
- runs_on.macos
- and runs_on.x86_64
- and not runs_on.macos_15
- and not runs_on.macos_26
- )
- )
+ return runs_on.ubuntu < 2604 or (runs_on.macos < 15 and runs_on.x86_64)
diff --git a/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_clear/CompilationInfo.expected b/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_clear/CompilationInfo.expected
index ff0b29da33fa..e8a0b5897082 100644
--- a/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_clear/CompilationInfo.expected
+++ b/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_clear/CompilationInfo.expected
@@ -1,22 +1,22 @@
-| All NuGet feeds reachable | 1.0 |
-| Failed project restore with missing package error | 0.0 |
-| Failed project restore with package source error | 0.0 |
-| Failed solution restore with missing package error | 0.0 |
-| Failed solution restore with package source error | 0.0 |
-| Inherited NuGet feed count | 1.0 |
-| NuGet feed responsiveness checked | 1.0 |
-| Project files on filesystem | 1.0 |
-| Reachable fallback NuGet feed count | 1.0 |
-| Resolved assembly conflicts | 0.0 |
-| Resource extraction enabled | 0.0 |
-| Restored .NET framework variants | 1.0 |
-| Restored projects through solution files | 0.0 |
-| Solution files on filesystem | 0.0 |
-| Source files generated | 0.0 |
-| Source files on filesystem | 1.0 |
-| Successfully restored project files | 1.0 |
-| Successfully restored solution files | 0.0 |
-| Unresolved references | 0.0 |
-| UseWPF set | 0.0 |
-| UseWindowsForms set | 0.0 |
-| WebView extraction enabled | 1.0 |
+| All NuGet feeds reachable | 1 |
+| Failed project restore with missing package error | 0 |
+| Failed project restore with package source error | 0 |
+| Failed solution restore with missing package error | 0 |
+| Failed solution restore with package source error | 0 |
+| Inherited NuGet feed count | 1 |
+| NuGet feed responsiveness checked | 1 |
+| Project files on filesystem | 1 |
+| Reachable fallback NuGet feed count | 1 |
+| Resolved assembly conflicts | 0 |
+| Resource extraction enabled | 0 |
+| Restored .NET framework variants | 1 |
+| Restored projects through solution files | 0 |
+| Solution files on filesystem | 0 |
+| Source files generated | 0 |
+| Source files on filesystem | 1 |
+| Successfully restored project files | 1 |
+| Successfully restored solution files | 0 |
+| Unresolved references | 0 |
+| UseWPF set | 0 |
+| UseWindowsForms set | 0 |
+| WebView extraction enabled | 1 |
diff --git a/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_clear/CompilationInfo.ql b/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_clear/CompilationInfo.ql
index 073ffe3b224d..ae7505a8b0be 100644
--- a/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_clear/CompilationInfo.ql
+++ b/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_clear/CompilationInfo.ql
@@ -1,15 +1,8 @@
import csharp
import semmle.code.csharp.commons.Diagnostics
-query predicate compilationInfo(string key, float value) {
+query predicate compilationInfo(string key, string value) {
key != "Resolved references" and
not key.matches("Compiler diagnostic count for%") and
- exists(Compilation c, string infoKey, string infoValue | infoValue = c.getInfo(infoKey) |
- key = infoKey and
- value = infoValue.toFloat()
- or
- not exists(infoValue.toFloat()) and
- key = infoKey + ": " + infoValue and
- value = 1
- )
+ value = any(Compilation c).getInfo(key)
}
diff --git a/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_config_error/CompilationInfo.expected b/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_config_error/CompilationInfo.expected
index 4acd4f54e8a6..c5cb911d3731 100644
--- a/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_config_error/CompilationInfo.expected
+++ b/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_config_error/CompilationInfo.expected
@@ -1,24 +1,25 @@
-| All NuGet feeds reachable | 0.0 |
-| Failed project restore with missing package error | 1.0 |
-| Failed project restore with package source error | 0.0 |
-| Failed solution restore with missing package error | 0.0 |
-| Failed solution restore with package source error | 0.0 |
-| Fallback nuget restore | 1.0 |
-| Inherited NuGet feed count | 1.0 |
-| NuGet feed responsiveness checked | 1.0 |
-| Project files on filesystem | 1.0 |
-| Reachable fallback NuGet feed count | 1.0 |
-| Resolved assembly conflicts | 7.0 |
-| Resource extraction enabled | 0.0 |
-| Restored .NET framework variants | 0.0 |
-| Restored projects through solution files | 0.0 |
-| Solution files on filesystem | 1.0 |
-| Source files generated | 0.0 |
-| Source files on filesystem | 1.0 |
-| Successfully ran fallback nuget restore | 1.0 |
-| Successfully restored project files | 0.0 |
-| Successfully restored solution files | 1.0 |
-| Unresolved references | 0.0 |
-| UseWPF set | 0.0 |
-| UseWindowsForms set | 0.0 |
-| WebView extraction enabled | 1.0 |
+| All NuGet feeds reachable | 0 |
+| Failed project restore with missing package error | 1 |
+| Failed project restore with package source error | 0 |
+| Failed solution restore with missing package error | 0 |
+| Failed solution restore with package source error | 0 |
+| Fallback nuget restore | 1 |
+| Inherited NuGet feed count | 1 |
+| NuGet feed responsiveness checked | 1 |
+| Project files on filesystem | 1 |
+| Reachable fallback NuGet feed count | 1 |
+| Resolved assembly conflicts | 7 |
+| Resource extraction enabled | 0 |
+| Restored .NET framework variants | 0 |
+| Restored projects through solution files | 0 |
+| Solution files on filesystem | 1 |
+| Source files generated | 0 |
+| Source files on filesystem | 1 |
+| Successfully ran fallback nuget restore | 1 |
+| Successfully restored project files | 0 |
+| Successfully restored solution files | 1 |
+| Unreachable NuGet feeds | https://abc.abc/packages/ |
+| Unresolved references | 0 |
+| UseWPF set | 0 |
+| UseWindowsForms set | 0 |
+| WebView extraction enabled | 1 |
diff --git a/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_config_error/CompilationInfo.ql b/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_config_error/CompilationInfo.ql
index 073ffe3b224d..ae7505a8b0be 100644
--- a/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_config_error/CompilationInfo.ql
+++ b/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_config_error/CompilationInfo.ql
@@ -1,15 +1,8 @@
import csharp
import semmle.code.csharp.commons.Diagnostics
-query predicate compilationInfo(string key, float value) {
+query predicate compilationInfo(string key, string value) {
key != "Resolved references" and
not key.matches("Compiler diagnostic count for%") and
- exists(Compilation c, string infoKey, string infoValue | infoValue = c.getInfo(infoKey) |
- key = infoKey and
- value = infoValue.toFloat()
- or
- not exists(infoValue.toFloat()) and
- key = infoKey + ": " + infoValue and
- value = 1
- )
+ value = any(Compilation c).getInfo(key)
}
diff --git a/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_config_error_timeout/CompilationInfo.expected b/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_config_error_timeout/CompilationInfo.expected
index 5a7abcf543c8..680a7960a06c 100644
--- a/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_config_error_timeout/CompilationInfo.expected
+++ b/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_config_error_timeout/CompilationInfo.expected
@@ -1,17 +1,25 @@
-| All NuGet feeds reachable | 0.0 |
-| Fallback nuget restore | 1.0 |
-| Inherited NuGet feed count | 1.0 |
-| NuGet feed responsiveness checked | 1.0 |
-| Project files on filesystem | 1.0 |
-| Reachable fallback NuGet feed count | 1.0 |
-| Resolved assembly conflicts | 7.0 |
-| Resource extraction enabled | 0.0 |
-| Restored .NET framework variants | 0.0 |
-| Solution files on filesystem | 1.0 |
-| Source files generated | 0.0 |
-| Source files on filesystem | 1.0 |
-| Successfully ran fallback nuget restore | 1.0 |
-| Unresolved references | 0.0 |
-| UseWPF set | 0.0 |
-| UseWindowsForms set | 0.0 |
-| WebView extraction enabled | 1.0 |
+| All NuGet feeds reachable | 0 |
+| Failed project restore with missing package error | 1 |
+| Failed project restore with package source error | 0 |
+| Failed solution restore with missing package error | 0 |
+| Failed solution restore with package source error | 0 |
+| Fallback nuget restore | 1 |
+| Inherited NuGet feed count | 1 |
+| NuGet feed responsiveness checked | 1 |
+| Project files on filesystem | 1 |
+| Reachable fallback NuGet feed count | 1 |
+| Resolved assembly conflicts | 7 |
+| Resource extraction enabled | 0 |
+| Restored .NET framework variants | 0 |
+| Restored projects through solution files | 0 |
+| Solution files on filesystem | 1 |
+| Source files generated | 0 |
+| Source files on filesystem | 1 |
+| Successfully ran fallback nuget restore | 1 |
+| Successfully restored project files | 0 |
+| Successfully restored solution files | 1 |
+| Unreachable NuGet feeds | https://localhost:53/packages/, https://localhost:80/packages/ |
+| Unresolved references | 0 |
+| UseWPF set | 0 |
+| UseWindowsForms set | 0 |
+| WebView extraction enabled | 1 |
diff --git a/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_config_error_timeout/CompilationInfo.ql b/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_config_error_timeout/CompilationInfo.ql
index 073ffe3b224d..ae7505a8b0be 100644
--- a/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_config_error_timeout/CompilationInfo.ql
+++ b/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_config_error_timeout/CompilationInfo.ql
@@ -1,15 +1,8 @@
import csharp
import semmle.code.csharp.commons.Diagnostics
-query predicate compilationInfo(string key, float value) {
+query predicate compilationInfo(string key, string value) {
key != "Resolved references" and
not key.matches("Compiler diagnostic count for%") and
- exists(Compilation c, string infoKey, string infoValue | infoValue = c.getInfo(infoKey) |
- key = infoKey and
- value = infoValue.toFloat()
- or
- not exists(infoValue.toFloat()) and
- key = infoKey + ": " + infoValue and
- value = 1
- )
+ value = any(Compilation c).getInfo(key)
}
diff --git a/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_config_error_timeout/diagnostics.expected b/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_config_error_timeout/diagnostics.expected
index bbd2081f4554..074e4bd48ae9 100644
--- a/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_config_error_timeout/diagnostics.expected
+++ b/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_config_error_timeout/diagnostics.expected
@@ -27,12 +27,12 @@
}
}
{
- "markdownMessage": "Found unreachable NuGet feed in C# analysis with build-mode 'none'. This may cause missing dependencies in the analysis.",
+ "markdownMessage": "Found unreachable NuGet feeds in C# analysis with build-mode 'none':\n\n- `https://localhost:53/packages/`\n- `https://localhost:80/packages/`\n\nThis may cause missing dependencies in the analysis.",
"severity": "note",
"source": {
"extractorName": "csharp",
"id": "csharp/autobuilder/buildless/unreachable-feed",
- "name": "Found unreachable NuGet feed in C# analysis with build-mode 'none'"
+ "name": "Found unreachable NuGet feeds in C# analysis with build-mode 'none'"
},
"visibility": {
"cliSummaryTable": true,
diff --git a/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_config_fallback/CompilationInfo.expected b/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_config_fallback/CompilationInfo.expected
index 9cc03f2f5372..384d64a59855 100644
--- a/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_config_fallback/CompilationInfo.expected
+++ b/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_config_fallback/CompilationInfo.expected
@@ -1,17 +1,25 @@
-| All NuGet feeds reachable | 0.0 |
-| Fallback nuget restore | 1.0 |
-| Inherited NuGet feed count | 1.0 |
-| NuGet feed responsiveness checked | 1.0 |
-| Project files on filesystem | 1.0 |
-| Reachable fallback NuGet feed count | 2.0 |
-| Resolved assembly conflicts | 7.0 |
-| Resource extraction enabled | 0.0 |
-| Restored .NET framework variants | 0.0 |
-| Solution files on filesystem | 1.0 |
-| Source files generated | 0.0 |
-| Source files on filesystem | 1.0 |
-| Successfully ran fallback nuget restore | 1.0 |
-| Unresolved references | 0.0 |
-| UseWPF set | 0.0 |
-| UseWindowsForms set | 0.0 |
-| WebView extraction enabled | 1.0 |
+| All NuGet feeds reachable | 0 |
+| Failed project restore with missing package error | 1 |
+| Failed project restore with package source error | 0 |
+| Failed solution restore with missing package error | 0 |
+| Failed solution restore with package source error | 0 |
+| Fallback nuget restore | 1 |
+| Inherited NuGet feed count | 1 |
+| NuGet feed responsiveness checked | 1 |
+| Project files on filesystem | 1 |
+| Reachable fallback NuGet feed count | 2 |
+| Resolved assembly conflicts | 7 |
+| Resource extraction enabled | 0 |
+| Restored .NET framework variants | 0 |
+| Restored projects through solution files | 0 |
+| Solution files on filesystem | 1 |
+| Source files generated | 0 |
+| Source files on filesystem | 1 |
+| Successfully ran fallback nuget restore | 1 |
+| Successfully restored project files | 0 |
+| Successfully restored solution files | 1 |
+| Unreachable NuGet feeds | https://www.nuget.org/api/v2/ |
+| Unresolved references | 0 |
+| UseWPF set | 0 |
+| UseWindowsForms set | 0 |
+| WebView extraction enabled | 1 |
diff --git a/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_config_fallback/CompilationInfo.ql b/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_config_fallback/CompilationInfo.ql
index 073ffe3b224d..ae7505a8b0be 100644
--- a/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_config_fallback/CompilationInfo.ql
+++ b/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_config_fallback/CompilationInfo.ql
@@ -1,15 +1,8 @@
import csharp
import semmle.code.csharp.commons.Diagnostics
-query predicate compilationInfo(string key, float value) {
+query predicate compilationInfo(string key, string value) {
key != "Resolved references" and
not key.matches("Compiler diagnostic count for%") and
- exists(Compilation c, string infoKey, string infoValue | infoValue = c.getInfo(infoKey) |
- key = infoKey and
- value = infoValue.toFloat()
- or
- not exists(infoValue.toFloat()) and
- key = infoKey + ": " + infoValue and
- value = 1
- )
+ value = any(Compilation c).getInfo(key)
}
diff --git a/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_config_fallback/diagnostics.expected b/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_config_fallback/diagnostics.expected
index bbd2081f4554..66867fd38eeb 100644
--- a/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_config_fallback/diagnostics.expected
+++ b/csharp/ql/integration-tests/posix/standalone_dependencies_nuget_config_fallback/diagnostics.expected
@@ -27,12 +27,12 @@
}
}
{
- "markdownMessage": "Found unreachable NuGet feed in C# analysis with build-mode 'none'. This may cause missing dependencies in the analysis.",
+ "markdownMessage": "Found unreachable NuGet feeds in C# analysis with build-mode 'none':\n\n- `https://www.nuget.org/api/v2/`\n\nThis may cause missing dependencies in the analysis.",
"severity": "note",
"source": {
"extractorName": "csharp",
"id": "csharp/autobuilder/buildless/unreachable-feed",
- "name": "Found unreachable NuGet feed in C# analysis with build-mode 'none'"
+ "name": "Found unreachable NuGet feeds in C# analysis with build-mode 'none'"
},
"visibility": {
"cliSummaryTable": true,
diff --git a/csharp/ql/lib/CHANGELOG.md b/csharp/ql/lib/CHANGELOG.md
index 4df4b3da22fa..78b2f677db3e 100644
--- a/csharp/ql/lib/CHANGELOG.md
+++ b/csharp/ql/lib/CHANGELOG.md
@@ -1,3 +1,15 @@
+## 7.3.0
+
+### New Features
+
+* Added taint modeling for OData action parameter binding (`Microsoft.AspNet.OData`/`Microsoft.AspNetCore.OData`). Values cast, `as`-converted, or type-tested out of `ODataActionParameters`, and entities tracked by `Delta` (via `GetInstance`, `Patch`, `Put`, `CopyChangedValues`, and `CopyUnchangedValues`), now taint the members of the target type.
+
+### Minor Analysis Improvements
+
+* In `build-mode: none`, project and solution restoration is now always attempted using the feeds available.
+* C# analysis with build mode `none` now lists unreachable explicitly configured NuGet feeds in both the extraction warning and the tool status page note. This makes it easier to identify feeds that may cause dependencies to be missing from the analysis.
+* Improved ASP.NET Core MVC controller and action discovery to more closely match runtime behavior, including application parts, endpoint mappings, inherited actions, and controller and action exclusions. Service-injected action parameters are no longer modeled as remote input.
+
## 7.2.0
### New Features
diff --git a/csharp/ql/lib/Linq/Helpers.qll b/csharp/ql/lib/Linq/Helpers.qll
index 2a4d5c8c27a2..fcbc01c5e35d 100644
--- a/csharp/ql/lib/Linq/Helpers.qll
+++ b/csharp/ql/lib/Linq/Helpers.qll
@@ -20,6 +20,26 @@ private int numStmts(ForeachStmt fes) {
else result = 1
}
+private predicate terminatesCallable(Stmt s) {
+ exists(Stmt stripped | stripped = s.stripSingletonBlocks() |
+ stripped instanceof ReturnStmt
+ or
+ stripped instanceof YieldBreakStmt
+ or
+ stripped instanceof ThrowStmt
+ or
+ stripped instanceof BreakStmt
+ or
+ stripped = any(BlockStmt b | terminatesCallable(b.getLastStmt()))
+ or
+ stripped =
+ any(IfStmt nested |
+ terminatesCallable(nested.getThen()) and
+ terminatesCallable(nested.getElse())
+ )
+ )
+}
+
/** Holds if the type's qualified name is "System.Linq.Enumerable" */
predicate isEnumerableType(ValueOrRefType t) {
t.hasFullyQualifiedName("System.Linq", "Enumerable")
@@ -152,7 +172,8 @@ predicate missedWhereOpportunity(ForeachStmtGenericEnumerable fes, IfStmt is) {
is.getThen() instanceof ContinueStmt
or
not exists(is.getElse()) and
- numStmts(fes) = 1
+ numStmts(fes) = 1 and
+ not terminatesCallable(is.getThen())
)
}
diff --git a/csharp/ql/lib/change-notes/released/7.3.0.md b/csharp/ql/lib/change-notes/released/7.3.0.md
new file mode 100644
index 000000000000..2af204c27e3d
--- /dev/null
+++ b/csharp/ql/lib/change-notes/released/7.3.0.md
@@ -0,0 +1,11 @@
+## 7.3.0
+
+### New Features
+
+* Added taint modeling for OData action parameter binding (`Microsoft.AspNet.OData`/`Microsoft.AspNetCore.OData`). Values cast, `as`-converted, or type-tested out of `ODataActionParameters`, and entities tracked by `Delta` (via `GetInstance`, `Patch`, `Put`, `CopyChangedValues`, and `CopyUnchangedValues`), now taint the members of the target type.
+
+### Minor Analysis Improvements
+
+* In `build-mode: none`, project and solution restoration is now always attempted using the feeds available.
+* C# analysis with build mode `none` now lists unreachable explicitly configured NuGet feeds in both the extraction warning and the tool status page note. This makes it easier to identify feeds that may cause dependencies to be missing from the analysis.
+* Improved ASP.NET Core MVC controller and action discovery to more closely match runtime behavior, including application parts, endpoint mappings, inherited actions, and controller and action exclusions. Service-injected action parameters are no longer modeled as remote input.
diff --git a/csharp/ql/lib/codeql-pack.release.yml b/csharp/ql/lib/codeql-pack.release.yml
index fda9ea165fc5..2b9b871fffa7 100644
--- a/csharp/ql/lib/codeql-pack.release.yml
+++ b/csharp/ql/lib/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 7.2.0
+lastReleaseVersion: 7.3.0
diff --git a/csharp/ql/lib/ext/Microsoft.AspNet.OData.model.yml b/csharp/ql/lib/ext/Microsoft.AspNet.OData.model.yml
new file mode 100644
index 000000000000..86065b1ca2ea
--- /dev/null
+++ b/csharp/ql/lib/ext/Microsoft.AspNet.OData.model.yml
@@ -0,0 +1,20 @@
+extensions:
+ - addsTo:
+ pack: codeql/csharp-all
+ extensible: summaryModel
+ data:
+ - ["Microsoft.AspNet.OData", "Delta", True, "GetInstance", "()", "", "Argument[this]", "ReturnValue", "taint", "manual"]
+ - ["Microsoft.AspNet.OData", "Delta", True, "Patch", "(TStructuralType)", "", "Argument[this]", "Argument[0]", "taint", "manual"]
+ - ["Microsoft.AspNet.OData", "Delta", True, "Put", "(TStructuralType)", "", "Argument[this]", "Argument[0]", "taint", "manual"]
+ - ["Microsoft.AspNet.OData", "Delta", True, "CopyChangedValues", "(TStructuralType)", "", "Argument[this]", "Argument[0]", "taint", "manual"]
+ - ["Microsoft.AspNet.OData", "Delta", True, "CopyUnchangedValues", "(TStructuralType)", "", "Argument[this]", "Argument[0]", "taint", "manual"]
+ - ["Microsoft.AspNetCore.OData.Deltas", "Delta", True, "GetInstance", "()", "", "Argument[this]", "ReturnValue", "taint", "manual"]
+ - ["Microsoft.AspNetCore.OData.Deltas", "Delta", True, "Patch", "(T)", "", "Argument[this]", "Argument[0]", "taint", "manual"]
+ - ["Microsoft.AspNetCore.OData.Deltas", "Delta", True, "Put", "(T)", "", "Argument[this]", "Argument[0]", "taint", "manual"]
+ - ["Microsoft.AspNetCore.OData.Deltas", "Delta", True, "CopyChangedValues", "(T)", "", "Argument[this]", "Argument[0]", "taint", "manual"]
+ - ["Microsoft.AspNetCore.OData.Deltas", "Delta", True, "CopyUnchangedValues", "(T)", "", "Argument[this]", "Argument[0]", "taint", "manual"]
+ - ["System.Web.Http.OData", "Delta", True, "GetEntity", "()", "", "Argument[this]", "ReturnValue", "taint", "manual"]
+ - ["System.Web.Http.OData", "Delta", True, "Patch", "(TEntityType)", "", "Argument[this]", "Argument[0]", "taint", "manual"]
+ - ["System.Web.Http.OData", "Delta", True, "Put", "(TEntityType)", "", "Argument[this]", "Argument[0]", "taint", "manual"]
+ - ["System.Web.Http.OData", "Delta", True, "CopyChangedValues", "(TEntityType)", "", "Argument[this]", "Argument[0]", "taint", "manual"]
+ - ["System.Web.Http.OData", "Delta", True, "CopyUnchangedValues", "(TEntityType)", "", "Argument[this]", "Argument[0]", "taint", "manual"]
diff --git a/csharp/ql/lib/qlpack.yml b/csharp/ql/lib/qlpack.yml
index c011a9a20d0b..18ee2c149098 100644
--- a/csharp/ql/lib/qlpack.yml
+++ b/csharp/ql/lib/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/csharp-all
-version: 7.2.1-dev
+version: 7.3.1-dev
groups: csharp
dbscheme: semmlecode.csharp.dbscheme
extractor: csharp
diff --git a/csharp/ql/lib/semmle/code/csharp/Unification.qll b/csharp/ql/lib/semmle/code/csharp/Unification.qll
index c8b78cd07a27..8dabaa8a20a8 100644
--- a/csharp/ql/lib/semmle/code/csharp/Unification.qll
+++ b/csharp/ql/lib/semmle/code/csharp/Unification.qll
@@ -348,6 +348,7 @@ module Gvn {
*
* `subsumes` indicates whether `arg1` in fact subsumes `arg2`.
*/
+ pragma[no_dynamic_join_order]
pragma[nomagic]
private predicate unifiableTypeArguments(
CompoundTypeKind k, GvnTypeArgument arg1, GvnTypeArgument arg2, int i, boolean subsumes
diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraph.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraph.qll
index 14bbb0251728..b30646466a57 100644
--- a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraph.qll
+++ b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraph.qll
@@ -190,7 +190,7 @@ module Ast implements AstSig {
final private class FinalForeachStmt = CS::ForeachStmt;
- class ForeachStmt extends FinalForeachStmt {
+ class ForEachStmt extends FinalForeachStmt {
Expr getVariable() {
result = this.getVariableDeclExpr() or result = this.getVariableDeclTuple()
}
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll
index 816b31580daf..88785012d81b 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll
@@ -30,14 +30,10 @@ module Input implements InputSig
)
}
- class SourceBase extends Void {
+ class FlowSummaryCallBase extends Void {
Location getLocation() { none() }
}
- class SinkBase = SourceBase;
-
- class FlowSummaryCallBase = SourceBase;
-
DataFlowCallable getSummarizedCallableAsDataFlowCallable(SummarizedCallableBase c) {
result.asSummarizedCallable() = c
}
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll
index 238ecab13461..418573cae8cc 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll
@@ -9,6 +9,7 @@ private import semmle.code.csharp.dispatch.Dispatch
private import semmle.code.csharp.commons.ComparisonTest
// import `TaintedMember` definitions from other files to avoid potential reevaluation
private import semmle.code.csharp.frameworks.JsonNET
+private import semmle.code.csharp.frameworks.OData
private import semmle.code.csharp.frameworks.WCF
private import semmle.code.csharp.security.dataflow.flowsources.Remote
diff --git a/csharp/ql/lib/semmle/code/csharp/frameworks/OData.qll b/csharp/ql/lib/semmle/code/csharp/frameworks/OData.qll
new file mode 100644
index 000000000000..bc0cbb431e3f
--- /dev/null
+++ b/csharp/ql/lib/semmle/code/csharp/frameworks/OData.qll
@@ -0,0 +1,115 @@
+/**
+ * Provides taint modeling for `Microsoft.AspNet.OData`/`Microsoft.AspNetCore.OData`
+ * (and the older `System.Web.Http.OData`) OData action parameter binding.
+ *
+ * OData actions receive their untrusted payload in one of two shapes that
+ * bypass the usual "type used as an action-method parameter" taint modeling:
+ *
+ * - `ODataActionParameters`, an untyped `Dictionary` whose
+ * values are cast, `as`-converted, or type-tested to arbitrary model types
+ * by the action method body.
+ * - `Delta`, a change-tracking wrapper for PATCH/PUT requests, whose
+ * tracked property values are exposed via `GetInstance()` (`GetEntity()` in
+ * the older `System.Web.Http.OData`) or copied onto an existing entity via
+ * `Patch`/`Put`/`CopyChangedValues`/`CopyUnchangedValues`.
+ *
+ * In both cases the type that ends up holding the client-controlled data has
+ * no static relationship to the action method's parameter types, so its
+ * members need to be taint-tracked explicitly.
+ */
+
+import csharp
+private import semmle.code.csharp.commons.Collections
+private import semmle.code.csharp.security.dataflow.flowsources.Remote
+
+/** The `ODataActionParameters` dictionary type, across OData library versions. */
+class ODataActionParametersClass extends Class {
+ ODataActionParametersClass() {
+ this.hasFullyQualifiedName("Microsoft.AspNet.OData", "ODataActionParameters") or
+ this.hasFullyQualifiedName("Microsoft.AspNetCore.OData.Formatter", "ODataActionParameters") or
+ this.hasFullyQualifiedName("System.Web.Http.OData", "ODataActionParameters")
+ }
+}
+
+/**
+ * Holds if `e` is (or, via local flow -- e.g. an upcast to `IDictionary`
+ * -- may hold the value of) an `ODataActionParameters` dictionary.
+ */
+private predicate isODataActionParametersValue(Expr e) {
+ exists(ParameterAccess e0 | e0.getType() instanceof ODataActionParametersClass |
+ e0 = e or DataFlow::localExprFlow(e0, e)
+ )
+}
+
+/**
+ * An indexer read on an `ODataActionParameters` dictionary, e.g. `parameters["Foo"]`
+ * (including through an upcast to a base dictionary type/interface).
+ */
+class ODataActionParameterRead extends ElementAccess {
+ ODataActionParameterRead() { isODataActionParametersValue(this.getQualifier()) }
+}
+
+/** Holds if `e` may (locally) hold the value of an `ODataActionParameters` entry. */
+private predicate isODataParameterValue(Expr e) {
+ DataFlow::localExprFlow(any(ODataActionParameterRead r), e)
+}
+
+/** The generic ``Delta`1`` change-tracking class, across OData library versions. */
+class DeltaClass extends UnboundGenericClass {
+ DeltaClass() {
+ this.getNumberOfTypeParameters() = 1 and
+ (
+ this.hasFullyQualifiedName("Microsoft.AspNet.OData", "Delta`1") or
+ this.hasFullyQualifiedName("Microsoft.AspNetCore.OData.Deltas", "Delta`1") or
+ this.hasFullyQualifiedName("System.Web.Http.OData", "Delta`1")
+ )
+ }
+}
+
+/**
+ * A type that a value read out of `ODataActionParameters` is cast, `as`-converted,
+ * or type-tested to -- directly, or wrapped in a collection (`List`,
+ * `IEnumerable`, arrays, ...) -- or a type that is tracked by a `Delta`.
+ */
+class ODataBoundType extends ValueOrRefType {
+ ODataBoundType() {
+ exists(Cast c | isODataParameterValue(c.getExpr()) |
+ this = c.getTargetType() or
+ this = c.getTargetType().(CollectionType).getElementType() or
+ this = c.getTargetType().(ParamsCollectionType).getElementType()
+ )
+ or
+ exists(IsExpr ie, Type t |
+ isODataParameterValue(ie.getExpr()) and
+ t = ie.getPattern().(TypePatternExpr).getCheckedType()
+ |
+ this = t or
+ this = t.(CollectionType).getElementType() or
+ this = t.(ParamsCollectionType).getElementType()
+ )
+ or
+ this = any(ConstructedClass c | c.getUnboundGeneric() instanceof DeltaClass).getTypeArgument(0)
+ }
+}
+
+/**
+ * Taint members (transitively) on types used in
+ * 1. Casts, `as`-conversions, or type tests applied to `ODataActionParameters` values.
+ * 2. The type argument of a `Delta`.
+ *
+ * Note that this also impacts uses of such types in other contexts, the same
+ * trade-off `AspNetRemoteFlowSourceMember` (`Remote.qll`) makes for ASP.NET
+ * action-method parameters.
+ */
+private class ODataBoundMember extends TaintTracking::TaintedMember, CandidateMemberToTaint {
+ ODataBoundMember() {
+ exists(Type t, Type t0 | t = this.getDeclaringType() |
+ (t = t0 or t = t0.(CollectionType).getElementType()) and
+ (
+ t0 = any(ODataBoundMember m).getType()
+ or
+ t0 instanceof ODataBoundType
+ )
+ )
+ }
+}
diff --git a/csharp/ql/lib/semmle/code/csharp/frameworks/microsoft/AspNetCore.qll b/csharp/ql/lib/semmle/code/csharp/frameworks/microsoft/AspNetCore.qll
index 350465052d1a..abdd81646828 100644
--- a/csharp/ql/lib/semmle/code/csharp/frameworks/microsoft/AspNetCore.qll
+++ b/csharp/ql/lib/semmle/code/csharp/frameworks/microsoft/AspNetCore.qll
@@ -2,6 +2,8 @@
import csharp
import semmle.code.csharp.frameworks.Microsoft
+private import semmle.code.csharp.commons.Compilation
+private import semmle.code.csharp.frameworks.System
/** The `Microsoft.AspNetCore` namespace. */
class MicrosoftAspNetCoreNamespace extends Namespace {
@@ -189,49 +191,229 @@ class MicrosoftAspNetCoreMvcControllerBaseClass extends Class {
}
}
-/**
- * A valid ASP.NET Core controller according to:
- * https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/actions?view=aspnetcore-3.1
- * https://github.com/dotnet/aspnetcore/blob/b3c93967ba508b8ef139add27132d9483c1a9eb4/src/Mvc/Mvc.Core/src/Controllers/ControllerFeatureProvider.cs#L39-L75
- */
-class MicrosoftAspNetCoreMvcController extends Class {
- MicrosoftAspNetCoreMvcController() {
+private predicate isPotentialMicrosoftAspNetCoreMvcController(Class controller) {
+ controller =
+ any(Class c |
+ (
+ exists(Assembly a |
+ a.getName() = ["Microsoft.AspNetCore.Mvc.Core", "Microsoft.AspNetCore.Mvc.ViewFeatures"]
+ ) or
+ exists(UsingNamespaceDirective ns |
+ ns.getImportedNamespace() instanceof MicrosoftAspNetCoreMvcNamespace
+ )
+ ) and
+ c.isPublic() and
+ not c instanceof Generic and
+ (
+ c.getABaseType*() instanceof MicrosoftAspNetCoreMvcControllerBaseClass
+ or
+ c.getABaseType*().getName().matches("%Controller")
+ or
+ c.getABaseType*()
+ .getAnAttribute()
+ .getType()
+ .getABaseType*()
+ // ApiControllerAttribute is derived from ControllerAttribute
+ .hasFullyQualifiedName("Microsoft.AspNetCore.Mvc", "ControllerAttribute")
+ ) and
+ not c.getABaseType*().getAnAttribute() instanceof MicrosoftAspNetCoreMvcNonControllerAttribute
+ )
+}
+
+bindingset[element]
+pragma[inline_late]
+private Compilation getACompilationFor(Element element) {
+ result.getAFileCompiled() = element.getFile()
+}
+
+private Assembly getAnAssemblyFor(Type type) {
+ result = getACompilationFor(type).getOutputAssembly()
+}
+
+private predicate isMicrosoftAspNetCoreMvcRegistration(MethodCall call) {
+ call.getTarget()
+ .hasFullyQualifiedName("Microsoft.Extensions.DependencyInjection",
+ ["MvcServiceCollectionExtensions", "MvcCoreServiceCollectionExtensions"],
+ ["AddControllers", "AddControllersWithViews", "AddMvc", "AddMvcCore"])
+}
+
+private predicate isMicrosoftAspNetCoreMvcApplication(Compilation compilation) {
+ exists(MethodCall registration |
+ isMicrosoftAspNetCoreMvcRegistration(registration) and
+ compilation.getAFileCompiled() = registration.getFile()
+ )
+}
+
+private predicate isMicrosoftAspNetCoreMvcAddApplicationPart(MethodCall call) {
+ call.getTarget()
+ .hasFullyQualifiedName("Microsoft.Extensions.DependencyInjection",
+ ["MvcCoreMvcBuilderExtensions", "MvcCoreMvcCoreBuilderExtensions"], "AddApplicationPart")
+}
+
+private predicate isMicrosoftAspNetCoreMvcApplicationPart(Compilation application, Assembly part) {
+ isMicrosoftAspNetCoreMvcApplication(application) and
+ (
+ part = application.getOutputAssembly()
+ or
+ exists(AssemblyAttribute attr, StringLiteral assemblyName |
+ application.getAFileCompiled() = attr.getFile() and
+ attr.getType()
+ .hasFullyQualifiedName("Microsoft.AspNetCore.Mvc.ApplicationParts",
+ "ApplicationPartAttribute") and
+ assemblyName = attr.getArgument(0) and
+ assemblyName.getValue() = part.getName()
+ )
+ or
+ exists(MethodCall addPart, PropertyAccess assemblyAccess, Type partType |
+ application.getAFileCompiled() = addPart.getFile() and
+ isMicrosoftAspNetCoreMvcAddApplicationPart(addPart) and
+ assemblyAccess = addPart.getArgumentForName("assembly") and
+ assemblyAccess.getTarget().hasName("Assembly") and
+ assemblyAccess.getQualifier().(TypeofExpr).getTypeAccess().getTarget() = partType and
+ part = getAnAssemblyFor(partType)
+ )
+ )
+}
+
+private predicate isInMicrosoftAspNetCoreMvcApplication(Class controller, Compilation application) {
+ isMicrosoftAspNetCoreMvcApplicationPart(application, getAnAssemblyFor(controller))
+}
+
+private predicate hasMicrosoftAspNetCoreMvcAttributeRoute(Class controller) {
+ exists(Attribute attr |
(
- exists(Assembly a |
- a.getName() = ["Microsoft.AspNetCore.Mvc.Core", "Microsoft.AspNetCore.Mvc.ViewFeatures"]
- ) or
- exists(UsingNamespaceDirective ns |
- ns.getImportedNamespace() instanceof MicrosoftAspNetCoreMvcNamespace
+ attr = controller.getABaseType*().getAnAttribute()
+ or
+ exists(Method method |
+ controller.hasMember(method) and
+ attr = method.getOverridee*().getAnAttribute()
)
) and
- this.isPublic() and
- not this instanceof Generic and
+ attr.getType()
+ .getABaseType*()
+ .getABaseInterface*()
+ .hasFullyQualifiedName("Microsoft.AspNetCore.Mvc.Routing", "IRouteTemplateProvider")
+ )
+}
+
+private predicate isMicrosoftAspNetCoreMvcConventionalEndpointMapping(MethodCall call) {
+ call.getTarget()
+ .hasFullyQualifiedName("Microsoft.AspNetCore.Builder",
+ "ControllerEndpointRouteBuilderExtensions",
+ [
+ "MapAreaControllerRoute", "MapControllerRoute", "MapDefaultControllerRoute",
+ "MapDynamicControllerRoute"
+ ])
+ or
+ call.getTarget()
+ .hasFullyQualifiedName("Microsoft.AspNetCore.Builder", "MvcApplicationBuilderExtensions",
+ "UseMvcWithDefaultRoute")
+ or
+ call.getTarget()
+ .hasFullyQualifiedName("Microsoft.AspNetCore.Builder", "MvcApplicationBuilderExtensions",
+ "UseMvc") and
+ exists(call.getArgumentForName("configureRoutes"))
+ or
+ call.getTarget()
+ .hasFullyQualifiedName("Microsoft.AspNetCore.Builder", "MvcAreaRouteBuilderExtensions",
+ "MapAreaRoute")
+}
+
+private predicate isMicrosoftAspNetCoreMvcAttributeEndpointMapping(MethodCall call) {
+ call.getTarget()
+ .hasFullyQualifiedName("Microsoft.AspNetCore.Builder",
+ "ControllerEndpointRouteBuilderExtensions", "MapControllers")
+ or
+ call.getTarget()
+ .hasFullyQualifiedName("Microsoft.AspNetCore.Builder", "MvcApplicationBuilderExtensions",
+ "UseMvc") and
+ not exists(call.getArgumentForName("configureRoutes"))
+}
+
+private predicate isMicrosoftAspNetCoreMvcFallbackEndpointMapping(MethodCall call, Class controller) {
+ call.getTarget()
+ .hasFullyQualifiedName("Microsoft.AspNetCore.Builder",
+ "ControllerEndpointRouteBuilderExtensions",
+ ["MapFallbackToAreaController", "MapFallbackToController"]) and
+ call.getArgumentForName("controller").getValue().toLowerCase() + "controller" =
+ controller.getName().toLowerCase() and
+ (
+ call.getTarget().hasName("MapFallbackToController")
+ or
+ call.getTarget().hasName("MapFallbackToAreaController") and
+ exists(Attribute area |
+ area = controller.getABaseType*().getAnAttribute() and
+ area.getType().hasFullyQualifiedName("Microsoft.AspNetCore.Mvc", "AreaAttribute") and
+ area.getArgument(0).getValue().toLowerCase() =
+ call.getArgumentForName("area").getValue().toLowerCase()
+ )
+ )
+}
+
+private predicate hasMicrosoftAspNetCoreMvcEndpointMapping(Compilation application, Class controller) {
+ isInMicrosoftAspNetCoreMvcApplication(controller, application) and
+ exists(MethodCall mapping |
+ application = getACompilationFor(mapping) and
(
- this.getABaseType*() instanceof MicrosoftAspNetCoreMvcControllerBaseClass
+ isMicrosoftAspNetCoreMvcConventionalEndpointMapping(mapping)
or
- this.getABaseType*().getName().matches("%Controller")
+ isMicrosoftAspNetCoreMvcAttributeEndpointMapping(mapping) and
+ hasMicrosoftAspNetCoreMvcAttributeRoute(controller)
or
- this.getABaseType*()
- .getAnAttribute()
- .getType()
- .getABaseType*()
- // ApiControllerAttribute is derived from ControllerAttribute
- .hasFullyQualifiedName("Microsoft.AspNetCore.Mvc", "ControllerAttribute")
- ) and
- not this.getABaseType*().getAnAttribute() instanceof
- MicrosoftAspNetCoreMvcNonControllerAttribute
- }
+ isMicrosoftAspNetCoreMvcFallbackEndpointMapping(mapping, controller)
+ )
+ )
+}
+
+private predicate hasMicrosoftAspNetCoreMvcControllerIdentity(Class controller) {
+ controller.getABaseType*() instanceof MicrosoftAspNetCoreMvcControllerBaseClass
+ or
+ controller
+ .getABaseType*()
+ .getAnAttribute()
+ .getType()
+ .getABaseType*()
+ .hasFullyQualifiedName("Microsoft.AspNetCore.Mvc", "ControllerAttribute")
+}
+
+private predicate isDefaultMicrosoftAspNetCoreMvcController(Class controller) {
+ controller instanceof NonNestedType and
+ controller.isPublic() and
+ not controller.isAbstract() and
+ not controller instanceof Generic and
+ (
+ hasMicrosoftAspNetCoreMvcControllerIdentity(controller)
+ or
+ controller.getName().toLowerCase().matches("%controller") and
+ hasMicrosoftAspNetCoreMvcEndpointMapping(_, controller)
+ ) and
+ not controller.getABaseType*().getAnAttribute() instanceof
+ MicrosoftAspNetCoreMvcNonControllerAttribute
+}
+
+private predicate isMicrosoftAspNetCoreMvcIDisposableMethod(Method method) {
+ exists(Method baseMethod |
+ baseMethod = method.getOverridee*() and
+ not exists(baseMethod.getOverridee()) and
+ (
+ baseMethod.getExplicitlyImplementedInterface() instanceof SystemIDisposableInterface
+ or
+ baseMethod.getUndecoratedName() = "Dispose" and
+ baseMethod.getNumberOfParameters() = 0 and
+ baseMethod.getReturnType() instanceof VoidType and
+ baseMethod.getDeclaringType().getABaseInterface*() instanceof SystemIDisposableInterface
+ )
+ )
+}
- /** Gets an action method for this controller. */
- Method getAnActionMethod() {
- result = this.getAMethod() and
- result.isPublic() and
- not result.isStatic() and
- not result.getAnAttribute() instanceof MicrosoftAspNetCoreMvcNonActionAttribute
+/** A class treated as an owner of ASP.NET Core MVC controller helper methods. */
+class MicrosoftAspNetCoreMvcControllerHelperClass extends Class {
+ MicrosoftAspNetCoreMvcControllerHelperClass() {
+ isPotentialMicrosoftAspNetCoreMvcController(this)
}
- /** Gets a `Redirect*` method. */
- Method getARedirectMethod() {
+ /** Gets a `Redirect*`, `Accepted*`, or `Created*` method. */
+ Method getAResponseMethod() {
result = this.getAMethod() and
(
result.getName().matches("Redirect%")
@@ -243,6 +425,35 @@ class MicrosoftAspNetCoreMvcController extends Class {
}
}
+/**
+ * An ASP.NET Core MVC controller, as described by:
+ * https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/actions
+ * https://learn.microsoft.com/en-us/aspnet/core/mvc/advanced/app-parts
+ * https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/routing
+ * https://github.com/dotnet/aspnetcore/blob/747d2cdb584079a0c7309115979f13c331fb7df7/src/Mvc/Mvc.Core/src/Controllers/ControllerFeatureProvider.cs#L20-L75
+ * https://github.com/dotnet/aspnetcore/blob/747d2cdb584079a0c7309115979f13c331fb7df7/src/Mvc/Mvc.Core/src/ApplicationModels/DefaultApplicationModelProvider.cs#L410-L459
+ */
+class MicrosoftAspNetCoreMvcController extends Class {
+ MicrosoftAspNetCoreMvcController() { isDefaultMicrosoftAspNetCoreMvcController(this) }
+
+ /** Gets an action method for this controller. */
+ Method getAnActionMethod() {
+ this.hasMember(result) and
+ result.isPublic() and
+ not result.isStatic() and
+ not result.isAbstract() and
+ not result instanceof Generic and
+ not result.getOverridee*().getAnAttribute() instanceof MicrosoftAspNetCoreMvcNonActionAttribute and
+ not result.getOverridee*().getDeclaringType() instanceof ObjectType and
+ not isMicrosoftAspNetCoreMvcIDisposableMethod(result)
+ }
+
+ /** Gets a `Redirect*`, `Accepted*`, or `Created*` method. */
+ Method getARedirectMethod() {
+ result = this.(MicrosoftAspNetCoreMvcControllerHelperClass).getAResponseMethod()
+ }
+}
+
/** The `Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper` interface. */
class MicrosoftAspNetCoreMvcRenderingIHtmlHelperInterface extends Interface {
MicrosoftAspNetCoreMvcRenderingIHtmlHelperInterface() {
diff --git a/csharp/ql/lib/semmle/code/csharp/security/dataflow/UrlRedirectQuery.qll b/csharp/ql/lib/semmle/code/csharp/security/dataflow/UrlRedirectQuery.qll
index bad6c990fa7b..1e53fccaaea1 100644
--- a/csharp/ql/lib/semmle/code/csharp/security/dataflow/UrlRedirectQuery.qll
+++ b/csharp/ql/lib/semmle/code/csharp/security/dataflow/UrlRedirectQuery.qll
@@ -251,7 +251,8 @@ class AspNetCoreRedirectSink extends Sink {
AspNetCoreRedirectSink() {
exists(MethodCall mc |
mc.getTarget() = any(MicrosoftAspNetCoreHttpHttpResponse response).getRedirectMethod() or
- mc.getTarget() = any(MicrosoftAspNetCoreMvcController response).getARedirectMethod()
+ mc.getTarget() =
+ any(MicrosoftAspNetCoreMvcControllerHelperClass response).getAResponseMethod()
|
// Response.Redirect uses 'location' parameter
this.getExpr() = mc.getArgumentForName("location")
diff --git a/csharp/ql/lib/semmle/code/csharp/security/dataflow/flowsources/Remote.qll b/csharp/ql/lib/semmle/code/csharp/security/dataflow/flowsources/Remote.qll
index 68c06a1828de..2a04d9c24c3d 100644
--- a/csharp/ql/lib/semmle/code/csharp/security/dataflow/flowsources/Remote.qll
+++ b/csharp/ql/lib/semmle/code/csharp/security/dataflow/flowsources/Remote.qll
@@ -117,7 +117,8 @@ class AspNetServiceRemoteFlowSource extends AspNetRemoteFlowSource, DataFlow::Pa
override string getSourceType() { result = "ASP.NET web service input" }
}
-private class CandidateMemberToTaint extends Member {
+/** A public, non-static, auto-implemented property or field, candidate for taint-tracking. */
+class CandidateMemberToTaint extends Member {
CandidateMemberToTaint() {
this.isPublic() and
not this.isStatic() and
@@ -306,7 +307,19 @@ class AspNetCoreActionMethodParameter extends AspNetCoreRemoteFlowSource, DataFl
AspNetCoreActionMethodParameter() {
exists(Parameter p |
p = this.getParameter() and
- p.fromSource()
+ p.fromSource() and
+ not exists(Attribute attr, ValueOrRefType attributeBase | attr = p.getAnAttribute() |
+ attributeBase = attr.getType().getABaseType*() and
+ (
+ attributeBase
+ .getABaseInterface*()
+ .hasFullyQualifiedName("Microsoft.AspNetCore.Http.Metadata", "IFromServiceMetadata")
+ or
+ attributeBase
+ .hasFullyQualifiedName("Microsoft.Extensions.DependencyInjection",
+ "FromKeyedServicesAttribute")
+ )
+ )
|
p = any(MicrosoftAspNetCoreMvcController c).getAnActionMethod().getAParameter()
)
diff --git a/csharp/ql/src/Bad Practices/CallsUnmanagedCode.qhelp b/csharp/ql/src/Bad Practices/CallsUnmanagedCode.qhelp
index d1ceae7b2f36..4d5d99186d7d 100644
--- a/csharp/ql/src/Bad Practices/CallsUnmanagedCode.qhelp
+++ b/csharp/ql/src/Bad Practices/CallsUnmanagedCode.qhelp
@@ -28,7 +28,7 @@ the User32.dll library.
MSDN, C# Reference extern.
- Wikipedia, Managed code.
+ Wikipedia, Managed code.
diff --git a/csharp/ql/src/Bad Practices/Comments/TodoComments.qhelp b/csharp/ql/src/Bad Practices/Comments/TodoComments.qhelp
index d8a4359750f3..f65d6809b4a7 100644
--- a/csharp/ql/src/Bad Practices/Comments/TodoComments.qhelp
+++ b/csharp/ql/src/Bad Practices/Comments/TodoComments.qhelp
@@ -44,7 +44,7 @@ Approxion:
Wikipedia:
-Comment tags.
+Comment tags.
diff --git a/csharp/ql/src/Bad Practices/Declarations/EmptyInterface.qhelp b/csharp/ql/src/Bad Practices/Declarations/EmptyInterface.qhelp
index b92bf3533b4d..68c9437b84f0 100644
--- a/csharp/ql/src/Bad Practices/Declarations/EmptyInterface.qhelp
+++ b/csharp/ql/src/Bad Practices/Declarations/EmptyInterface.qhelp
@@ -24,7 +24,7 @@ being used as a marker.
- Wikipedia: Marker interface pattern
+ Wikipedia: Marker interface pattern
Microsoft: Using Attributes in C#
diff --git a/csharp/ql/src/Bad Practices/UnmanagedCodeCheck.qhelp b/csharp/ql/src/Bad Practices/UnmanagedCodeCheck.qhelp
index 7c0f24517255..ac13c5bc118b 100644
--- a/csharp/ql/src/Bad Practices/UnmanagedCodeCheck.qhelp
+++ b/csharp/ql/src/Bad Practices/UnmanagedCodeCheck.qhelp
@@ -23,7 +23,7 @@ same function being performed by managed code is shown after.
MSDN, C# Reference extern.
- Wikipedia, Managed code.
+ Wikipedia, Managed code.
diff --git a/csharp/ql/src/CHANGELOG.md b/csharp/ql/src/CHANGELOG.md
index 2e430f88a6ed..cf86d815aa53 100644
--- a/csharp/ql/src/CHANGELOG.md
+++ b/csharp/ql/src/CHANGELOG.md
@@ -1,3 +1,9 @@
+## 1.9.3
+
+### Minor Analysis Improvements
+
+* The `cs/linq/missed-where` query no longer flags `foreach` loops where the matching branch terminates the method, iterator, or loop instead of continuing with filtered loop work.
+
## 1.9.2
### Minor Analysis Improvements
diff --git a/csharp/ql/src/Dead Code/DeadStoreOfLocal.qhelp b/csharp/ql/src/Dead Code/DeadStoreOfLocal.qhelp
index 6e4ca17ec6a7..628e683f2ca5 100644
--- a/csharp/ql/src/Dead Code/DeadStoreOfLocal.qhelp
+++ b/csharp/ql/src/Dead Code/DeadStoreOfLocal.qhelp
@@ -64,7 +64,7 @@ The revised example eliminates the unread assignments.
-Wikipedia: Dead store.
+Wikipedia: Dead store.
MSDN, Code Analysis for Managed Code, CA1804: Remove unused locals.
Microsoft: What's new in C# 7 - Discards.
diff --git a/csharp/ql/src/Likely Bugs/BadCheckOdd.qhelp b/csharp/ql/src/Likely Bugs/BadCheckOdd.qhelp
index e0fc5d6d1ac4..4a87583b0bbe 100644
--- a/csharp/ql/src/Likely Bugs/BadCheckOdd.qhelp
+++ b/csharp/ql/src/Likely Bugs/BadCheckOdd.qhelp
@@ -36,7 +36,7 @@ Consider using x % 2 != 0 to check for odd and x % 2 == 0% Operator (C# Reference).
- Wikipedia: Modulo Operation - Common pitfalls.
+ Wikipedia: Modulo Operation - Common pitfalls.
diff --git a/csharp/ql/src/Linq/MissedWhereOpportunity.qhelp b/csharp/ql/src/Linq/MissedWhereOpportunity.qhelp
index 6b22d1a14edc..53ac540441c4 100644
--- a/csharp/ql/src/Linq/MissedWhereOpportunity.qhelp
+++ b/csharp/ql/src/Linq/MissedWhereOpportunity.qhelp
@@ -3,29 +3,38 @@
"qhelp.dtd">
-Programmers sometimes need to iterative over a filtered version of a sequence, rather than the
-sequence itself. For example, you might want to print out only the numbers in the range [1,10] that
-are even. One standard way of doing this is to write a loop that iterates over the whole sequence,
-testing the variable each iteration to determine whether or not it is even. This is often written
-using either if(!condition(var)) continue; as the initial statement in the loop, or by
+
Programmers sometimes need to iterate over a filtered version of a sequence, rather than the
+sequence itself. For example, you might want to print out only the numbers in the range [1,10] that
+are even. One standard way of doing this is to write a loop that iterates over the whole sequence,
+testing the variable each iteration to determine whether or not it is even. This is often written
+using either if(!condition(var)) continue; as the initial statement in the loop, or by
enclosing the entire loop body with if(condition(var)).
+This recommendation does not apply when the matching branch exits the loop without continuing to
+later iterations, such as with return, yield break, or throw.
+In those cases the loop is searching for a terminal condition rather than filtering the remaining
+loop body.
+
-This pattern works well and is also available as the Where method in LINQ in C# 3.5
-and above. It is better to use a library method in preference to writing your own pattern unless you
-have a specific need for a custom version. In particular, this makes the code easier to read by
+
This pattern works well and is also available as the Where method in LINQ in C# 3.5
+and above. It is better to use a library method in preference to writing your own pattern unless you
+have a specific need for a custom version. In particular, this makes the code easier to read by
expressing the intent better and by reducing the nesting depth of the code.
-This example shows two ways of iterating over a series of integers and only performing an action
+
This example shows two ways of iterating over a series of integers and only performing an action
on the even ones.
This is far better expressed using the Where method.
+The following example should not use Where, because the matching branch exits the
+method or iterator instead of continuing with filtered loop work.
+
+
diff --git a/csharp/ql/src/Linq/MissedWhereOpportunityGood.cs b/csharp/ql/src/Linq/MissedWhereOpportunityGood.cs
new file mode 100644
index 000000000000..b96db876583a
--- /dev/null
+++ b/csharp/ql/src/Linq/MissedWhereOpportunityGood.cs
@@ -0,0 +1,13 @@
+class MissedWhereOpportunityGood
+{
+ public int? FindFirstEven(System.Collections.Generic.IEnumerable values)
+ {
+ foreach (int value in values)
+ {
+ if (value % 2 == 0)
+ return value;
+ }
+
+ return null;
+ }
+}
diff --git a/csharp/ql/src/Metrics/Callables/CCyclomaticComplexity.qhelp b/csharp/ql/src/Metrics/Callables/CCyclomaticComplexity.qhelp
index 56419eca6e2f..7bd1be9c8160 100644
--- a/csharp/ql/src/Metrics/Callables/CCyclomaticComplexity.qhelp
+++ b/csharp/ql/src/Metrics/Callables/CCyclomaticComplexity.qhelp
@@ -32,8 +32,8 @@ testing easier because each helper method can be tested individually.
- Wikipedia. Cyclomatic complexity.
- Wikipedia. Control flow diagram.
+ Wikipedia. Cyclomatic complexity.
+ Wikipedia. Control flow diagram.
Wolfram MathWorld. Linearly Independent.
diff --git a/csharp/ql/src/Metrics/Files/FCyclomaticComplexity.qhelp b/csharp/ql/src/Metrics/Files/FCyclomaticComplexity.qhelp
index d9959a9066ec..3859330a1b00 100644
--- a/csharp/ql/src/Metrics/Files/FCyclomaticComplexity.qhelp
+++ b/csharp/ql/src/Metrics/Files/FCyclomaticComplexity.qhelp
@@ -67,10 +67,10 @@ T. J. McCabe, A Complexity Measure. IEEE Transactions on Software Engin
Dave Thomas, Refactoring as Meta Programming?, in Journal of Object Technology, vol. 4, no. 1, January-February 2005, pp. 7-11.
-Wikipedia: Cyclomatic complexity
+Wikipedia: Cyclomatic complexity
-Wikipedia: Code refactoring
+Wikipedia: Code refactoring
diff --git a/csharp/ql/src/Metrics/Files/FSelfContainedness.qhelp b/csharp/ql/src/Metrics/Files/FSelfContainedness.qhelp
index d8142ffa8ecd..213ea1cfd3fe 100644
--- a/csharp/ql/src/Metrics/Files/FSelfContainedness.qhelp
+++ b/csharp/ql/src/Metrics/Files/FSelfContainedness.qhelp
@@ -19,7 +19,7 @@ should also try to use libraries with source code available.
- Wikipedia. Software Portability.
+ Wikipedia. Software Portability.
diff --git a/csharp/ql/src/Metrics/RefTypes/TUnmanagedCode.qhelp b/csharp/ql/src/Metrics/RefTypes/TUnmanagedCode.qhelp
index 8106b90d7b40..0a4b3a4c5fd4 100644
--- a/csharp/ql/src/Metrics/RefTypes/TUnmanagedCode.qhelp
+++ b/csharp/ql/src/Metrics/RefTypes/TUnmanagedCode.qhelp
@@ -28,7 +28,7 @@ unmanaged code from the User32.dll library.
MSDN, C# Reference. extern.
- Wikipedia. Managed code.
+ Wikipedia. Managed code.
diff --git a/csharp/ql/src/Security Features/CWE-079/XSS.qhelp b/csharp/ql/src/Security Features/CWE-079/XSS.qhelp
index a6183e13c8ed..e766ef6726eb 100644
--- a/csharp/ql/src/Security Features/CWE-079/XSS.qhelp
+++ b/csharp/ql/src/Security Features/CWE-079/XSS.qhelp
@@ -40,7 +40,7 @@ OWASP:
(Cross Site Scripting) Prevention Cheat Sheet.
-Wikipedia: Cross-site scripting.
+Wikipedia: Cross-site scripting.
diff --git a/csharp/ql/src/Security Features/CWE-548/ASPNetDirectoryListing.qhelp b/csharp/ql/src/Security Features/CWE-548/ASPNetDirectoryListing.qhelp
index f078ff574058..e03cf21a96f0 100644
--- a/csharp/ql/src/Security Features/CWE-548/ASPNetDirectoryListing.qhelp
+++ b/csharp/ql/src/Security Features/CWE-548/ASPNetDirectoryListing.qhelp
@@ -21,13 +21,13 @@ If this configuration may be used in production, remove the directoryBrows
-The following example shows the directoryBrowse enable attribute set to true in a Web.config file for ASP.NET:
+The following example shows the directoryBrowse enabled attribute set to true in a Web.config file for ASP.NET:
-To fix this problem, the enable attribute should be set to false, or the directoryBrowse element should be removed completely:
+To fix this problem, the enabled attribute should be set to false, or the directoryBrowse element should be removed completely:
@@ -36,8 +36,8 @@ To fix this problem, the enable attribute should be set to fa
-MSDN:
-directoryBrowse element.
+Microsoft Learn:
+directoryBrowse element.
diff --git a/csharp/ql/src/Security Features/CWE-548/Web.config.bad b/csharp/ql/src/Security Features/CWE-548/Web.config.bad
index 24f547103f82..0d579a8d9a06 100644
--- a/csharp/ql/src/Security Features/CWE-548/Web.config.bad
+++ b/csharp/ql/src/Security Features/CWE-548/Web.config.bad
@@ -1,7 +1,7 @@
-
+
...
-
-
\ No newline at end of file
+
+
diff --git a/csharp/ql/src/Security Features/CWE-548/Web.config.good b/csharp/ql/src/Security Features/CWE-548/Web.config.good
index 5c0566153c64..b2d3258c5d05 100644
--- a/csharp/ql/src/Security Features/CWE-548/Web.config.good
+++ b/csharp/ql/src/Security Features/CWE-548/Web.config.good
@@ -1,7 +1,7 @@
-
+
...
-
-
\ No newline at end of file
+
+
diff --git a/csharp/ql/src/Security Features/InadequateRSAPadding.qhelp b/csharp/ql/src/Security Features/InadequateRSAPadding.qhelp
index cee74515198c..22bad62ea611 100644
--- a/csharp/ql/src/Security Features/InadequateRSAPadding.qhelp
+++ b/csharp/ql/src/Security Features/InadequateRSAPadding.qhelp
@@ -12,7 +12,7 @@
- Wikipedia. RSA. Padding Schemes.
+ Wikipedia. RSA. Padding Schemes.
diff --git a/csharp/ql/src/Security Features/InsecureRandomness.qhelp b/csharp/ql/src/Security Features/InsecureRandomness.qhelp
index 6f9634643ec3..3cecacab77e4 100644
--- a/csharp/ql/src/Security Features/InsecureRandomness.qhelp
+++ b/csharp/ql/src/Security Features/InsecureRandomness.qhelp
@@ -57,7 +57,7 @@ library method, which generates a password with a bias, therefore should be avoi
- Wikipedia. Pseudo-random number generator.
+ Wikipedia. Pseudo-random number generator.
MSDN. RandomNumberGenerator.
MSDN. Membership.GeneratePassword.
diff --git a/csharp/ql/src/Security Features/InsufficientKeySize.qhelp b/csharp/ql/src/Security Features/InsufficientKeySize.qhelp
index 906881cf0c25..da61b8a3200c 100644
--- a/csharp/ql/src/Security Features/InsufficientKeySize.qhelp
+++ b/csharp/ql/src/Security Features/InsufficientKeySize.qhelp
@@ -14,7 +14,7 @@ symmetric encryption.
- Wikipedia. Key size.
+ Wikipedia. Key size.
diff --git a/csharp/ql/src/Security Features/WeakEncryption.qhelp b/csharp/ql/src/Security Features/WeakEncryption.qhelp
index 1749144ae8b9..17ddee743501 100644
--- a/csharp/ql/src/Security Features/WeakEncryption.qhelp
+++ b/csharp/ql/src/Security Features/WeakEncryption.qhelp
@@ -17,8 +17,8 @@
- Wikipedia: Key Size
- Wikipedia: DES
+ Wikipedia: Key Size
+ Wikipedia: DES
diff --git a/csharp/ql/src/change-notes/released/1.9.3.md b/csharp/ql/src/change-notes/released/1.9.3.md
new file mode 100644
index 000000000000..675cc4b65718
--- /dev/null
+++ b/csharp/ql/src/change-notes/released/1.9.3.md
@@ -0,0 +1,5 @@
+## 1.9.3
+
+### Minor Analysis Improvements
+
+* The `cs/linq/missed-where` query no longer flags `foreach` loops where the matching branch terminates the method, iterator, or loop instead of continuing with filtered loop work.
diff --git a/csharp/ql/src/codeql-pack.release.yml b/csharp/ql/src/codeql-pack.release.yml
index 842136056d89..46a56c94195c 100644
--- a/csharp/ql/src/codeql-pack.release.yml
+++ b/csharp/ql/src/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 1.9.2
+lastReleaseVersion: 1.9.3
diff --git a/csharp/ql/src/qlpack.yml b/csharp/ql/src/qlpack.yml
index 84048f55bc43..c90064a3b913 100644
--- a/csharp/ql/src/qlpack.yml
+++ b/csharp/ql/src/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/csharp-queries
-version: 1.9.3-dev
+version: 1.9.4-dev
groups:
- csharp
- queries
diff --git a/csharp/ql/test/library-tests/dataflow/flowsources/aspremote/AspRemoteFlowSource.cs b/csharp/ql/test/library-tests/dataflow/flowsources/aspremote/AspRemoteFlowSource.cs
index 5bc8025f231b..d7b085ef61fd 100644
--- a/csharp/ql/test/library-tests/dataflow/flowsources/aspremote/AspRemoteFlowSource.cs
+++ b/csharp/ql/test/library-tests/dataflow/flowsources/aspremote/AspRemoteFlowSource.cs
@@ -1,5 +1,6 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.DependencyInjection;
using System;
namespace Testing
@@ -21,6 +22,27 @@ public object MyAction(ViewModel viewModel)
{
throw null;
}
+
+ public void BindingSources(
+ string ordinary,
+ [FromQuery] string fromQuery,
+ [FromBody] string fromBody,
+ [FromRoute] string fromRoute,
+ [FromHeader] string fromHeader,
+ [FromServices] IRequestService fromServices,
+ [CustomFromServices] IRequestService customFromServices,
+ [FromKeyedServices("cache")] IRequestService fromKeyedServices,
+ [CustomFromKeyedServices] IRequestService customFromKeyedServices)
+ { }
+ }
+
+ public interface IRequestService { }
+
+ public sealed class CustomFromServicesAttribute : FromServicesAttribute { }
+
+ public sealed class CustomFromKeyedServicesAttribute : FromKeyedServicesAttribute
+ {
+ public CustomFromKeyedServicesAttribute() : base("custom") { }
}
public class Item
diff --git a/csharp/ql/test/library-tests/dataflow/flowsources/aspremote/aspRemoteFlowSource.expected b/csharp/ql/test/library-tests/dataflow/flowsources/aspremote/aspRemoteFlowSource.expected
index ef7a8f3cd784..6ba2741857b5 100644
--- a/csharp/ql/test/library-tests/dataflow/flowsources/aspremote/aspRemoteFlowSource.expected
+++ b/csharp/ql/test/library-tests/dataflow/flowsources/aspremote/aspRemoteFlowSource.expected
@@ -1,23 +1,27 @@
remoteFlowSourceMembers
-| AspRemoteFlowSource.cs:10:23:10:31 | RequestId |
-| AspRemoteFlowSource.cs:11:23:11:36 | RequestIdField |
-| AspRemoteFlowSource.cs:28:23:28:29 | Tainted |
+| AspRemoteFlowSource.cs:11:23:11:31 | RequestId |
+| AspRemoteFlowSource.cs:12:23:12:36 | RequestIdField |
+| AspRemoteFlowSource.cs:50:23:50:29 | Tainted |
remoteFlowSources
-| AspRemoteFlowSource.cs:20:42:20:50 | viewModel |
-| AspRemoteFlowSource.cs:35:42:35:46 | param |
-| AspRemoteFlowSource.cs:43:58:43:63 | newUrl |
-| AspRemoteFlowSource.cs:44:61:44:65 | myApi |
-| AspRemoteFlowSource.cs:44:75:44:79 | myUrl |
-| AspRemoteFlowSource.cs:46:46:46:56 | lambdaParam |
-| AspRemoteFlowSource.cs:52:65:52:76 | mapPostParam |
-| AspRemoteFlowSource.cs:53:63:53:73 | mapPutParam |
-| AspRemoteFlowSource.cs:54:69:54:82 | mapDeleteParam |
-| AspRemoteFlowSource.cs:56:41:56:44 | item |
-| AspRemoteFlowSource.cs:64:43:64:47 | param |
-| AspRemoteFlowSource.cs:71:34:71:35 | id |
-| AspRemoteFlowSource.cs:73:35:73:41 | command |
-| AspRemoteFlowSource.cs:73:48:73:52 | count |
-| AspRemoteFlowSource.cs:75:40:75:43 | data |
-| AspRemoteFlowSource.cs:77:34:77:38 | value |
-| AspRemoteFlowSource.cs:79:37:79:42 | itemId |
-| AspRemoteFlowSource.cs:92:35:92:46 | derivedParam |
+| AspRemoteFlowSource.cs:21:42:21:50 | viewModel |
+| AspRemoteFlowSource.cs:27:20:27:27 | ordinary |
+| AspRemoteFlowSource.cs:28:32:28:40 | fromQuery |
+| AspRemoteFlowSource.cs:29:31:29:38 | fromBody |
+| AspRemoteFlowSource.cs:30:32:30:40 | fromRoute |
+| AspRemoteFlowSource.cs:31:33:31:42 | fromHeader |
+| AspRemoteFlowSource.cs:57:42:57:46 | param |
+| AspRemoteFlowSource.cs:65:58:65:63 | newUrl |
+| AspRemoteFlowSource.cs:66:61:66:65 | myApi |
+| AspRemoteFlowSource.cs:66:75:66:79 | myUrl |
+| AspRemoteFlowSource.cs:68:46:68:56 | lambdaParam |
+| AspRemoteFlowSource.cs:74:65:74:76 | mapPostParam |
+| AspRemoteFlowSource.cs:75:63:75:73 | mapPutParam |
+| AspRemoteFlowSource.cs:76:69:76:82 | mapDeleteParam |
+| AspRemoteFlowSource.cs:78:41:78:44 | item |
+| AspRemoteFlowSource.cs:93:34:93:35 | id |
+| AspRemoteFlowSource.cs:95:35:95:41 | command |
+| AspRemoteFlowSource.cs:95:48:95:52 | count |
+| AspRemoteFlowSource.cs:97:40:97:43 | data |
+| AspRemoteFlowSource.cs:99:34:99:38 | value |
+| AspRemoteFlowSource.cs:101:37:101:42 | itemId |
+| AspRemoteFlowSource.cs:114:35:114:46 | derivedParam |
diff --git a/csharp/ql/test/library-tests/frameworks/OData/OData.cs b/csharp/ql/test/library-tests/frameworks/OData/OData.cs
new file mode 100644
index 000000000000..8e63a2dec7d4
--- /dev/null
+++ b/csharp/ql/test/library-tests/frameworks/OData/OData.cs
@@ -0,0 +1,123 @@
+namespace Test
+{
+ using Microsoft.AspNet.OData;
+ using System.Collections.Generic;
+
+ public class EntityMetadata
+ {
+ public string Owner { get; set; }
+ }
+
+ public class BoundEntity1
+ {
+ public string Name { get; set; }
+
+ public string Content { get; set; }
+
+ public EntityMetadata Metadata { get; set; }
+
+ public List Revisions { get; set; }
+ }
+
+ public class BoundEntity2
+ {
+ public string Name { get; set; }
+ }
+
+ public class RelatedItem
+ {
+ public string Label { get; set; }
+
+ public string Category { get; set; }
+ }
+
+ public class Widget
+ {
+ public string Name { get; set; }
+ }
+
+ public class UnrelatedType
+ {
+ // Never reached via an ODataActionParameters/Delta cast, so this
+ // member must stay untainted even though `UnrelatedType` itself is
+ // used elsewhere in the file.
+ public string Name { get; set; }
+ }
+
+ public class SampleController
+ {
+ void Sink(object o) { }
+
+ void CastFromDictionary(ODataActionParameters parameters)
+ {
+ var entity = (BoundEntity1)parameters["Entity"];
+ Sink(entity); // $ hasTaintFlow=line:51
+ Sink(entity.Name); // $ hasTaintFlow=line:51
+ Sink(entity.Content); // $ hasTaintFlow=line:51
+ Sink(entity.Metadata.Owner); // $ hasTaintFlow=line:51
+ foreach (var m in entity.Revisions)
+ {
+ Sink(m.Owner); // $ hasTaintFlow=line:51
+ }
+ }
+
+ void IsAsFromDictionary(ODataActionParameters parameters)
+ {
+ if (parameters["Items"] is IEnumerable items1)
+ {
+ foreach (var item in items1)
+ {
+ Sink(item.Label); // $ hasTaintFlow=line:64
+ }
+ }
+
+ var items2 = parameters["Items"] as IEnumerable;
+ foreach (var item in items2)
+ {
+ Sink(item.Category); // $ hasTaintFlow=line:64
+ }
+ }
+
+ void UpcastThenIndex(ODataActionParameters parameters)
+ {
+ var dict = (IDictionary)parameters;
+ var entity = (BoundEntity2)dict["Entity"];
+ Sink(entity.Name); // $ hasTaintFlow=line:81
+ }
+
+ void DeltaPatch(Delta delta, Widget original)
+ {
+ delta.Patch(original);
+ Sink(original.Name); // $ hasTaintFlow=line:88
+ }
+
+ void DeltaGetInstance(Delta delta)
+ {
+ var w = delta.GetInstance();
+ Sink(w.Name); // $ hasTaintFlow=line:94
+ }
+
+ void LegacyDeltaPatch(System.Web.Http.OData.Delta delta, Widget original)
+ {
+ delta.Patch(original);
+ Sink(original.Name); // $ hasTaintFlow=line:100
+ }
+
+ void LegacyDeltaGetEntity(System.Web.Http.OData.Delta delta)
+ {
+ var w = delta.GetEntity();
+ Sink(w.Name); // $ hasTaintFlow=line:106
+ }
+
+ void Untainted()
+ {
+ var w = new Widget();
+ w.Name = "safe";
+ Sink(w.Name);
+
+ var u = new UnrelatedType();
+ u.Name = "also safe";
+ Sink(u.Name);
+ }
+ }
+}
diff --git a/csharp/ql/test/library-tests/frameworks/OData/OData.expected b/csharp/ql/test/library-tests/frameworks/OData/OData.expected
new file mode 100644
index 000000000000..1c69969e8756
--- /dev/null
+++ b/csharp/ql/test/library-tests/frameworks/OData/OData.expected
@@ -0,0 +1,89 @@
+models
+| 1 | Summary: Microsoft.AspNet.OData; Delta; true; GetInstance; (); ; Argument[this]; ReturnValue; taint; manual |
+| 2 | Summary: Microsoft.AspNet.OData; Delta; true; Patch; (TStructuralType); ; Argument[this]; Argument[0]; taint; manual |
+| 3 | Summary: System.Web.Http.OData; Delta; true; GetEntity; (); ; Argument[this]; ReturnValue; taint; manual |
+| 4 | Summary: System.Web.Http.OData; Delta; true; Patch; (TEntityType); ; Argument[this]; Argument[0]; taint; manual |
+edges
+| OData.cs:51:55:51:64 | parameters : ODataActionParameters | OData.cs:53:26:53:59 | (...) ... : BoundEntity1 | provenance | |
+| OData.cs:53:17:53:22 | access to local variable entity : BoundEntity1 | OData.cs:54:18:54:23 | access to local variable entity | provenance | |
+| OData.cs:53:17:53:22 | access to local variable entity : BoundEntity1 | OData.cs:55:18:55:28 | access to property Name | provenance | |
+| OData.cs:53:17:53:22 | access to local variable entity : BoundEntity1 | OData.cs:56:18:56:31 | access to property Content | provenance | |
+| OData.cs:53:17:53:22 | access to local variable entity : BoundEntity1 | OData.cs:57:18:57:38 | access to property Owner | provenance | |
+| OData.cs:53:17:53:22 | access to local variable entity : BoundEntity1 | OData.cs:60:22:60:28 | access to property Owner | provenance | |
+| OData.cs:53:26:53:59 | (...) ... : BoundEntity1 | OData.cs:53:17:53:22 | access to local variable entity : BoundEntity1 | provenance | |
+| OData.cs:64:55:64:64 | parameters : ODataActionParameters | OData.cs:70:26:70:35 | access to property Label | provenance | |
+| OData.cs:64:55:64:64 | parameters : ODataActionParameters | OData.cs:74:26:74:72 | ... as ... : IEnumerable | provenance | |
+| OData.cs:74:17:74:22 | access to local variable items2 : IEnumerable | OData.cs:77:22:77:34 | access to property Category | provenance | |
+| OData.cs:74:26:74:72 | ... as ... : IEnumerable | OData.cs:74:17:74:22 | access to local variable items2 : IEnumerable | provenance | |
+| OData.cs:81:52:81:61 | parameters : ODataActionParameters | OData.cs:83:24:83:62 | (...) ... : ODataActionParameters | provenance | |
+| OData.cs:83:17:83:20 | access to local variable dict : ODataActionParameters | OData.cs:84:26:84:53 | (...) ... : BoundEntity2 | provenance | |
+| OData.cs:83:24:83:62 | (...) ... : ODataActionParameters | OData.cs:83:17:83:20 | access to local variable dict : ODataActionParameters | provenance | |
+| OData.cs:84:17:84:22 | access to local variable entity : BoundEntity2 | OData.cs:85:18:85:28 | access to property Name | provenance | |
+| OData.cs:84:26:84:53 | (...) ... : BoundEntity2 | OData.cs:84:17:84:22 | access to local variable entity : BoundEntity2 | provenance | |
+| OData.cs:88:39:88:43 | delta : Delta | OData.cs:90:13:90:17 | access to parameter delta : Delta | provenance | |
+| OData.cs:90:13:90:17 | access to parameter delta : Delta | OData.cs:90:25:90:32 | [post] access to parameter original : Widget | provenance | MaD:2 |
+| OData.cs:90:25:90:32 | [post] access to parameter original : Widget | OData.cs:91:18:91:30 | access to property Name | provenance | |
+| OData.cs:94:45:94:49 | delta : Delta | OData.cs:96:21:96:25 | access to parameter delta : Delta | provenance | |
+| OData.cs:96:17:96:17 | access to local variable w : Widget | OData.cs:97:18:97:23 | access to property Name | provenance | |
+| OData.cs:96:21:96:25 | access to parameter delta : Delta | OData.cs:96:21:96:39 | call to method GetInstance : Widget | provenance | MaD:1 |
+| OData.cs:96:21:96:39 | call to method GetInstance : Widget | OData.cs:96:17:96:17 | access to local variable w : Widget | provenance | |
+| OData.cs:100:67:100:71 | delta : Delta | OData.cs:102:13:102:17 | access to parameter delta : Delta | provenance | |
+| OData.cs:102:13:102:17 | access to parameter delta : Delta | OData.cs:102:25:102:32 | [post] access to parameter original : Widget | provenance | MaD:4 |
+| OData.cs:102:25:102:32 | [post] access to parameter original : Widget | OData.cs:103:18:103:30 | access to property Name | provenance | |
+| OData.cs:106:71:106:75 | delta : Delta | OData.cs:108:21:108:25 | access to parameter delta : Delta | provenance | |
+| OData.cs:108:17:108:17 | access to local variable w : Widget | OData.cs:109:18:109:23 | access to property Name | provenance | |
+| OData.cs:108:21:108:25 | access to parameter delta : Delta | OData.cs:108:21:108:37 | call to method GetEntity : Widget | provenance | MaD:3 |
+| OData.cs:108:21:108:37 | call to method GetEntity : Widget | OData.cs:108:17:108:17 | access to local variable w : Widget | provenance | |
+nodes
+| OData.cs:51:55:51:64 | parameters : ODataActionParameters | semmle.label | parameters : ODataActionParameters |
+| OData.cs:53:17:53:22 | access to local variable entity : BoundEntity1 | semmle.label | access to local variable entity : BoundEntity1 |
+| OData.cs:53:26:53:59 | (...) ... : BoundEntity1 | semmle.label | (...) ... : BoundEntity1 |
+| OData.cs:54:18:54:23 | access to local variable entity | semmle.label | access to local variable entity |
+| OData.cs:55:18:55:28 | access to property Name | semmle.label | access to property Name |
+| OData.cs:56:18:56:31 | access to property Content | semmle.label | access to property Content |
+| OData.cs:57:18:57:38 | access to property Owner | semmle.label | access to property Owner |
+| OData.cs:60:22:60:28 | access to property Owner | semmle.label | access to property Owner |
+| OData.cs:64:55:64:64 | parameters : ODataActionParameters | semmle.label | parameters : ODataActionParameters |
+| OData.cs:70:26:70:35 | access to property Label | semmle.label | access to property Label |
+| OData.cs:74:17:74:22 | access to local variable items2 : IEnumerable | semmle.label | access to local variable items2 : IEnumerable |
+| OData.cs:74:26:74:72 | ... as ... : IEnumerable | semmle.label | ... as ... : IEnumerable |
+| OData.cs:77:22:77:34 | access to property Category | semmle.label | access to property Category |
+| OData.cs:81:52:81:61 | parameters : ODataActionParameters | semmle.label | parameters : ODataActionParameters |
+| OData.cs:83:17:83:20 | access to local variable dict : ODataActionParameters | semmle.label | access to local variable dict : ODataActionParameters |
+| OData.cs:83:24:83:62 | (...) ... : ODataActionParameters | semmle.label | (...) ... : ODataActionParameters |
+| OData.cs:84:17:84:22 | access to local variable entity : BoundEntity2 | semmle.label | access to local variable entity : BoundEntity2 |
+| OData.cs:84:26:84:53 | (...) ... : BoundEntity2 | semmle.label | (...) ... : BoundEntity2 |
+| OData.cs:85:18:85:28 | access to property Name | semmle.label | access to property Name |
+| OData.cs:88:39:88:43 | delta : Delta | semmle.label | delta : Delta |
+| OData.cs:90:13:90:17 | access to parameter delta : Delta | semmle.label | access to parameter delta : Delta |
+| OData.cs:90:25:90:32 | [post] access to parameter original : Widget | semmle.label | [post] access to parameter original : Widget |
+| OData.cs:91:18:91:30 | access to property Name | semmle.label | access to property Name |
+| OData.cs:94:45:94:49 | delta : Delta | semmle.label | delta : Delta |
+| OData.cs:96:17:96:17 | access to local variable w : Widget | semmle.label | access to local variable w : Widget |
+| OData.cs:96:21:96:25 | access to parameter delta : Delta | semmle.label | access to parameter delta : Delta |
+| OData.cs:96:21:96:39 | call to method GetInstance : Widget | semmle.label | call to method GetInstance : Widget |
+| OData.cs:97:18:97:23 | access to property Name | semmle.label | access to property Name |
+| OData.cs:100:67:100:71 | delta : Delta | semmle.label | delta : Delta |
+| OData.cs:102:13:102:17 | access to parameter delta : Delta | semmle.label | access to parameter delta : Delta |
+| OData.cs:102:25:102:32 | [post] access to parameter original : Widget | semmle.label | [post] access to parameter original : Widget |
+| OData.cs:103:18:103:30 | access to property Name | semmle.label | access to property Name |
+| OData.cs:106:71:106:75 | delta : Delta | semmle.label | delta : Delta |
+| OData.cs:108:17:108:17 | access to local variable w : Widget | semmle.label | access to local variable w : Widget |
+| OData.cs:108:21:108:25 | access to parameter delta : Delta | semmle.label | access to parameter delta : Delta |
+| OData.cs:108:21:108:37 | call to method GetEntity : Widget | semmle.label | call to method GetEntity : Widget |
+| OData.cs:109:18:109:23 | access to property Name | semmle.label | access to property Name |
+subpaths
+testFailures
+#select
+| OData.cs:54:18:54:23 | access to local variable entity | OData.cs:51:55:51:64 | parameters : ODataActionParameters | OData.cs:54:18:54:23 | access to local variable entity | This path depends on an $@. | OData.cs:51:55:51:64 | parameters | ODataParameters value |
+| OData.cs:55:18:55:28 | access to property Name | OData.cs:51:55:51:64 | parameters : ODataActionParameters | OData.cs:55:18:55:28 | access to property Name | This path depends on an $@. | OData.cs:51:55:51:64 | parameters | ODataParameters value |
+| OData.cs:56:18:56:31 | access to property Content | OData.cs:51:55:51:64 | parameters : ODataActionParameters | OData.cs:56:18:56:31 | access to property Content | This path depends on an $@. | OData.cs:51:55:51:64 | parameters | ODataParameters value |
+| OData.cs:57:18:57:38 | access to property Owner | OData.cs:51:55:51:64 | parameters : ODataActionParameters | OData.cs:57:18:57:38 | access to property Owner | This path depends on an $@. | OData.cs:51:55:51:64 | parameters | ODataParameters value |
+| OData.cs:60:22:60:28 | access to property Owner | OData.cs:51:55:51:64 | parameters : ODataActionParameters | OData.cs:60:22:60:28 | access to property Owner | This path depends on an $@. | OData.cs:51:55:51:64 | parameters | ODataParameters value |
+| OData.cs:70:26:70:35 | access to property Label | OData.cs:64:55:64:64 | parameters : ODataActionParameters | OData.cs:70:26:70:35 | access to property Label | This path depends on an $@. | OData.cs:64:55:64:64 | parameters | ODataParameters value |
+| OData.cs:77:22:77:34 | access to property Category | OData.cs:64:55:64:64 | parameters : ODataActionParameters | OData.cs:77:22:77:34 | access to property Category | This path depends on an $@. | OData.cs:64:55:64:64 | parameters | ODataParameters value |
+| OData.cs:85:18:85:28 | access to property Name | OData.cs:81:52:81:61 | parameters : ODataActionParameters | OData.cs:85:18:85:28 | access to property Name | This path depends on an $@. | OData.cs:81:52:81:61 | parameters | ODataParameters value |
+| OData.cs:91:18:91:30 | access to property Name | OData.cs:88:39:88:43 | delta : Delta | OData.cs:91:18:91:30 | access to property Name | This path depends on an $@. | OData.cs:88:39:88:43 | delta | ODataParameters value |
+| OData.cs:97:18:97:23 | access to property Name | OData.cs:94:45:94:49 | delta : Delta | OData.cs:97:18:97:23 | access to property Name | This path depends on an $@. | OData.cs:94:45:94:49 | delta | ODataParameters value |
+| OData.cs:103:18:103:30 | access to property Name | OData.cs:100:67:100:71 | delta : Delta | OData.cs:103:18:103:30 | access to property Name | This path depends on an $@. | OData.cs:100:67:100:71 | delta | ODataParameters value |
+| OData.cs:109:18:109:23 | access to property Name | OData.cs:106:71:106:75 | delta : Delta | OData.cs:109:18:109:23 | access to property Name | This path depends on an $@. | OData.cs:106:71:106:75 | delta | ODataParameters value |
diff --git a/csharp/ql/test/library-tests/frameworks/OData/OData.ql b/csharp/ql/test/library-tests/frameworks/OData/OData.ql
new file mode 100644
index 000000000000..df79cc58cdb4
--- /dev/null
+++ b/csharp/ql/test/library-tests/frameworks/OData/OData.ql
@@ -0,0 +1,30 @@
+/**
+ * @kind path-problem
+ */
+
+import csharp
+import utils.test.InlineFlowTest
+import PathGraph
+
+module TaintConfig implements DataFlow::ConfigSig {
+ predicate isSource(DataFlow::Node n) {
+ exists(Parameter p | p = n.asParameter() |
+ p.getType().hasFullyQualifiedName("Microsoft.AspNet.OData", "ODataActionParameters")
+ or
+ p.getType().getUnboundDeclaration().hasFullyQualifiedName("Microsoft.AspNet.OData", "Delta`1")
+ or
+ p.getType().getUnboundDeclaration().hasFullyQualifiedName("System.Web.Http.OData", "Delta`1")
+ )
+ }
+
+ predicate isSink(DataFlow::Node sink) {
+ exists(MethodCall c | c.getArgument(0) = sink.asExpr() and c.getTarget().hasName("Sink"))
+ }
+}
+
+import TaintFlowTest
+
+from PathNode source, PathNode sink
+where flowPath(source, sink)
+select sink.getNode(), source, sink, "This path depends on an $@.", source.getNode(),
+ "ODataParameters value"
diff --git a/csharp/ql/test/library-tests/frameworks/OData/options b/csharp/ql/test/library-tests/frameworks/OData/options
new file mode 100644
index 000000000000..64d98a3ae94f
--- /dev/null
+++ b/csharp/ql/test/library-tests/frameworks/OData/options
@@ -0,0 +1,4 @@
+semmle-extractor-options: /nostdlib /noconfig
+semmle-extractor-options: --load-sources-from-project:${testdir}/../../../resources/stubs/_frameworks/Microsoft.NETCore.App/Microsoft.NETCore.App.csproj
+semmle-extractor-options: ${testdir}/../../../resources/stubs/Microsoft.AspNet.OData.cs
+semmle-extractor-options: ${testdir}/../../../resources/stubs/System.Web.Http.OData.cs
diff --git a/csharp/ql/test/library-tests/frameworks/microsoft/AspNetCore.cs b/csharp/ql/test/library-tests/frameworks/microsoft/AspNetCore.cs
index bcf0c766f340..7808bd841f27 100644
--- a/csharp/ql/test/library-tests/frameworks/microsoft/AspNetCore.cs
+++ b/csharp/ql/test/library-tests/frameworks/microsoft/AspNetCore.cs
@@ -1,4 +1,16 @@
using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Routing;
+using Microsoft.Extensions.DependencyInjection;
+
+public static class MvcRegistration
+{
+ public static void Register(IServiceCollection services, IEndpointRouteBuilder endpoints)
+ {
+ services.AddControllers();
+ endpoints.MapControllerRoute("default", "{controller}/{action}");
+ }
+}
// has sufix "Controller"
public class HomeController
@@ -101,3 +113,91 @@ public string Index()
return "This is Home Controller";
}
}
+
+// has case-insensitive suffix "Controller"
+public class LowerCasecontroller
+{
+ public void Action() { }
+}
+
+// derives from ControllerBase, whose [Controller] attribute is inherited
+public class Products : ControllerBase
+{
+ public void List() { }
+}
+
+// is a nested type
+public class ControllerContainer
+{
+ public class NestedController
+ {
+ public void Action() { }
+ }
+}
+
+// only this base class has the Controller suffix
+public class PlainController
+{
+ public void BaseAction() { }
+}
+
+public class DerivedFromPlain : PlainController
+{
+ public void DerivedAction() { }
+}
+
+// is a closed subclass of an open generic controller
+public class GenericBaseController : ControllerBase
+{
+ public void GenericBaseAction(string input) { }
+}
+
+public class ClosedGenericController : GenericBaseController
+{
+ public void ClosedAction(string input) { }
+}
+
+[Controller]
+public abstract class AbstractActionBase
+{
+ public abstract void AbstractAction(string input);
+
+ public void InheritedAction(string input) { }
+
+ [NonAction]
+ public virtual void InheritedNonAction(string input) { }
+}
+
+public class ConcreteActionEndpoint : AbstractActionBase
+{
+ public override void AbstractAction(string input) { }
+
+ public override void InheritedNonAction(string input) { }
+}
+
+public class ActionCasesController : ControllerBase, System.IDisposable
+{
+ public void PublicAction(string input) { }
+
+ public static void StaticAction(string input) { }
+
+ public void GenericAction(T input) { }
+
+ [NonAction]
+ public void ExplicitNonAction(string input) { }
+
+ public override string ToString() => "action cases";
+
+ public void Dispose() { }
+
+ protected void ProtectedAction(string input) { }
+
+ internal void InternalAction(string input) { }
+
+ private void PrivateAction(string input) { }
+}
+
+public class NewDisposeController : Controller
+{
+ public new void Dispose() { }
+}
diff --git a/csharp/ql/test/library-tests/frameworks/microsoft/AspNetCore.expected b/csharp/ql/test/library-tests/frameworks/microsoft/AspNetCore.expected
index e9866698ccdc..428c772add9a 100644
--- a/csharp/ql/test/library-tests/frameworks/microsoft/AspNetCore.expected
+++ b/csharp/ql/test/library-tests/frameworks/microsoft/AspNetCore.expected
@@ -1,7 +1,13 @@
-| AspNetCore.cs:4:14:4:27 | HomeController |
-| AspNetCore.cs:13:14:13:28 | HomeController1 |
-| AspNetCore.cs:22:14:22:28 | HomeController2 |
-| AspNetCore.cs:32:14:32:28 | HomeController3 |
-| AspNetCore.cs:42:14:42:28 | HomeController4 |
-| AspNetCore.cs:51:14:51:28 | HomeController5 |
-| AspNetCore.cs:60:23:60:37 | HomeController6 |
+| AspNetCore.cs:16:14:16:27 | HomeController |
+| AspNetCore.cs:25:14:25:28 | HomeController1 |
+| AspNetCore.cs:34:14:34:28 | HomeController2 |
+| AspNetCore.cs:44:14:44:28 | HomeController3 |
+| AspNetCore.cs:54:14:54:28 | HomeController4 |
+| AspNetCore.cs:63:14:63:28 | HomeController5 |
+| AspNetCore.cs:118:14:118:32 | LowerCasecontroller |
+| AspNetCore.cs:124:14:124:21 | Products |
+| AspNetCore.cs:139:14:139:28 | PlainController |
+| AspNetCore.cs:155:14:155:36 | ClosedGenericController |
+| AspNetCore.cs:171:14:171:35 | ConcreteActionEndpoint |
+| AspNetCore.cs:178:14:178:34 | ActionCasesController |
+| AspNetCore.cs:200:14:200:33 | NewDisposeController |
diff --git a/csharp/ql/test/library-tests/frameworks/microsoft/AspNetCoreActions.expected b/csharp/ql/test/library-tests/frameworks/microsoft/AspNetCoreActions.expected
new file mode 100644
index 000000000000..3ee137abc849
--- /dev/null
+++ b/csharp/ql/test/library-tests/frameworks/microsoft/AspNetCoreActions.expected
@@ -0,0 +1,16 @@
+| AspNetCore.cs:16:14:16:27 | HomeController | AspNetCore.cs:18:19:18:23 | Index |
+| AspNetCore.cs:25:14:25:28 | HomeController1 | AspNetCore.cs:27:19:27:23 | Index |
+| AspNetCore.cs:34:14:34:28 | HomeController2 | AspNetCore.cs:36:19:36:23 | Index |
+| AspNetCore.cs:44:14:44:28 | HomeController3 | AspNetCore.cs:46:19:46:23 | Index |
+| AspNetCore.cs:54:14:54:28 | HomeController4 | AspNetCore.cs:56:19:56:23 | Index |
+| AspNetCore.cs:63:14:63:28 | HomeController5 | AspNetCore.cs:56:19:56:23 | Index |
+| AspNetCore.cs:63:14:63:28 | HomeController5 | AspNetCore.cs:65:19:65:23 | Index |
+| AspNetCore.cs:118:14:118:32 | LowerCasecontroller | AspNetCore.cs:120:17:120:22 | Action |
+| AspNetCore.cs:124:14:124:21 | Products | AspNetCore.cs:126:17:126:20 | List |
+| AspNetCore.cs:139:14:139:28 | PlainController | AspNetCore.cs:141:17:141:26 | BaseAction |
+| AspNetCore.cs:155:14:155:36 | ClosedGenericController | AspNetCore.cs:152:17:152:33 | GenericBaseAction |
+| AspNetCore.cs:155:14:155:36 | ClosedGenericController | AspNetCore.cs:157:17:157:28 | ClosedAction |
+| AspNetCore.cs:171:14:171:35 | ConcreteActionEndpoint | AspNetCore.cs:165:17:165:31 | InheritedAction |
+| AspNetCore.cs:171:14:171:35 | ConcreteActionEndpoint | AspNetCore.cs:173:26:173:39 | AbstractAction |
+| AspNetCore.cs:178:14:178:34 | ActionCasesController | AspNetCore.cs:180:17:180:28 | PublicAction |
+| AspNetCore.cs:200:14:200:33 | NewDisposeController | AspNetCore.cs:202:21:202:27 | Dispose |
diff --git a/csharp/ql/test/library-tests/frameworks/microsoft/AspNetCoreActions.ql b/csharp/ql/test/library-tests/frameworks/microsoft/AspNetCoreActions.ql
new file mode 100644
index 000000000000..f4e927cd0b07
--- /dev/null
+++ b/csharp/ql/test/library-tests/frameworks/microsoft/AspNetCoreActions.ql
@@ -0,0 +1,9 @@
+import csharp
+import semmle.code.csharp.frameworks.microsoft.AspNetCore
+
+from MicrosoftAspNetCoreMvcController controller, Method action
+where
+ controller.fromSource() and
+ action = controller.getAnActionMethod() and
+ action.fromSource()
+select controller, action
diff --git a/csharp/ql/test/query-tests/Linq/MissedWhereOpportunity/MissedWhereOpportunity.cs b/csharp/ql/test/query-tests/Linq/MissedWhereOpportunity/MissedWhereOpportunity.cs
index 0fee1e9c48ff..7b9d35821299 100644
--- a/csharp/ql/test/query-tests/Linq/MissedWhereOpportunity/MissedWhereOpportunity.cs
+++ b/csharp/ql/test/query-tests/Linq/MissedWhereOpportunity/MissedWhereOpportunity.cs
@@ -76,6 +76,104 @@ public void M5(IEnumerable elements)
} // $ Alert
}
+ public int M6(IEnumerable elements)
+ {
+ // GOOD: The filtered case returns from the method instead of continuing the loop.
+ foreach (var element in elements)
+ {
+ if (element.GetHashCode() % 2 == 0)
+ {
+ return element;
+ }
+ }
+
+ return 0;
+ }
+
+ public IEnumerable M7(IEnumerable elements)
+ {
+ // GOOD: The filtered case exits the iterator instead of continuing the loop.
+ foreach (var element in elements)
+ {
+ if (element.GetHashCode() % 2 == 0)
+ {
+ yield break;
+ }
+ }
+ }
+
+ public void M8(IEnumerable elements)
+ {
+ // GOOD: The filtered case throws instead of continuing the loop.
+ foreach (var element in elements)
+ {
+ if (element.GetHashCode() % 2 == 0)
+ {
+ throw new InvalidOperationException();
+ }
+ }
+ }
+
+ public IEnumerable M9(IEnumerable