mirror of
https://github.com/github/codeql.git
synced 2026-05-05 13:45:19 +02:00
Merge branch 'main' into rust-df-patterns
This commit is contained in:
4
.github/workflows/csv-coverage-metrics.yml
vendored
4
.github/workflows/csv-coverage-metrics.yml
vendored
@@ -37,7 +37,7 @@ jobs:
|
||||
run: |
|
||||
DATABASE="${{ runner.temp }}/java-database"
|
||||
codeql database analyze --format=sarif-latest --output=metrics-java.sarif -- "$DATABASE" ./java/ql/src/Metrics/Summaries/FrameworkCoverage.ql
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: metrics-java.sarif
|
||||
path: metrics-java.sarif
|
||||
@@ -64,7 +64,7 @@ jobs:
|
||||
run: |
|
||||
DATABASE="${{ runner.temp }}/csharp-database"
|
||||
codeql database analyze --format=sarif-latest --output=metrics-csharp.sarif -- "$DATABASE" ./csharp/ql/src/Metrics/Summaries/FrameworkCoverage.ql
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: metrics-csharp.sarif
|
||||
path: metrics-csharp.sarif
|
||||
|
||||
10
.github/workflows/csv-coverage-pr-artifacts.yml
vendored
10
.github/workflows/csv-coverage-pr-artifacts.yml
vendored
@@ -71,21 +71,21 @@ jobs:
|
||||
run: |
|
||||
python base/misc/scripts/library-coverage/compare-folders.py out_base out_merge comparison.md
|
||||
- name: Upload CSV package list
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: csv-framework-coverage-merge
|
||||
path: |
|
||||
out_merge/framework-coverage-*.csv
|
||||
out_merge/framework-coverage-*.rst
|
||||
- name: Upload CSV package list
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: csv-framework-coverage-base
|
||||
path: |
|
||||
out_base/framework-coverage-*.csv
|
||||
out_base/framework-coverage-*.rst
|
||||
- name: Upload comparison results
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: comparison
|
||||
path: |
|
||||
@@ -97,7 +97,7 @@ jobs:
|
||||
env:
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
- name: Upload PR number
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: pr
|
||||
path: pr/
|
||||
@@ -117,7 +117,7 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
- name: Upload comment ID (if it exists)
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: comment
|
||||
path: comment/
|
||||
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
run: |
|
||||
python script/misc/scripts/library-coverage/generate-timeseries.py codeqlModels
|
||||
- name: Upload timeseries CSV
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: framework-coverage-timeseries
|
||||
path: framework-coverage-timeseries-*.csv
|
||||
|
||||
4
.github/workflows/csv-coverage.yml
vendored
4
.github/workflows/csv-coverage.yml
vendored
@@ -34,12 +34,12 @@ jobs:
|
||||
run: |
|
||||
python script/misc/scripts/library-coverage/generate-report.py ci codeqlModels script
|
||||
- name: Upload CSV package list
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: framework-coverage-csv
|
||||
path: framework-coverage-*.csv
|
||||
- name: Upload RST package list
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: framework-coverage-rst
|
||||
path: framework-coverage-*.rst
|
||||
|
||||
16
.github/workflows/mad_modelDiff.yml
vendored
16
.github/workflows/mad_modelDiff.yml
vendored
@@ -38,14 +38,20 @@ jobs:
|
||||
path: codeql-main
|
||||
ref: main
|
||||
- uses: ./codeql-main/.github/actions/fetch-codeql
|
||||
# compute the shortname of the project that does not contain any special (disk) characters
|
||||
- run: |
|
||||
echo "SHORTNAME=${SLUG//[^a-zA-Z0-9_]/}" >> $GITHUB_OUTPUT
|
||||
env:
|
||||
SLUG: ${{ matrix.slug }}
|
||||
id: shortname
|
||||
- name: Download database
|
||||
env:
|
||||
SLUG: ${{ matrix.slug }}
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
SHORTNAME: ${{ steps.shortname.outputs.SHORTNAME }}
|
||||
run: |
|
||||
set -x
|
||||
mkdir lib-dbs
|
||||
SHORTNAME=${SLUG//[^a-zA-Z0-9_]/}
|
||||
gh api -H "Accept: application/zip" "/repos/${SLUG}/code-scanning/codeql/databases/java" > "$SHORTNAME.zip"
|
||||
unzip -q -d "${SHORTNAME}-db" "${SHORTNAME}.zip"
|
||||
mkdir "lib-dbs/$SHORTNAME/"
|
||||
@@ -93,14 +99,14 @@ jobs:
|
||||
name="diff_${basename/.model.yml/""}"
|
||||
(diff -w -u $m $t | diff2html -i stdin -F $MODELS/$name.html) || true
|
||||
done
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: models
|
||||
name: models-${{ steps.shortname.outputs.SHORTNAME }}
|
||||
path: tmp-models/**/**/*.model.yml
|
||||
retention-days: 20
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: diffs
|
||||
name: diffs-${{ steps.shortname.outputs.SHORTNAME }}
|
||||
path: tmp-models/*.html
|
||||
# An html file is only produced if the generated models differ.
|
||||
if-no-files-found: ignore
|
||||
|
||||
2
.github/workflows/mad_regenerate-models.yml
vendored
2
.github/workflows/mad_regenerate-models.yml
vendored
@@ -59,7 +59,7 @@ jobs:
|
||||
find java -name "*.model.yml" -print0 | xargs -0 git add
|
||||
git status
|
||||
git diff --cached > models.patch
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: patch
|
||||
path: models.patch
|
||||
|
||||
7
.github/workflows/post-pr-comment.yml
vendored
7
.github/workflows/post-pr-comment.yml
vendored
@@ -17,8 +17,11 @@ jobs:
|
||||
post_comment:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download artifact
|
||||
run: gh run download "${WORKFLOW_RUN_ID}" --repo "${GITHUB_REPOSITORY}" --name "comment"
|
||||
- name: Download artifacts
|
||||
run: |
|
||||
gh run download "${WORKFLOW_RUN_ID}" --repo "${GITHUB_REPOSITORY}" --name "comment-pr-number"
|
||||
gh run download "${WORKFLOW_RUN_ID}" --repo "${GITHUB_REPOSITORY}" --name "comment-body"
|
||||
gh run download "${WORKFLOW_RUN_ID}" --repo "${GITHUB_REPOSITORY}" --name "comment-id"
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
WORKFLOW_RUN_ID: ${{ github.event.workflow_run.id }}
|
||||
|
||||
12
.github/workflows/qhelp-pr-preview.yml
vendored
12
.github/workflows/qhelp-pr-preview.yml
vendored
@@ -36,9 +36,9 @@ jobs:
|
||||
- run: echo "${PR_NUMBER}" > pr_number.txt
|
||||
env:
|
||||
PR_NUMBER: ${{ github.event.number }}
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: comment
|
||||
name: comment-pr-number
|
||||
path: pr_number.txt
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
@@ -78,9 +78,9 @@ jobs:
|
||||
exit "${EXIT_CODE}"
|
||||
|
||||
- if: ${{ !cancelled() }}
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: comment
|
||||
name: comment-body
|
||||
path: comment_body.txt
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
@@ -94,9 +94,9 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ github.token }}
|
||||
PR_NUMBER: ${{ github.event.number }}
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: comment
|
||||
name: comment-id
|
||||
path: comment_id.txt
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
4
.github/workflows/ql-for-ql-build.yml
vendored
4
.github/workflows/ql-for-ql-build.yml
vendored
@@ -75,7 +75,7 @@ jobs:
|
||||
sarif_file: ql-for-ql.sarif
|
||||
category: ql-for-ql
|
||||
- name: Sarif as artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ql-for-ql.sarif
|
||||
path: ql-for-ql.sarif
|
||||
@@ -84,7 +84,7 @@ jobs:
|
||||
mkdir split-sarif
|
||||
node ./ql/scripts/split-sarif.js ql-for-ql.sarif split-sarif
|
||||
- name: Upload langs as artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ql-for-ql-langs
|
||||
path: split-sarif
|
||||
|
||||
@@ -65,7 +65,7 @@ jobs:
|
||||
"${CODEQL}" dataset measure --threads 4 --output "stats/${{ matrix.repo }}/stats.xml" "${{ runner.temp }}/database/db-ql"
|
||||
env:
|
||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: measurements
|
||||
path: stats
|
||||
@@ -76,14 +76,14 @@ jobs:
|
||||
needs: measure
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/download-artifact@v3
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: measurements
|
||||
path: stats
|
||||
- run: |
|
||||
python -m pip install --user lxml
|
||||
find stats -name 'stats.xml' -print0 | sort -z | xargs -0 python ruby/scripts/merge_stats.py --output ql/ql/src/ql.dbscheme.stats --normalise ql_tokeninfo
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ql.dbscheme.stats
|
||||
path: ql/ql/src/ql.dbscheme.stats
|
||||
|
||||
2
.github/workflows/query-list.yml
vendored
2
.github/workflows/query-list.yml
vendored
@@ -37,7 +37,7 @@ jobs:
|
||||
run: |
|
||||
python codeql/misc/scripts/generate-code-scanning-query-list.py > code-scanning-query-list.csv
|
||||
- name: Upload code scanning query list
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: code-scanning-query-list
|
||||
path: code-scanning-query-list.csv
|
||||
|
||||
24
.github/workflows/ruby-build.yml
vendored
24
.github/workflows/ruby-build.yml
vendored
@@ -92,17 +92,17 @@ jobs:
|
||||
- name: Generate dbscheme
|
||||
if: ${{ matrix.os == 'ubuntu-latest' && steps.cache-extractor.outputs.cache-hit != 'true'}}
|
||||
run: ../target/release/codeql-extractor-ruby generate --dbscheme ql/lib/ruby.dbscheme --library ql/lib/codeql/ruby/ast/internal/TreeSitter.qll
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: ${{ matrix.os == 'ubuntu-latest' }}
|
||||
with:
|
||||
name: ruby.dbscheme
|
||||
path: ruby/ql/lib/ruby.dbscheme
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: ${{ matrix.os == 'ubuntu-latest' }}
|
||||
with:
|
||||
name: TreeSitter.qll
|
||||
path: ruby/ql/lib/codeql/ruby/ast/internal/TreeSitter.qll
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: extractor-${{ matrix.os }}
|
||||
path: |
|
||||
@@ -134,7 +134,7 @@ jobs:
|
||||
PACK_FOLDER=$(readlink -f "$PACKS"/codeql/ruby-queries/*)
|
||||
codeql generate query-help --format=sarifv2.1.0 --output="${PACK_FOLDER}/rules.sarif" ql/src
|
||||
(cd ql/src; find queries \( -name '*.qhelp' -o -name '*.rb' -o -name '*.erb' \) -exec bash -c 'mkdir -p "'"${PACK_FOLDER}"'/$(dirname "{}")"' \; -exec cp "{}" "${PACK_FOLDER}/{}" \;)
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: codeql-ruby-queries
|
||||
path: |
|
||||
@@ -147,19 +147,19 @@ jobs:
|
||||
needs: [build, compile-queries]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/download-artifact@v3
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ruby.dbscheme
|
||||
path: ruby/ruby
|
||||
- uses: actions/download-artifact@v3
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: extractor-ubuntu-latest
|
||||
path: ruby/linux64
|
||||
- uses: actions/download-artifact@v3
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: extractor-windows-latest
|
||||
path: ruby/win64
|
||||
- uses: actions/download-artifact@v3
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: extractor-macos-latest
|
||||
path: ruby/osx64
|
||||
@@ -172,13 +172,13 @@ jobs:
|
||||
cp win64/codeql-extractor-ruby.exe ruby/tools/win64/extractor.exe
|
||||
chmod +x ruby/tools/{linux64,osx64}/extractor
|
||||
zip -rq codeql-ruby.zip ruby
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: codeql-ruby-pack
|
||||
path: ruby/codeql-ruby.zip
|
||||
retention-days: 1
|
||||
include-hidden-files: true
|
||||
- uses: actions/download-artifact@v3
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: codeql-ruby-queries
|
||||
path: ruby/qlpacks
|
||||
@@ -190,7 +190,7 @@ jobs:
|
||||
]
|
||||
}' > .codeqlmanifest.json
|
||||
zip -rq codeql-ruby-bundle.zip .codeqlmanifest.json ruby qlpacks
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: codeql-ruby-bundle
|
||||
path: ruby/codeql-ruby-bundle.zip
|
||||
@@ -214,7 +214,7 @@ jobs:
|
||||
uses: ./.github/actions/fetch-codeql
|
||||
|
||||
- name: Download Ruby bundle
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: codeql-ruby-bundle
|
||||
path: ${{ runner.temp }}
|
||||
|
||||
9
.github/workflows/ruby-dataset-measure.yml
vendored
9
.github/workflows/ruby-dataset-measure.yml
vendored
@@ -52,9 +52,9 @@ jobs:
|
||||
run: |
|
||||
mkdir -p "stats/${{ matrix.repo }}"
|
||||
codeql dataset measure --threads 4 --output "stats/${{ matrix.repo }}/stats.xml" "${{ runner.temp }}/database/db-ruby"
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: measurements
|
||||
name: measurements-${{ hashFiles('stats/**') }}
|
||||
path: stats
|
||||
retention-days: 1
|
||||
|
||||
@@ -63,14 +63,13 @@ jobs:
|
||||
needs: measure
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/download-artifact@v3
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: measurements
|
||||
path: stats
|
||||
- run: |
|
||||
python -m pip install --user lxml
|
||||
find stats -name 'stats.xml' | sort | xargs python ruby/scripts/merge_stats.py --output ruby/ql/lib/ruby.dbscheme.stats --normalise ruby_tokeninfo
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ruby.dbscheme.stats
|
||||
path: ruby/ql/lib/ruby.dbscheme.stats
|
||||
|
||||
2
.github/workflows/swift.yml
vendored
2
.github/workflows/swift.yml
vendored
@@ -98,7 +98,7 @@ jobs:
|
||||
- name: Generate C++ files
|
||||
run: |
|
||||
bazel run //swift/codegen:codegen -- --generate=trap,cpp --cpp-output=$PWD/generated-cpp-files
|
||||
- uses: actions/upload-artifact@v3
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: swift-generated-cpp-files
|
||||
path: generated-cpp-files/**
|
||||
|
||||
@@ -196,8 +196,6 @@ private predicate isInvalidFunction(Function func) {
|
||||
expr.getEnclosingFunction() = func and
|
||||
not exists(expr.getType())
|
||||
)
|
||||
or
|
||||
count(func.getEntryPoint().getLocation()) > 1
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
13
cpp/ql/src/Telemetry/CompilerErrors.ql
Normal file
13
cpp/ql/src/Telemetry/CompilerErrors.ql
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* @name Compiler errors
|
||||
* @description A count of all compiler errors, grouped by error text.
|
||||
* @kind metric
|
||||
* @tags summary telemetry
|
||||
* @id cpp/telemetry/compiler-errors
|
||||
*/
|
||||
|
||||
import Metrics
|
||||
|
||||
from CppMetrics::ErrorCount m
|
||||
where RankMetric<CppMetrics::ErrorCount>::getRank(m) <= 50
|
||||
select m.toString(), m.getValue()
|
||||
12
cpp/ql/src/Telemetry/DatabaseQuality.ql
Normal file
12
cpp/ql/src/Telemetry/DatabaseQuality.ql
Normal file
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* @name Database quality
|
||||
* @description Metrics that indicate the quality of the database.
|
||||
* @kind metric
|
||||
* @tags summary telemetry
|
||||
* @id cpp/telemetry/database-quality
|
||||
*/
|
||||
|
||||
import Metrics
|
||||
|
||||
from QualityMetric m
|
||||
select m.toString(), m.getValue()
|
||||
29
cpp/ql/src/Telemetry/Diagnostics.qll
Normal file
29
cpp/ql/src/Telemetry/Diagnostics.qll
Normal file
@@ -0,0 +1,29 @@
|
||||
import cpp
|
||||
|
||||
/**
|
||||
* A syntax error.
|
||||
*/
|
||||
class SyntaxError extends CompilerError {
|
||||
SyntaxError() {
|
||||
this.getTag().matches("exp_%") or
|
||||
this.getTag() =
|
||||
[
|
||||
"bad_data_member_initialization", "bad_pure_specifier", "bad_return", "bad_uuid_string",
|
||||
"literal_without_initializer", "missing_class_definition", "missing_exception_declaration",
|
||||
"nonstd_const_member_decl_not_allowed", "operator_name_not_allowed",
|
||||
"wide_string_invalid_in_asm"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A cannot open file error.
|
||||
* Typically this is due to a missing include.
|
||||
*/
|
||||
class CannotOpenFileError extends CompilerError {
|
||||
CannotOpenFileError() { this.hasTag(["cannot_open_file", "cannot_open_file_reason"]) }
|
||||
|
||||
string getIncludedFile() {
|
||||
result = this.getMessage().regexpCapture("cannot open source file '([^']+)'", 1)
|
||||
}
|
||||
}
|
||||
12
cpp/ql/src/Telemetry/ExtractionMetrics.ql
Normal file
12
cpp/ql/src/Telemetry/ExtractionMetrics.ql
Normal file
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* @name Extraction metrics
|
||||
* @description Raw metrics relating to extraction.
|
||||
* @kind metric
|
||||
* @tags summary telemetry
|
||||
* @id cpp/telemetry/extraction-metrics
|
||||
*/
|
||||
|
||||
import Metrics
|
||||
|
||||
from ExtractionMetric m
|
||||
select m.toString(), m.getValue()
|
||||
269
cpp/ql/src/Telemetry/Metrics.qll
Normal file
269
cpp/ql/src/Telemetry/Metrics.qll
Normal file
@@ -0,0 +1,269 @@
|
||||
import cpp
|
||||
import Diagnostics
|
||||
|
||||
/**
|
||||
* A metric is a string with a value.
|
||||
*/
|
||||
abstract class Metric extends string {
|
||||
bindingset[this]
|
||||
Metric() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A metric that we want to report in cpp/telemetry/extraction-metrics
|
||||
*/
|
||||
abstract class ExtractionMetric extends Metric {
|
||||
bindingset[this]
|
||||
ExtractionMetric() { any() }
|
||||
|
||||
/** Gets the value of this metric. */
|
||||
abstract int getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* A metric that provides a baseline for a SuccessMetric.
|
||||
*/
|
||||
abstract class BaseMetric extends ExtractionMetric {
|
||||
bindingset[this]
|
||||
BaseMetric() { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A metric that is relative to another metric,
|
||||
* so can be used to calculate percentages.
|
||||
*
|
||||
* For clarity, metrics should express success,
|
||||
* so higher values means better.
|
||||
*/
|
||||
abstract class SuccessMetric extends ExtractionMetric {
|
||||
bindingset[this]
|
||||
SuccessMetric() { any() }
|
||||
|
||||
/** Gets the metric this is relative to. */
|
||||
abstract BaseMetric getBaseline();
|
||||
}
|
||||
|
||||
/**
|
||||
* A metric used to report database quality.
|
||||
*/
|
||||
class QualityMetric extends Metric {
|
||||
BaseMetric baseMetric;
|
||||
SuccessMetric relativeMetric;
|
||||
|
||||
QualityMetric() {
|
||||
baseMetric = relativeMetric.getBaseline() and this = "Percentage of " + relativeMetric
|
||||
}
|
||||
|
||||
float getValue() {
|
||||
baseMetric.getValue() > 0 and
|
||||
result = 100.0 * relativeMetric.getValue() / baseMetric.getValue()
|
||||
}
|
||||
}
|
||||
|
||||
signature class RankedMetric extends Metric {
|
||||
int getValue();
|
||||
}
|
||||
|
||||
module RankMetric<RankedMetric M> {
|
||||
int getRank(M s) { s = rank[result](M m | | m order by m.getValue() desc) }
|
||||
}
|
||||
|
||||
/** Various metrics we want to report. */
|
||||
module CppMetrics {
|
||||
class Compilations extends BaseMetric {
|
||||
Compilations() { this = "compilations" }
|
||||
|
||||
override int getValue() { result = count(Compilation c) }
|
||||
}
|
||||
|
||||
class SourceAndHeaderFiles extends BaseMetric {
|
||||
SourceAndHeaderFiles() { this = "source/header files" }
|
||||
|
||||
override int getValue() { result = count(File f | f.fromSource()) }
|
||||
}
|
||||
|
||||
class SourceAndHeaderFilesWithoutErrors extends SuccessMetric {
|
||||
SourceAndHeaderFilesWithoutErrors() { this = "source/header files without errors" }
|
||||
|
||||
override int getValue() {
|
||||
result = count(File f | f.fromSource() and not exists(CompilerError e | f = e.getFile()))
|
||||
}
|
||||
|
||||
override SourceAndHeaderFiles getBaseline() { any() }
|
||||
}
|
||||
|
||||
class CompilationsWithoutErrors extends SuccessMetric {
|
||||
CompilationsWithoutErrors() { this = "compilations without errors" }
|
||||
|
||||
override int getValue() {
|
||||
result = count(Compilation c | not exists(Diagnostic d | d.getFile() = c.getAFileCompiled()))
|
||||
}
|
||||
|
||||
override Compilations getBaseline() { any() }
|
||||
}
|
||||
|
||||
class Expressions extends BaseMetric {
|
||||
Expressions() { this = "expressions" }
|
||||
|
||||
override int getValue() { result = count(Expr e) }
|
||||
}
|
||||
|
||||
class SucceededExpressions extends SuccessMetric {
|
||||
SucceededExpressions() { this = "non-error expressions" }
|
||||
|
||||
override int getValue() { result = count(Expr e) - count(ErrorExpr e) }
|
||||
|
||||
override Expressions getBaseline() { any() }
|
||||
}
|
||||
|
||||
class TypedExpressions extends SuccessMetric {
|
||||
TypedExpressions() { this = "expressions with a known type" }
|
||||
|
||||
override int getValue() { result = count(Expr e | not e.getType() instanceof ErroneousType) }
|
||||
|
||||
override Expressions getBaseline() { any() }
|
||||
}
|
||||
|
||||
class Calls extends BaseMetric {
|
||||
Calls() { this = "calls" }
|
||||
|
||||
override int getValue() { result = count(Call c) }
|
||||
}
|
||||
|
||||
class CallsWithExplicitTarget extends SuccessMetric {
|
||||
CallsWithExplicitTarget() { this = "calls with an explicit target" }
|
||||
|
||||
override int getValue() {
|
||||
result = count(Call c | not c.getTarget().getADeclarationEntry().isImplicit())
|
||||
}
|
||||
|
||||
override Calls getBaseline() { any() }
|
||||
}
|
||||
|
||||
class Variables extends BaseMetric {
|
||||
Variables() { this = "variables" }
|
||||
|
||||
override int getValue() { result = count(Variable v) }
|
||||
}
|
||||
|
||||
class VariablesKnownType extends SuccessMetric {
|
||||
VariablesKnownType() { this = "variables with a known type" }
|
||||
|
||||
override int getValue() {
|
||||
result = count(Variable v | not v.getType() instanceof ErroneousType)
|
||||
}
|
||||
|
||||
override Variables getBaseline() { any() }
|
||||
}
|
||||
|
||||
class LinesOfText extends BaseMetric {
|
||||
LinesOfText() { this = "lines of text" }
|
||||
|
||||
override int getValue() { result = sum(File f | | f.getMetrics().getNumberOfLines()) }
|
||||
}
|
||||
|
||||
class LinesOfCode extends BaseMetric {
|
||||
LinesOfCode() { this = "lines of code" }
|
||||
|
||||
override int getValue() { result = sum(File f | | f.getMetrics().getNumberOfLinesOfCode()) }
|
||||
}
|
||||
|
||||
private predicate errorLine(File file, int line) {
|
||||
exists(Locatable l, Location loc |
|
||||
loc = l.getLocation() and
|
||||
loc.getFile() = file and
|
||||
line in [loc.getStartLine() .. loc.getEndLine()]
|
||||
|
|
||||
l instanceof Diagnostic
|
||||
or
|
||||
l instanceof ErrorExpr
|
||||
)
|
||||
}
|
||||
|
||||
class SucceededLines extends SuccessMetric {
|
||||
SucceededLines() { this = "lines of code without errors" }
|
||||
|
||||
override int getValue() {
|
||||
result =
|
||||
sum(File f | | f.getMetrics().getNumberOfLinesOfCode()) -
|
||||
count(File f, int line | errorLine(f, line))
|
||||
}
|
||||
|
||||
override LinesOfCode getBaseline() { any() }
|
||||
}
|
||||
|
||||
class Functions extends BaseMetric {
|
||||
Functions() { this = "functions" }
|
||||
|
||||
override int getValue() { result = count(Function f) }
|
||||
}
|
||||
|
||||
class SucceededFunctions extends SuccessMetric {
|
||||
SucceededFunctions() { this = "functions without errors" }
|
||||
|
||||
override int getValue() { result = count(Function f | not f.hasErrors()) }
|
||||
|
||||
override Functions getBaseline() { any() }
|
||||
}
|
||||
|
||||
class Includes extends BaseMetric {
|
||||
Includes() { this = "#include directives" }
|
||||
|
||||
override int getValue() { result = count(Include i) + count(CannotOpenFileError e) }
|
||||
}
|
||||
|
||||
class SucceededIncludes extends SuccessMetric {
|
||||
SucceededIncludes() { this = "successfully resolved #include directives" }
|
||||
|
||||
override int getValue() { result = count(Include i) }
|
||||
|
||||
override Includes getBaseline() { any() }
|
||||
}
|
||||
|
||||
class SucceededIncludeCount extends Metric {
|
||||
string includeText;
|
||||
|
||||
SucceededIncludeCount() {
|
||||
exists(Include i |
|
||||
i.getIncludeText() = includeText and
|
||||
exists(i.getFile().getRelativePath()) // Only report includes from the repo
|
||||
) and
|
||||
this = "Successfully included " + includeText
|
||||
}
|
||||
|
||||
int getValue() { result = count(Include i | i.getIncludeText() = includeText) }
|
||||
|
||||
string getIncludeText() { result = includeText }
|
||||
}
|
||||
|
||||
class MissingIncludeCount extends Metric {
|
||||
string includeText;
|
||||
|
||||
MissingIncludeCount() {
|
||||
exists(CannotOpenFileError e | e.getIncludedFile() = includeText) and
|
||||
this = "Failed to include '" + includeText + "'"
|
||||
}
|
||||
|
||||
int getValue() { result = count(CannotOpenFileError e | e.getIncludedFile() = includeText) }
|
||||
|
||||
string getIncludeText() { result = includeText }
|
||||
}
|
||||
|
||||
class CompilerErrors extends ExtractionMetric {
|
||||
CompilerErrors() { this = "compiler errors" }
|
||||
|
||||
override int getValue() { result = count(CompilerError e) }
|
||||
}
|
||||
|
||||
class ErrorCount extends Metric {
|
||||
ErrorCount() { exists(CompilerError e | e.getMessage() = this) }
|
||||
|
||||
int getValue() { result = count(CompilerError e | e.getMessage() = this) }
|
||||
}
|
||||
|
||||
class SyntaxErrorCount extends ExtractionMetric {
|
||||
SyntaxErrorCount() { this = "syntax errors" }
|
||||
|
||||
override int getValue() { result = count(SyntaxError e) }
|
||||
}
|
||||
}
|
||||
13
cpp/ql/src/Telemetry/MissingIncludes.ql
Normal file
13
cpp/ql/src/Telemetry/MissingIncludes.ql
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* @name Failed to include header file
|
||||
* @description A count of all failed includes, grouped by filename.
|
||||
* @kind metric
|
||||
* @tags summary telemetry
|
||||
* @id cpp/telemetry/failed-includes
|
||||
*/
|
||||
|
||||
import Metrics
|
||||
|
||||
from CppMetrics::MissingIncludeCount e
|
||||
where RankMetric<CppMetrics::MissingIncludeCount>::getRank(e) <= 50
|
||||
select e.getIncludeText(), e.getValue()
|
||||
13
cpp/ql/src/Telemetry/SucceededIncludes.ql
Normal file
13
cpp/ql/src/Telemetry/SucceededIncludes.ql
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* @name Successfully included header files
|
||||
* @description A count of all succeeded includes, grouped by filename.
|
||||
* @kind metric
|
||||
* @tags summary telemetry
|
||||
* @id cpp/telemetry/succeeded-includes
|
||||
*/
|
||||
|
||||
import Metrics
|
||||
|
||||
from CppMetrics::SucceededIncludeCount m
|
||||
where RankMetric<CppMetrics::SucceededIncludeCount>::getRank(m) <= 50
|
||||
select m.getIncludeText(), m.getValue()
|
||||
@@ -0,0 +1,10 @@
|
||||
| test.cpp:5:7:5:7 | x | unnecessary NULL check before call to $@ | test.cpp:6:5:6:8 | call to free | free |
|
||||
| test.cpp:23:7:23:7 | x | unnecessary NULL check before call to $@ | test.cpp:26:5:26:8 | call to free | free |
|
||||
| test.cpp:31:7:31:8 | ! ... | unnecessary NULL check before call to $@ | test.cpp:35:3:35:6 | call to free | free |
|
||||
| test.cpp:31:7:31:24 | ... \|\| ... | unnecessary NULL check before call to $@ | test.cpp:35:3:35:6 | call to free | free |
|
||||
| test.cpp:31:8:31:8 | x | unnecessary NULL check before call to $@ | test.cpp:35:3:35:6 | call to free | free |
|
||||
| test.cpp:94:12:94:12 | x | unnecessary NULL check before call to $@ | test.cpp:94:3:94:13 | call to free | free |
|
||||
| test.cpp:98:7:98:8 | ! ... | unnecessary NULL check before call to $@ | test.cpp:101:3:101:6 | call to free | free |
|
||||
| test.cpp:98:8:98:8 | x | unnecessary NULL check before call to $@ | test.cpp:101:3:101:6 | call to free | free |
|
||||
| test.cpp:106:7:106:18 | ... != ... | unnecessary NULL check before call to $@ | test.cpp:107:5:107:8 | call to free | free |
|
||||
| test.cpp:113:7:113:18 | ... != ... | unnecessary NULL check before call to $@ | test.cpp:114:17:114:20 | call to free | free |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Best Practices/GuardedFree.ql
|
||||
@@ -0,0 +1,115 @@
|
||||
extern "C" void free(void *ptr);
|
||||
extern "C" int strcmp(const char *s1, const char *s2);
|
||||
|
||||
void test0(int *x) {
|
||||
if (x) // BAD
|
||||
free(x);
|
||||
}
|
||||
|
||||
void test1(int *x) {
|
||||
if (x) { // BAD
|
||||
free(x);
|
||||
}
|
||||
}
|
||||
|
||||
void test2(int *x) {
|
||||
if (x) { // GOOD: x is being accessed in the body of the if
|
||||
*x = 42;
|
||||
free(x);
|
||||
}
|
||||
}
|
||||
|
||||
void test3(int *x, bool b) {
|
||||
if (x) { // GOOD [FALSE POSITIVE]: x is being accessed in the body of the if
|
||||
if (b)
|
||||
*x = 42;
|
||||
free(x);
|
||||
}
|
||||
}
|
||||
|
||||
bool test4(char *x, char *y) {
|
||||
if (!x || strcmp(x, y)) { // GOOD [FALSE POSITIVE]: x is being accessed in the guard and return value depends on x
|
||||
free(x);
|
||||
return true;
|
||||
}
|
||||
free(x);
|
||||
return false;
|
||||
}
|
||||
|
||||
void test5(char *x) {
|
||||
if (x)
|
||||
*x = 42;
|
||||
if (x) { // BAD
|
||||
free(x);
|
||||
}
|
||||
}
|
||||
|
||||
void test6(char *x) {
|
||||
*x = 42;
|
||||
if (x) { // BAD
|
||||
free(x);
|
||||
}
|
||||
}
|
||||
|
||||
void test7(char *x) {
|
||||
if (x || x) { // BAD [NOT DETECTED]
|
||||
free(x);
|
||||
}
|
||||
}
|
||||
|
||||
bool test8(char *x) {
|
||||
if (x) { // GOOD: return value depends on x
|
||||
free(x);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef FOO
|
||||
#define my_free(x) free(x - 1)
|
||||
#else
|
||||
#define my_free(x) free(x)
|
||||
#endif
|
||||
|
||||
void test9(char *x) {
|
||||
if (x) { // GOOD: macro may make free behave unexpectedly when compiled differently
|
||||
my_free(x);
|
||||
}
|
||||
}
|
||||
|
||||
void test10(char *x) {
|
||||
if (x) { // GOOD: #ifdef may make free behave unexpectedly when compiled differently
|
||||
#ifdef FOO
|
||||
free(x - 1);
|
||||
#else
|
||||
free(x);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#define TRY_FREE(x) \
|
||||
if (x) free(x);
|
||||
|
||||
void test11(char *x) {
|
||||
TRY_FREE(x) // BAD
|
||||
}
|
||||
|
||||
bool test12(char *x) {
|
||||
if (!x) // GOOD [FALSE POSITIVE]: return value depends on x
|
||||
return false;
|
||||
|
||||
free(x);
|
||||
return true;
|
||||
}
|
||||
|
||||
void test13(char *x) {
|
||||
if (x != nullptr) // BAD
|
||||
free(x);
|
||||
}
|
||||
|
||||
void inspect(char *x);
|
||||
|
||||
void test14(char *x) {
|
||||
if (x != nullptr) // GOOD [FALSE POSITIVE]: x might be accessed in the first operand of the comma operator
|
||||
inspect(x), free(x);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
| 'this' may only be used inside a nonstatic member function | 1 |
|
||||
| There was an error during this compilation | 1 |
|
||||
| expected a ')' | 1 |
|
||||
| expected a ';' | 1 |
|
||||
| expected an expression | 1 |
|
||||
| identifier 'no_such_function' is undefined | 1 |
|
||||
| identifier 'nsf2' is undefined | 1 |
|
||||
| identifier 'so_is_this' is undefined | 1 |
|
||||
| identifier 'uint32_t' is undefined | 1 |
|
||||
| too few arguments in function call | 1 |
|
||||
@@ -0,0 +1 @@
|
||||
Telemetry/CompilerErrors.ql
|
||||
@@ -0,0 +1,9 @@
|
||||
| Percentage of calls with an explicit target | 50.0 |
|
||||
| Percentage of compilations without errors | 50.0 |
|
||||
| Percentage of expressions with a known type | 30.0 |
|
||||
| Percentage of functions without errors | 75.0 |
|
||||
| Percentage of lines of code without errors | 63.1578947368421 |
|
||||
| Percentage of non-error expressions | 30.0 |
|
||||
| Percentage of source/header files without errors | 66.66666666666667 |
|
||||
| Percentage of successfully resolved #include directives | 100.0 |
|
||||
| Percentage of variables with a known type | 90.0 |
|
||||
@@ -0,0 +1 @@
|
||||
Telemetry/DatabaseQuality.ql
|
||||
@@ -0,0 +1,7 @@
|
||||
| test.cpp:6:13:6:31 | initializer for x | test.cpp:6:13:6:31 | <error expr> |
|
||||
| test.cpp:7:5:7:8 | ExprStmt | file://:0:0:0:0 | <error expr> |
|
||||
| test.cpp:8:5:8:23 | ExprStmt | file://:0:0:0:0 | <error expr> |
|
||||
| test.cpp:9:5:9:21 | ExprStmt | file://:0:0:0:0 | <error expr> |
|
||||
| test.cpp:11:5:11:8 | ExprStmt | file://:0:0:0:0 | <error expr> |
|
||||
| test.cpp:15:5:15:8 | ExprStmt | file://:0:0:0:0 | <error expr> |
|
||||
| test.cpp:16:5:16:16 | ExprStmt | file://:0:0:0:0 | <error expr> |
|
||||
@@ -0,0 +1,4 @@
|
||||
import cpp
|
||||
|
||||
from ErrorExpr e
|
||||
select e.getParent(), e
|
||||
@@ -0,0 +1,20 @@
|
||||
| #include directives | 2 |
|
||||
| calls | 2 |
|
||||
| calls with an explicit target | 1 |
|
||||
| compilations | 2 |
|
||||
| compilations without errors | 1 |
|
||||
| compiler errors | 10 |
|
||||
| expressions | 10 |
|
||||
| expressions with a known type | 3 |
|
||||
| functions | 8 |
|
||||
| functions without errors | 6 |
|
||||
| lines of code | 19 |
|
||||
| lines of code without errors | 12 |
|
||||
| lines of text | 24 |
|
||||
| non-error expressions | 3 |
|
||||
| source/header files | 3 |
|
||||
| source/header files without errors | 2 |
|
||||
| successfully resolved #include directives | 2 |
|
||||
| syntax errors | 3 |
|
||||
| variables | 10 |
|
||||
| variables with a known type | 9 |
|
||||
@@ -0,0 +1 @@
|
||||
Telemetry/ExtractionMetrics.ql
|
||||
@@ -0,0 +1 @@
|
||||
| "test.h" | 2 |
|
||||
@@ -0,0 +1 @@
|
||||
Telemetry/SucceededIncludes.ql
|
||||
10
cpp/ql/test/library-tests/extraction_errors/diags.expected
Normal file
10
cpp/ql/test/library-tests/extraction_errors/diags.expected
Normal file
@@ -0,0 +1,10 @@
|
||||
| file://:0:0:0:0 | There was an error during this compilation |
|
||||
| test.cpp:6:14:6:14 | identifier 'no_such_function' is undefined |
|
||||
| test.cpp:9:14:9:14 | identifier 'nsf2' is undefined |
|
||||
| test.cpp:11:7:11:7 | too few arguments in function call |
|
||||
| test.cpp:14:1:14:1 | identifier 'uint32_t' is undefined |
|
||||
| test.cpp:15:5:15:5 | 'this' may only be used inside a nonstatic member function |
|
||||
| test.cpp:15:10:15:10 | expected a ';' |
|
||||
| test.cpp:16:5:16:5 | identifier 'so_is_this' is undefined |
|
||||
| test.cpp:16:16:16:16 | expected a ')' |
|
||||
| test.cpp:16:16:16:16 | expected an expression |
|
||||
4
cpp/ql/test/library-tests/extraction_errors/diags.ql
Normal file
4
cpp/ql/test/library-tests/extraction_errors/diags.ql
Normal file
@@ -0,0 +1,4 @@
|
||||
import cpp
|
||||
|
||||
from Diagnostic d
|
||||
select d
|
||||
@@ -0,0 +1 @@
|
||||
| test.cpp:6:10:6:10 | x | This variable does not have a type. |
|
||||
@@ -0,0 +1,5 @@
|
||||
import cpp
|
||||
|
||||
from Variable v
|
||||
where v.getType() instanceof ErroneousType or not exists(v.getType())
|
||||
select v, "This variable does not have a type."
|
||||
@@ -0,0 +1,6 @@
|
||||
| test.c:3:6:3:6 | g |
|
||||
| test.c:4:13:4:13 | no_such_function |
|
||||
| test.cpp:5:6:5:25 | function_with_errors |
|
||||
| test.cpp:14:10:14:12 | fn2 |
|
||||
| test.h:2:5:2:5 | f |
|
||||
| test.h:2:5:2:5 | f |
|
||||
5
cpp/ql/test/library-tests/extraction_errors/functions.ql
Normal file
5
cpp/ql/test/library-tests/extraction_errors/functions.ql
Normal file
@@ -0,0 +1,5 @@
|
||||
import cpp
|
||||
|
||||
from Function fn
|
||||
where fn.fromSource()
|
||||
select fn
|
||||
5
cpp/ql/test/library-tests/extraction_errors/test.c
Normal file
5
cpp/ql/test/library-tests/extraction_errors/test.c
Normal file
@@ -0,0 +1,5 @@
|
||||
#include "test.h"
|
||||
|
||||
void g() {
|
||||
int x = no_such_function();
|
||||
}
|
||||
17
cpp/ql/test/library-tests/extraction_errors/test.cpp
Normal file
17
cpp/ql/test/library-tests/extraction_errors/test.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
// semmle-extractor-options: --expect_errors
|
||||
|
||||
#include "test.h"
|
||||
|
||||
void function_with_errors() {
|
||||
auto x = no_such_function();
|
||||
x+2;
|
||||
no_such_function();
|
||||
ADD(x+1, nsf2());
|
||||
f(1);
|
||||
f();
|
||||
}
|
||||
|
||||
uint32_t fn2() {
|
||||
this is a syntax error;
|
||||
so_is_this(;
|
||||
}
|
||||
2
cpp/ql/test/library-tests/extraction_errors/test.h
Normal file
2
cpp/ql/test/library-tests/extraction_errors/test.h
Normal file
@@ -0,0 +1,2 @@
|
||||
#define ADD(A,B) ((A)+(B))
|
||||
int f(int);
|
||||
@@ -0,0 +1,67 @@
|
||||
test1.cpp:
|
||||
# 3| int foo(int)
|
||||
# 3| Block 0
|
||||
# 3| v3_1(void) = EnterFunction :
|
||||
test2.cpp:
|
||||
# 1| v3_1(void) = EnterFunction :
|
||||
test1.cpp:
|
||||
# 3| mu3_2(unknown) = AliasedDefinition :
|
||||
test2.cpp:
|
||||
# 1| mu3_2(unknown) = AliasedDefinition :
|
||||
test1.cpp:
|
||||
# 3| mu3_3(unknown) = InitializeNonLocal :
|
||||
test2.cpp:
|
||||
# 1| mu3_3(unknown) = InitializeNonLocal :
|
||||
test1.cpp:
|
||||
# 3| r3_4(glval<int>) = VariableAddress[i] :
|
||||
test2.cpp:
|
||||
# 1| r3_4(glval<int>) = VariableAddress[i] :
|
||||
test1.cpp:
|
||||
# 3| mu3_5(int) = InitializeParameter[i] : &:r1_4, &:r3_4
|
||||
test2.cpp:
|
||||
# 1| mu3_5(int) = InitializeParameter[i] : &:r1_4, &:r3_4
|
||||
#-----| Goto -> Block 2
|
||||
#-----| Goto -> Block 2
|
||||
|
||||
# 1| Block 0
|
||||
#-----| Goto -> Block 2
|
||||
#-----| Goto -> Block 2
|
||||
|
||||
test1.cpp:
|
||||
# 3| Block 1
|
||||
# 3| r3_6(glval<int>) = VariableAddress[#return] :
|
||||
test2.cpp:
|
||||
# 1| r3_6(glval<int>) = VariableAddress[#return] :
|
||||
test1.cpp:
|
||||
# 3| v3_7(void) = ReturnValue : &:r1_6, &:r3_6, ~m?
|
||||
test2.cpp:
|
||||
# 1| v3_7(void) = ReturnValue : &:r1_6, &:r3_6, ~m?
|
||||
test1.cpp:
|
||||
# 3| v3_8(void) = AliasedUse : ~m?
|
||||
test2.cpp:
|
||||
# 1| v3_8(void) = AliasedUse : ~m?
|
||||
test1.cpp:
|
||||
# 3| v3_9(void) = ExitFunction :
|
||||
test2.cpp:
|
||||
# 1| v3_9(void) = ExitFunction :
|
||||
|
||||
# 1| Block 1
|
||||
|
||||
test1.cpp:
|
||||
# 4| Block 2
|
||||
# 4| r4_1(glval<int>) = VariableAddress[#return] :
|
||||
# 4| r4_2(int) = Constant[42] :
|
||||
# 4| mu4_3(int) = Store[#return] : &:r4_1, r4_2
|
||||
#-----| Goto -> Block 1
|
||||
#-----| Goto -> Block 1
|
||||
|
||||
test2.cpp:
|
||||
# 2| Block 2
|
||||
# 2| r2_1(glval<int>) = VariableAddress[#return] :
|
||||
# 2| r2_2(glval<int>) = VariableAddress[i] :
|
||||
# 2| r2_3(int) = Load[i] : &:r2_2, ~m?
|
||||
# 2| mu2_4(int) = Store[#return] : &:r2_1, r2_3
|
||||
#-----| Goto -> Block 1
|
||||
#-----| Goto -> Block 1
|
||||
|
||||
# 1| int foo(int)
|
||||
|
||||
@@ -70,4 +70,4 @@ options:
|
||||
description: >
|
||||
[EXPERIMENTAL] The value is a path to the MsBuild binary log file that should be extracted.
|
||||
This option only works when `--build-mode none` is also specified.
|
||||
type: string
|
||||
type: array
|
||||
|
||||
@@ -106,10 +106,10 @@ namespace Semmle.Extraction.CSharp
|
||||
var canonicalPathCache = CanonicalPathCache.Create(logger, 1000);
|
||||
var pathTransformer = new PathTransformer(canonicalPathCache);
|
||||
|
||||
if (options.BinaryLogPath is string binlogPath)
|
||||
if (options.BinaryLogPaths is string[] binlogPaths)
|
||||
{
|
||||
logger.LogInfo(" Running binary log analysis.");
|
||||
return RunBinaryLogAnalysis(analyzerStopwatch, options, binlogPath, logger, canonicalPathCache, pathTransformer);
|
||||
return RunBinaryLogAnalysis(analyzerStopwatch, options, binlogPaths, logger, canonicalPathCache, pathTransformer);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -124,6 +124,25 @@ namespace Semmle.Extraction.CSharp
|
||||
}
|
||||
}
|
||||
|
||||
private static ExitCode RunBinaryLogAnalysis(Stopwatch stopwatch, Options options, string[] binlogPaths, ILogger logger, CanonicalPathCache canonicalPathCache, PathTransformer pathTransformer)
|
||||
{
|
||||
var allFailed = true;
|
||||
foreach (var binlogPath in binlogPaths)
|
||||
{
|
||||
var exit = RunBinaryLogAnalysis(stopwatch, options, binlogPath, logger, canonicalPathCache, pathTransformer);
|
||||
switch (exit)
|
||||
{
|
||||
case ExitCode.Ok:
|
||||
case ExitCode.Errors:
|
||||
allFailed = false;
|
||||
break;
|
||||
case ExitCode.Failed:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return allFailed ? ExitCode.Failed : ExitCode.Ok;
|
||||
}
|
||||
|
||||
private static ExitCode RunBinaryLogAnalysis(Stopwatch stopwatch, Options options, string binlogPath, ILogger logger, CanonicalPathCache canonicalPathCache, PathTransformer pathTransformer)
|
||||
{
|
||||
logger.LogInfo($"Reading compiler calls from binary log {binlogPath}");
|
||||
@@ -190,11 +209,11 @@ namespace Semmle.Extraction.CSharp
|
||||
switch (exit)
|
||||
{
|
||||
case ExitCode.Ok:
|
||||
allFailed &= false;
|
||||
allFailed = false;
|
||||
logger.LogInfo($" Compilation {diagnosticName} succeeded");
|
||||
break;
|
||||
case ExitCode.Errors:
|
||||
allFailed &= false;
|
||||
allFailed = false;
|
||||
logger.LogWarning($" Compilation {diagnosticName} had errors");
|
||||
break;
|
||||
case ExitCode.Failed:
|
||||
|
||||
@@ -33,9 +33,9 @@ namespace Semmle.Extraction.CSharp
|
||||
public bool AssemblySensitiveTrap { get; private set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// The path to the binary log file, or null if unspecified.
|
||||
/// The paths to the binary log files, or null if unspecified.
|
||||
/// </summary>
|
||||
public string? BinaryLogPath { get; set; }
|
||||
public string[]? BinaryLogPaths { get; set; }
|
||||
|
||||
public static Options CreateWithEnvironment(string[] arguments)
|
||||
{
|
||||
@@ -71,7 +71,7 @@ namespace Semmle.Extraction.CSharp
|
||||
ProjectsToLoad.Add(value);
|
||||
return true;
|
||||
case "binlog":
|
||||
BinaryLogPath = value;
|
||||
BinaryLogPaths = value.Split(FileUtils.NewLineCharacters, StringSplitOptions.RemoveEmptyEntries);
|
||||
return true;
|
||||
default:
|
||||
return base.HandleOption(key, value);
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
| a/A.cs:0:0:0:0 | a/A.cs |
|
||||
| a/obj/Debug/net8.0/.NETCoreApp,Version=v8.0.AssemblyAttributes.cs:0:0:0:0 | a/obj/Debug/net8.0/.NETCoreApp,Version=v8.0.AssemblyAttributes.cs |
|
||||
| a/obj/Debug/net8.0/test.AssemblyInfo.cs:0:0:0:0 | a/obj/Debug/net8.0/test.AssemblyInfo.cs |
|
||||
| a/obj/Debug/net8.0/test.GlobalUsings.g.cs:0:0:0:0 | a/obj/Debug/net8.0/test.GlobalUsings.g.cs |
|
||||
| b/B.cs:0:0:0:0 | b/B.cs |
|
||||
| b/obj/Debug/net8.0/.NETCoreApp,Version=v8.0.AssemblyAttributes.cs:0:0:0:0 | b/obj/Debug/net8.0/.NETCoreApp,Version=v8.0.AssemblyAttributes.cs |
|
||||
| b/obj/Debug/net8.0/test.AssemblyInfo.cs:0:0:0:0 | b/obj/Debug/net8.0/test.AssemblyInfo.cs |
|
||||
| b/obj/Debug/net8.0/test.GlobalUsings.g.cs:0:0:0:0 | b/obj/Debug/net8.0/test.GlobalUsings.g.cs |
|
||||
| generated/a/test.csproj (net8.0)/System.Text.RegularExpressions.Generator/System.Text.RegularExpressions.Generator.RegexGenerator/RegexGenerator.g.cs:0:0:0:0 | generated/a/test.csproj (net8.0)/System.Text.RegularExpressions.Generator/System.Text.RegularExpressions.Generator.RegexGenerator/RegexGenerator.g.cs |
|
||||
| generated/b/test.csproj (net8.0)/System.Text.RegularExpressions.Generator/System.Text.RegularExpressions.Generator.RegexGenerator/RegexGenerator.g.cs:0:0:0:0 | generated/b/test.csproj (net8.0)/System.Text.RegularExpressions.Generator/System.Text.RegularExpressions.Generator.RegexGenerator/RegexGenerator.g.cs |
|
||||
@@ -0,0 +1,5 @@
|
||||
import csharp
|
||||
|
||||
from File f
|
||||
where f.fromSource()
|
||||
select f
|
||||
@@ -0,0 +1,9 @@
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
var dummy = "dummy";
|
||||
|
||||
partial class Test
|
||||
{
|
||||
[GeneratedRegex("abc|def", RegexOptions.IgnoreCase, "en-US")]
|
||||
private static partial Regex AbcOrDefGeneratedRegex();
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,9 @@
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
var dummy = "dummy";
|
||||
|
||||
partial class Test
|
||||
{
|
||||
[GeneratedRegex("abc|def", RegexOptions.IgnoreCase, "en-US")]
|
||||
private static partial Regex AbcOrDefGeneratedRegex();
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"markdownMessage": "C# analysis with build-mode 'none' completed.",
|
||||
"severity": "unknown",
|
||||
"source": {
|
||||
"extractorName": "csharp",
|
||||
"id": "csharp/autobuilder/buildless/complete",
|
||||
"name": "C# analysis with build-mode 'none' completed"
|
||||
},
|
||||
"visibility": {
|
||||
"cliSummaryTable": true,
|
||||
"statusPage": false,
|
||||
"telemetry": true
|
||||
}
|
||||
}
|
||||
{
|
||||
"markdownMessage": "C# was extracted with build-mode set to 'none'. This means that all C# source in the working directory will be scanned, with build tools, such as Nuget and Dotnet CLIs, only contributing information about external dependencies.",
|
||||
"severity": "note",
|
||||
"source": {
|
||||
"extractorName": "csharp",
|
||||
"id": "csharp/autobuilder/buildless/mode-active",
|
||||
"name": "C# was extracted with build-mode set to 'none'"
|
||||
},
|
||||
"visibility": {
|
||||
"cliSummaryTable": true,
|
||||
"statusPage": true,
|
||||
"telemetry": true
|
||||
}
|
||||
}
|
||||
{
|
||||
"markdownMessage": "C# was extracted with the experimental 'binlog' option.",
|
||||
"severity": "note",
|
||||
"source": {
|
||||
"extractorName": "csharp",
|
||||
"id": "csharp/autobuilder/buildless/binlog",
|
||||
"name": "C# was extracted with the experimental 'binlog' option"
|
||||
},
|
||||
"visibility": {
|
||||
"cliSummaryTable": true,
|
||||
"statusPage": true,
|
||||
"telemetry": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"sdk": {
|
||||
"version": "8.0.101"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import commands
|
||||
|
||||
|
||||
def test(codeql, csharp):
|
||||
commands.run(["dotnet", "build", "a/test.csproj", "/bl:a.binlog"])
|
||||
commands.run(["dotnet", "build", "b/test.csproj", "/bl:b.binlog"])
|
||||
codeql.database.create(build_mode="none", extractor_option=["binlog=a.binlog", "binlog=b.binlog"])
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: fix
|
||||
---
|
||||
* The behaviour of the `subtypes` column in models-as-data now matches other languages more closely.
|
||||
@@ -25,8 +25,12 @@
|
||||
* packages in the group `<groupname>` according to the `packageGrouping`
|
||||
* predicate.
|
||||
* 2. The `type` column selects a type within that package.
|
||||
* 3. The `subtypes` is a boolean that indicates whether to jump to an
|
||||
* arbitrary subtype of that type.
|
||||
* 3. The `subtypes` column is a boolean that controls what restrictions we
|
||||
* place on the type `t` of the selector base when accessing a field or
|
||||
* calling a method. When it is false, `t` must be the exact type specified
|
||||
* by this row. When it is true, `t` may be a type which embeds the specified
|
||||
* type, and for interface methods `t` may be a type which implements the
|
||||
* interface.
|
||||
* 4. The `name` column optionally selects a specific named member of the type.
|
||||
* 5. The `signature` column is always empty.
|
||||
* 6. The `ext` column is always empty.
|
||||
@@ -470,17 +474,24 @@ SourceSinkInterpretationInput::SourceOrSinkElement interpretElement(
|
||||
elementSpec(pkg, type, subtypes, name, signature, ext) and
|
||||
// Go does not need to distinguish functions with signature
|
||||
signature = "" and
|
||||
(
|
||||
exists(Field f | f.hasQualifiedName(interpretPackage(pkg), type, name) | result.asEntity() = f)
|
||||
exists(string p | p = interpretPackage(pkg) |
|
||||
exists(Entity e | result.hasFullInfo(e, p, type, subtypes) |
|
||||
e.(Field).hasQualifiedName(p, type, name) or
|
||||
e.(Method).hasQualifiedName(p, type, name)
|
||||
)
|
||||
or
|
||||
exists(Method m | m.hasQualifiedName(interpretPackage(pkg), type, name) |
|
||||
result.asEntity() = m
|
||||
or
|
||||
subtypes = true and result.asEntity().(Method).implementsIncludingInterfaceMethods(m)
|
||||
subtypes = true and
|
||||
// p.type is an interface and we include types which implement it
|
||||
exists(Method m2, string pkg2, string type2 |
|
||||
m2.getReceiverType().implements(p, type) and
|
||||
m2.getName() = name and
|
||||
m2.getReceiverBaseType().hasQualifiedName(pkg2, type2)
|
||||
|
|
||||
result.hasFullInfo(m2, pkg2, type2, subtypes)
|
||||
)
|
||||
or
|
||||
type = "" and
|
||||
exists(Entity e | e.hasQualifiedName(interpretPackage(pkg), name) | result.asEntity() = e)
|
||||
exists(Entity e | e.hasQualifiedName(p, name) | result.asOtherEntity() = e)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -149,21 +149,63 @@ module SourceSinkInterpretationInput implements
|
||||
)
|
||||
}
|
||||
|
||||
// Note that due to embedding, which is currently implemented via some
|
||||
// Methods having multiple qualified names, a given Method is liable to have
|
||||
// more than one SourceOrSinkElement, one for each of the names it claims.
|
||||
private newtype TSourceOrSinkElement =
|
||||
TEntityElement(Entity e) or
|
||||
TMethodEntityElement(Method m, string pkg, string type, boolean subtypes) {
|
||||
m.hasQualifiedName(pkg, type, _) and
|
||||
subtypes = [true, false]
|
||||
} or
|
||||
TFieldEntityElement(Field f, string pkg, string type, boolean subtypes) {
|
||||
f.hasQualifiedName(pkg, type, _) and
|
||||
subtypes = [true, false]
|
||||
} or
|
||||
TOtherEntityElement(Entity e) {
|
||||
not e instanceof Method and
|
||||
not e instanceof Field
|
||||
} or
|
||||
TAstElement(AstNode n)
|
||||
|
||||
/** An element representable by CSV modeling. */
|
||||
class SourceOrSinkElement extends TSourceOrSinkElement {
|
||||
/** Gets this source or sink element as an entity, if it is one. */
|
||||
Entity asEntity() { this = TEntityElement(result) }
|
||||
Entity asEntity() {
|
||||
result = [this.asMethodEntity(), this.asFieldEntity(), this.asOtherEntity()]
|
||||
}
|
||||
|
||||
/** Gets this source or sink element as a method, if it is one. */
|
||||
Method asMethodEntity() { this = TMethodEntityElement(result, _, _, _) }
|
||||
|
||||
/** Gets this source or sink element as a field, if it is one. */
|
||||
Field asFieldEntity() { this = TFieldEntityElement(result, _, _, _) }
|
||||
|
||||
/** Gets this source or sink element as an entity which isn't a field or method, if it is one. */
|
||||
Entity asOtherEntity() { this = TOtherEntityElement(result) }
|
||||
|
||||
/** Gets this source or sink element as an AST node, if it is one. */
|
||||
AstNode asAstNode() { this = TAstElement(result) }
|
||||
|
||||
/**
|
||||
* Holds if this source or sink element is a method or field that was specified
|
||||
* with the given values for `e`, `pkg`, `type` and `subtypes`.
|
||||
*/
|
||||
predicate hasFullInfo(Entity e, string pkg, string type, boolean subtypes) {
|
||||
this = TMethodEntityElement(e, pkg, type, subtypes) or
|
||||
this = TFieldEntityElement(e, pkg, type, subtypes)
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this source or sink element. */
|
||||
string toString() {
|
||||
(this instanceof TOtherEntityElement or this instanceof TAstElement) and
|
||||
result = "element representing " + [this.asEntity().toString(), this.asAstNode().toString()]
|
||||
or
|
||||
exists(Entity e, string pkg, string name, boolean subtypes |
|
||||
this.hasFullInfo(e, pkg, name, subtypes) and
|
||||
result =
|
||||
"element representing " + e.toString() + " with receiver type " + pkg + "." + name +
|
||||
" and subtypes=" + subtypes
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the location of this element. */
|
||||
@@ -203,7 +245,17 @@ module SourceSinkInterpretationInput implements
|
||||
|
||||
/** Gets the target of this call, if any. */
|
||||
SourceOrSinkElement getCallTarget() {
|
||||
result.asEntity() = this.asCall().getNode().(DataFlow::CallNode).getTarget()
|
||||
exists(DataFlow::CallNode cn, Function callTarget |
|
||||
cn = this.asCall().getNode() and
|
||||
callTarget = cn.getTarget()
|
||||
|
|
||||
(
|
||||
result.asOtherEntity() = callTarget
|
||||
or
|
||||
callTarget instanceof Method and
|
||||
result = getElementWithQualifier(callTarget, cn.getReceiver())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this node. */
|
||||
@@ -228,6 +280,105 @@ module SourceSinkInterpretationInput implements
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a method or field spec for `e` which applies in the context of
|
||||
* qualifier `qual`.
|
||||
*
|
||||
* Note that naively checking `e`'s qualified name is not correct, because
|
||||
* `Method`s and `Field`s may have multiple qualified names due to embedding.
|
||||
* We must instead check that the package and type name given by
|
||||
* `result.hasFullInfo` refer to either `qual`'s type or to a type it embeds.
|
||||
*/
|
||||
bindingset[e, qual]
|
||||
pragma[inline_late]
|
||||
private SourceOrSinkElement getElementWithQualifier(Entity e, DataFlow::Node qual) {
|
||||
exists(boolean subtypes, Type syntacticQualBaseType, Type targetType |
|
||||
syntacticQualBaseType = getSyntacticQualifierBaseType(qual) and
|
||||
result = constructElement(e, targetType, subtypes)
|
||||
|
|
||||
subtypes = [true, false] and
|
||||
syntacticQualBaseType = targetType
|
||||
or
|
||||
subtypes = true and
|
||||
(
|
||||
// `syntacticQualBaseType`'s underlying type might be an interface type and `sse`
|
||||
// might refer to a method defined on an interface embedded within it.
|
||||
targetType =
|
||||
syntacticQualBaseType.getUnderlyingType().(InterfaceType).getAnEmbeddedInterface()
|
||||
or
|
||||
// `syntacticQualBaseType`'s underlying type might be a struct type and `sse`
|
||||
// might be a promoted method or field in it.
|
||||
targetType = getAnIntermediateEmbeddedType(e, syntacticQualBaseType.getUnderlyingType())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
bindingset[e, targetType, subtypes]
|
||||
pragma[inline_late]
|
||||
private SourceOrSinkElement constructElement(Entity e, Type targetType, boolean subtypes) {
|
||||
exists(string pkg, string typename |
|
||||
targetType.hasQualifiedName(pkg, typename) and
|
||||
result.hasFullInfo(e, pkg, typename, subtypes)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of an embedded field of `st` which is on the path to `e`,
|
||||
* which is a promoted method or field of `st`, or its base type if it's a
|
||||
* pointer type.
|
||||
*/
|
||||
private Type getAnIntermediateEmbeddedType(Entity e, StructType st) {
|
||||
exists(Field field1, Field field2, int depth1, int depth2, Type t2 |
|
||||
field1 = st.getFieldAtDepth(_, depth1) and
|
||||
field2 = st.getFieldAtDepth(_, depth2) and
|
||||
result = lookThroughPointerType(field1.getType()) and
|
||||
t2 = lookThroughPointerType(field2.getType()) and
|
||||
(
|
||||
field1 = field2
|
||||
or
|
||||
field2 = result.getUnderlyingType().(StructType).getFieldAtDepth(_, depth2 - depth1 - 1)
|
||||
)
|
||||
|
|
||||
e.(Method).getReceiverBaseType() = t2
|
||||
or
|
||||
e.(Field).getDeclaringType() = t2.getUnderlyingType()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the base type of `underlying`, where `n` is of the form
|
||||
* `implicitDeref?(underlying.implicitFieldRead1.implicitFieldRead2...)`
|
||||
*
|
||||
* For Go syntax like `qualifier.method()` or `qualifier.field`, this is the type of `qualifier`, before any
|
||||
* implicit dereference is interposed because `qualifier` is of pointer type, or implicit field accesses
|
||||
* navigate to any embedded struct types that truly host `field`.
|
||||
*/
|
||||
private Type getSyntacticQualifierBaseType(DataFlow::Node n) {
|
||||
exists(DataFlow::Node n2 |
|
||||
// look through implicit dereference, if there is one
|
||||
not exists(n.asInstruction().(IR::EvalImplicitDerefInstruction).getOperand()) and
|
||||
n2 = n
|
||||
or
|
||||
n2.asExpr() = n.asInstruction().(IR::EvalImplicitDerefInstruction).getOperand()
|
||||
|
|
||||
result = lookThroughPointerType(skipImplicitFieldReads(n2).getType())
|
||||
)
|
||||
}
|
||||
|
||||
private DataFlow::Node skipImplicitFieldReads(DataFlow::Node n) {
|
||||
not exists(lookThroughImplicitFieldRead(n)) and result = n
|
||||
or
|
||||
result = skipImplicitFieldReads(lookThroughImplicitFieldRead(n))
|
||||
}
|
||||
|
||||
private DataFlow::Node lookThroughImplicitFieldRead(DataFlow::Node n) {
|
||||
result.asInstruction() =
|
||||
n.(DataFlow::InstructionNode)
|
||||
.asInstruction()
|
||||
.(IR::ImplicitFieldReadInstruction)
|
||||
.getBaseInstruction()
|
||||
}
|
||||
|
||||
/** Provides additional sink specification logic. */
|
||||
bindingset[c]
|
||||
predicate interpretOutput(string c, InterpretNode mid, InterpretNode node) {
|
||||
@@ -242,10 +393,12 @@ module SourceSinkInterpretationInput implements
|
||||
e = mid.asElement()
|
||||
|
|
||||
(c = "Parameter" or c = "") and
|
||||
node.asNode().asParameter() = e.asEntity()
|
||||
n.asParameter() = pragma[only_bind_into](e).asEntity()
|
||||
or
|
||||
c = "" and
|
||||
n.(DataFlow::FieldReadNode).getField() = e.asEntity()
|
||||
exists(DataFlow::FieldReadNode frn | frn = n |
|
||||
c = "" and
|
||||
pragma[only_bind_into](e) = getElementWithQualifier(frn.getField(), frn.getBase())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -259,10 +412,13 @@ module SourceSinkInterpretationInput implements
|
||||
mid.asCallable() = getNodeEnclosingCallable(ret)
|
||||
)
|
||||
or
|
||||
exists(DataFlow::Write fw, Field f |
|
||||
exists(SourceOrSinkElement e, DataFlow::Write fw, DataFlow::Node base, Field f |
|
||||
e = mid.asElement() and
|
||||
f = e.asFieldEntity()
|
||||
|
|
||||
c = "" and
|
||||
f = mid.asElement().asEntity() and
|
||||
fw.writesField(_, f, node.asNode())
|
||||
fw.writesField(base, f, node.asNode()) and
|
||||
pragma[only_bind_into](e) = getElementWithQualifier(f, base)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "I1", False, "Source", "", "", "ReturnValue", "remote", "manual"]
|
||||
- ["github.com/nonexistent/test", "I1", False, "Source", "", "", "ReturnValue", "qltest", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: summaryModel
|
||||
@@ -13,4 +13,4 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "I1", False, "Sink", "", "", "Argument[0]", "path-injection", "manual"]
|
||||
- ["github.com/nonexistent/test", "I1", False, "Sink", "", "", "Argument[0]", "qltest", "manual"]
|
||||
@@ -1,14 +1,12 @@
|
||||
import go
|
||||
import semmle.go.dataflow.ExternalFlow
|
||||
import ModelValidation
|
||||
import semmle.go.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import MakeTest<FlowTest>
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
predicate isSource(DataFlow::Node source) { sourceNode(source, "qltest") }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink = any(FileSystemAccess fsa).getAPathArgument() }
|
||||
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "qltest") }
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
@@ -3,7 +3,7 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "I1", True, "Source", "", "", "ReturnValue", "remote", "manual"]
|
||||
- ["github.com/nonexistent/test", "I1", True, "Source", "", "", "ReturnValue", "qltest", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: summaryModel
|
||||
@@ -13,4 +13,4 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "I1", True, "Sink", "", "", "Argument[0]", "path-injection", "manual"]
|
||||
- ["github.com/nonexistent/test", "I1", True, "Sink", "", "", "Argument[0]", "qltest", "manual"]
|
||||
@@ -1,14 +1,12 @@
|
||||
import go
|
||||
import semmle.go.dataflow.ExternalFlow
|
||||
import ModelValidation
|
||||
import semmle.go.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import MakeTest<FlowTest>
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
predicate isSource(DataFlow::Node source) { sourceNode(source, "qltest") }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink = any(FileSystemAccess fsa).getAPathArgument() }
|
||||
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "qltest") }
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
@@ -3,7 +3,7 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "I2", False, "Source", "", "", "ReturnValue", "remote", "manual"]
|
||||
- ["github.com/nonexistent/test", "I2", False, "Source", "", "", "ReturnValue", "qltest", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: summaryModel
|
||||
@@ -13,4 +13,4 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "I2", False, "Sink", "", "", "Argument[0]", "path-injection", "manual"]
|
||||
- ["github.com/nonexistent/test", "I2", False, "Sink", "", "", "Argument[0]", "qltest", "manual"]
|
||||
@@ -1,14 +1,12 @@
|
||||
import go
|
||||
import semmle.go.dataflow.ExternalFlow
|
||||
import ModelValidation
|
||||
import semmle.go.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import MakeTest<FlowTest>
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
predicate isSource(DataFlow::Node source) { sourceNode(source, "qltest") }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink = any(FileSystemAccess fsa).getAPathArgument() }
|
||||
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "qltest") }
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
@@ -3,7 +3,7 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "I2", True, "Source", "", "", "ReturnValue", "remote", "manual"]
|
||||
- ["github.com/nonexistent/test", "I2", True, "Source", "", "", "ReturnValue", "qltest", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: summaryModel
|
||||
@@ -13,4 +13,4 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "I2", True, "Sink", "", "", "Argument[0]", "path-injection", "manual"]
|
||||
- ["github.com/nonexistent/test", "I2", True, "Sink", "", "", "Argument[0]", "qltest", "manual"]
|
||||
@@ -1,14 +1,12 @@
|
||||
import go
|
||||
import semmle.go.dataflow.ExternalFlow
|
||||
import ModelValidation
|
||||
import semmle.go.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import MakeTest<FlowTest>
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
predicate isSource(DataFlow::Node source) { sourceNode(source, "qltest") }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink = any(FileSystemAccess fsa).getAPathArgument() }
|
||||
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "qltest") }
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
@@ -3,7 +3,7 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "IEmbedI1", True, "Source", "", "", "ReturnValue", "remote", "manual"]
|
||||
- ["github.com/nonexistent/test", "IEmbedI1", True, "Source", "", "", "ReturnValue", "qltest", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: summaryModel
|
||||
@@ -13,4 +13,4 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "IEmbedI1", True, "Sink", "", "", "Argument[0]", "path-injection", "manual"]
|
||||
- ["github.com/nonexistent/test", "IEmbedI1", True, "Sink", "", "", "Argument[0]", "qltest", "manual"]
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import go
|
||||
import semmle.go.dataflow.ExternalFlow
|
||||
import ModelValidation
|
||||
import semmle.go.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import MakeTest<FlowTest>
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
predicate isSource(DataFlow::Node source) { sourceNode(source, "qltest") }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink = any(FileSystemAccess fsa).getAPathArgument() }
|
||||
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "qltest") }
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
|
||||
@@ -3,7 +3,7 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "IEmbedI2", True, "Source", "", "", "ReturnValue", "remote", "manual"]
|
||||
- ["github.com/nonexistent/test", "IEmbedI2", True, "Source", "", "", "ReturnValue", "qltest", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: summaryModel
|
||||
@@ -13,4 +13,4 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "IEmbedI2", True, "Sink", "", "", "Argument[0]", "path-injection", "manual"]
|
||||
- ["github.com/nonexistent/test", "IEmbedI2", True, "Sink", "", "", "Argument[0]", "qltest", "manual"]
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import go
|
||||
import semmle.go.dataflow.ExternalFlow
|
||||
import ModelValidation
|
||||
import semmle.go.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import MakeTest<FlowTest>
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
predicate isSource(DataFlow::Node source) { sourceNode(source, "qltest") }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink = any(FileSystemAccess fsa).getAPathArgument() }
|
||||
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "qltest") }
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "PImplEmbedI1", True, "Source", "", "", "ReturnValue", "qltest", "manual"]
|
||||
- ["github.com/nonexistent/test", "PImplEmbedI1", True, "SourceField", "", "", "", "qltest", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: summaryModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "PImplEmbedI1", True, "Step", "", "", "Argument[0]", "ReturnValue", "value", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "PImplEmbedI1", True, "Sink", "", "", "Argument[0]", "qltest", "manual"]
|
||||
- ["github.com/nonexistent/test", "PImplEmbedI1", True, "SinkField", "", "", "", "qltest", "manual"]
|
||||
@@ -0,0 +1,26 @@
|
||||
import go
|
||||
import ModelValidation
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import MakeTest<FlowTest>
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { sourceNode(source, "qltest") }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "qltest") }
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
|
||||
module FlowTest implements TestSig {
|
||||
string getARelevantTag() { result = "PImplEmbedI1[t]" }
|
||||
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
tag = "PImplEmbedI1[t]" and
|
||||
exists(DataFlow::Node sink | Flow::flowTo(sink) |
|
||||
sink.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(),
|
||||
location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and
|
||||
element = sink.toString() and
|
||||
value = ""
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "PImplEmbedI2", True, "Source", "", "", "ReturnValue", "qltest", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: summaryModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "PImplEmbedI2", True, "Step", "", "", "Argument[0]", "ReturnValue", "value", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "PImplEmbedI2", True, "Sink", "", "", "Argument[0]", "qltest", "manual"]
|
||||
@@ -0,0 +1,26 @@
|
||||
import go
|
||||
import ModelValidation
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import MakeTest<FlowTest>
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { sourceNode(source, "qltest") }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "qltest") }
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
|
||||
module FlowTest implements TestSig {
|
||||
string getARelevantTag() { result = "PImplEmbedI2[t]" }
|
||||
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
tag = "PImplEmbedI2[t]" and
|
||||
exists(DataFlow::Node sink | Flow::flowTo(sink) |
|
||||
sink.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(),
|
||||
location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and
|
||||
element = sink.toString() and
|
||||
value = ""
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
testFailures
|
||||
invalidModelRow
|
||||
failures
|
||||
@@ -3,7 +3,8 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "S1", False, "Source", "", "", "ReturnValue", "remote", "manual"]
|
||||
- ["github.com/nonexistent/test", "S1", False, "Source", "", "", "ReturnValue", "qltest", "manual"]
|
||||
- ["github.com/nonexistent/test", "S1", False, "SourceField", "", "", "", "qltest", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: summaryModel
|
||||
@@ -13,4 +14,5 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "S1", False, "Sink", "", "", "Argument[0]", "path-injection", "manual"]
|
||||
- ["github.com/nonexistent/test", "S1", False, "Sink", "", "", "Argument[0]", "qltest", "manual"]
|
||||
- ["github.com/nonexistent/test", "S1", False, "SinkField", "", "", "", "qltest", "manual"]
|
||||
@@ -1,14 +1,12 @@
|
||||
import go
|
||||
import semmle.go.dataflow.ExternalFlow
|
||||
import ModelValidation
|
||||
import semmle.go.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import MakeTest<FlowTest>
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
predicate isSource(DataFlow::Node source) { sourceNode(source, "qltest") }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink = any(FileSystemAccess fsa).getAPathArgument() }
|
||||
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "qltest") }
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
@@ -0,0 +1,3 @@
|
||||
testFailures
|
||||
invalidModelRow
|
||||
failures
|
||||
@@ -3,7 +3,8 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "S1", True, "Source", "", "", "ReturnValue", "remote", "manual"]
|
||||
- ["github.com/nonexistent/test", "S1", True, "Source", "", "", "ReturnValue", "qltest", "manual"]
|
||||
- ["github.com/nonexistent/test", "S1", True, "SourceField", "", "", "", "qltest", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: summaryModel
|
||||
@@ -13,4 +14,5 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "S1", True, "Sink", "", "", "Argument[0]", "path-injection", "manual"]
|
||||
- ["github.com/nonexistent/test", "S1", True, "Sink", "", "", "Argument[0]", "qltest", "manual"]
|
||||
- ["github.com/nonexistent/test", "S1", True, "SinkField", "", "", "", "qltest", "manual"]
|
||||
@@ -1,14 +1,12 @@
|
||||
import go
|
||||
import semmle.go.dataflow.ExternalFlow
|
||||
import ModelValidation
|
||||
import semmle.go.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import MakeTest<FlowTest>
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
predicate isSource(DataFlow::Node source) { sourceNode(source, "qltest") }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink = any(FileSystemAccess fsa).getAPathArgument() }
|
||||
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "qltest") }
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
@@ -3,7 +3,8 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SEmbedI1", True, "Source", "", "", "ReturnValue", "remote", "manual"]
|
||||
- ["github.com/nonexistent/test", "SEmbedI1", True, "Source", "", "", "ReturnValue", "qltest", "manual"]
|
||||
- ["github.com/nonexistent/test", "SEmbedI1", True, "SourceField", "", "", "", "qltest", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: summaryModel
|
||||
@@ -13,4 +14,5 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SEmbedI1", True, "Sink", "", "", "Argument[0]", "path-injection", "manual"]
|
||||
- ["github.com/nonexistent/test", "SEmbedI1", True, "Sink", "", "", "Argument[0]", "qltest", "manual"]
|
||||
- ["github.com/nonexistent/test", "SEmbedI1", True, "SinkField", "", "", "", "qltest", "manual"]
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import go
|
||||
import semmle.go.dataflow.ExternalFlow
|
||||
import ModelValidation
|
||||
import semmle.go.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import MakeTest<FlowTest>
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
predicate isSource(DataFlow::Node source) { sourceNode(source, "qltest") }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink = any(FileSystemAccess fsa).getAPathArgument() }
|
||||
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "qltest") }
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
|
||||
@@ -3,7 +3,7 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SEmbedI2", True, "Source", "", "", "ReturnValue", "remote", "manual"]
|
||||
- ["github.com/nonexistent/test", "SEmbedI2", True, "Source", "", "", "ReturnValue", "qltest", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: summaryModel
|
||||
@@ -13,4 +13,4 @@ extensions:
|
||||
pack: codeql/go-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SEmbedI2", True, "Sink", "", "", "Argument[0]", "path-injection", "manual"]
|
||||
- ["github.com/nonexistent/test", "SEmbedI2", True, "Sink", "", "", "Argument[0]", "qltest", "manual"]
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import go
|
||||
import semmle.go.dataflow.ExternalFlow
|
||||
import ModelValidation
|
||||
import semmle.go.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import MakeTest<FlowTest>
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
predicate isSource(DataFlow::Node source) { sourceNode(source, "qltest") }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sink = any(FileSystemAccess fsa).getAPathArgument() }
|
||||
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "qltest") }
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
testFailures
|
||||
invalidModelRow
|
||||
failures
|
||||
@@ -0,0 +1,18 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SEmbedP1", True, "Source", "", "", "ReturnValue", "qltest", "manual"]
|
||||
- ["github.com/nonexistent/test", "SEmbedP1", True, "SourceField", "", "", "", "qltest", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: summaryModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SEmbedP1", True, "Step", "", "", "Argument[0]", "ReturnValue", "value", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SEmbedP1", True, "Sink", "", "", "Argument[0]", "qltest", "manual"]
|
||||
- ["github.com/nonexistent/test", "SEmbedP1", True, "SinkField", "", "", "", "qltest", "manual"]
|
||||
@@ -0,0 +1,26 @@
|
||||
import go
|
||||
import ModelValidation
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import MakeTest<FlowTest>
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { sourceNode(source, "qltest") }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "qltest") }
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
|
||||
module FlowTest implements TestSig {
|
||||
string getARelevantTag() { result = "SEmbedP1[t]" }
|
||||
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
tag = "SEmbedP1[t]" and
|
||||
exists(DataFlow::Node sink | Flow::flowTo(sink) |
|
||||
sink.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(),
|
||||
location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and
|
||||
element = sink.toString() and
|
||||
value = ""
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
testFailures
|
||||
invalidModelRow
|
||||
failures
|
||||
@@ -0,0 +1,16 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SEmbedP2", True, "Source", "", "", "ReturnValue", "qltest", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: summaryModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SEmbedP2", True, "Step", "", "", "Argument[0]", "ReturnValue", "value", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SEmbedP2", True, "Sink", "", "", "Argument[0]", "qltest", "manual"]
|
||||
@@ -0,0 +1,26 @@
|
||||
import go
|
||||
import ModelValidation
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
import MakeTest<FlowTest>
|
||||
|
||||
module Config implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) { sourceNode(source, "qltest") }
|
||||
|
||||
predicate isSink(DataFlow::Node sink) { sinkNode(sink, "qltest") }
|
||||
}
|
||||
|
||||
module Flow = TaintTracking::Global<Config>;
|
||||
|
||||
module FlowTest implements TestSig {
|
||||
string getARelevantTag() { result = "SEmbedP2[t]" }
|
||||
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
tag = "SEmbedP2[t]" and
|
||||
exists(DataFlow::Node sink | Flow::flowTo(sink) |
|
||||
sink.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(),
|
||||
location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and
|
||||
element = sink.toString() and
|
||||
value = ""
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
testFailures
|
||||
invalidModelRow
|
||||
failures
|
||||
@@ -0,0 +1,18 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SEmbedPtrP1", True, "Source", "", "", "ReturnValue", "qltest", "manual"]
|
||||
- ["github.com/nonexistent/test", "SEmbedPtrP1", True, "SourceField", "", "", "", "qltest", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: summaryModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SEmbedPtrP1", True, "Step", "", "", "Argument[0]", "ReturnValue", "value", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/go-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["github.com/nonexistent/test", "SEmbedPtrP1", True, "Sink", "", "", "Argument[0]", "qltest", "manual"]
|
||||
- ["github.com/nonexistent/test", "SEmbedPtrP1", True, "SinkField", "", "", "", "qltest", "manual"]
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user