Merge branch 'main' into rust-df-patterns

This commit is contained in:
Simon Friis Vindum
2024-11-13 16:22:07 +01:00
162 changed files with 2919 additions and 882 deletions

View File

@@ -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

View File

@@ -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/

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 }}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 }}

View File

@@ -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

View File

@@ -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/**

View File

@@ -196,8 +196,6 @@ private predicate isInvalidFunction(Function func) {
expr.getEnclosingFunction() = func and
not exists(expr.getType())
)
or
count(func.getEntryPoint().getLocation()) > 1
}
/**

View 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()

View 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()

View 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)
}
}

View 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()

View 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) }
}
}

View 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()

View 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()

View File

@@ -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 |

View File

@@ -0,0 +1 @@
experimental/Best Practices/GuardedFree.ql

View File

@@ -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);
}

View File

@@ -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 |

View File

@@ -0,0 +1 @@
Telemetry/CompilerErrors.ql

View File

@@ -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 |

View File

@@ -0,0 +1 @@
Telemetry/DatabaseQuality.ql

View File

@@ -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> |

View File

@@ -0,0 +1,4 @@
import cpp
from ErrorExpr e
select e.getParent(), e

View File

@@ -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 |

View File

@@ -0,0 +1 @@
Telemetry/ExtractionMetrics.ql

View File

@@ -0,0 +1 @@
| "test.h" | 2 |

View File

@@ -0,0 +1 @@
Telemetry/SucceededIncludes.ql

View 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 |

View File

@@ -0,0 +1,4 @@
import cpp
from Diagnostic d
select d

View File

@@ -0,0 +1 @@
| test.cpp:6:10:6:10 | x | This variable does not have a type. |

View File

@@ -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."

View File

@@ -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 |

View File

@@ -0,0 +1,5 @@
import cpp
from Function fn
where fn.fromSource()
select fn

View File

@@ -0,0 +1,5 @@
#include "test.h"
void g() {
int x = no_such_function();
}

View 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(;
}

View File

@@ -0,0 +1,2 @@
#define ADD(A,B) ((A)+(B))
int f(int);

View File

@@ -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)

View File

@@ -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

View File

@@ -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:

View File

@@ -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);

View File

@@ -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 |

View File

@@ -0,0 +1,5 @@
import csharp
from File f
where f.fromSource()
select f

View File

@@ -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();
}

View File

@@ -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>

View File

@@ -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();
}

View File

@@ -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>

View File

@@ -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
}
}

View File

@@ -0,0 +1,5 @@
{
"sdk": {
"version": "8.0.101"
}
}

View File

@@ -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"])

View File

@@ -0,0 +1,4 @@
---
category: fix
---
* The behaviour of the `subtypes` column in models-as-data now matches other languages more closely.

View File

@@ -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)
)
}

View File

@@ -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)
)
}
}

View File

@@ -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"]

View File

@@ -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>;

View File

@@ -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"]

View File

@@ -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>;

View File

@@ -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"]

View File

@@ -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>;

View File

@@ -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"]

View File

@@ -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>;

View File

@@ -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"]

View File

@@ -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>;

View File

@@ -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"]

View File

@@ -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>;

View File

@@ -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"]

View File

@@ -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 = ""
)
}
}

View File

@@ -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"]

View File

@@ -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 = ""
)
}
}

View File

@@ -0,0 +1,3 @@
testFailures
invalidModelRow
failures

View File

@@ -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"]

View File

@@ -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>;

View File

@@ -0,0 +1,3 @@
testFailures
invalidModelRow
failures

View File

@@ -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"]

View File

@@ -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>;

View File

@@ -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"]

View File

@@ -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>;

View File

@@ -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"]

View File

@@ -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>;

View File

@@ -0,0 +1,3 @@
testFailures
invalidModelRow
failures

View File

@@ -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"]

View File

@@ -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 = ""
)
}
}

View File

@@ -0,0 +1,3 @@
testFailures
invalidModelRow
failures

View File

@@ -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"]

View File

@@ -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 = ""
)
}
}

View File

@@ -0,0 +1,3 @@
testFailures
invalidModelRow
failures

View File

@@ -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