diff --git a/change-notes/1.25/analysis-cpp.md b/change-notes/1.25/analysis-cpp.md index d282441b092..43a5d9e9f8d 100644 --- a/change-notes/1.25/analysis-cpp.md +++ b/change-notes/1.25/analysis-cpp.md @@ -16,6 +16,7 @@ The following changes in version 1.25 affect C/C++ analysis in all applications. ## Changes to libraries +* The library `VCS.qll` and all queries that imported it have been removed. * The data-flow library has been improved, which affects most security queries by potentially adding more results. Flow through functions now takes nested field reads/writes into account. For example, the library is able to track flow from `taint()` to `sink()` via the method @@ -39,3 +40,5 @@ The following changes in version 1.25 affect C/C++ analysis in all applications. } }; ``` +* The security pack taint tracking library (`semmle.code.cpp.security.TaintTracking`) now considers that equality checks may block the flow of taint. This results in fewer false positive results from queries that use this library. + diff --git a/change-notes/1.25/analysis-javascript.md b/change-notes/1.25/analysis-javascript.md index 0eb939d6801..385dc8d3d6e 100644 --- a/change-notes/1.25/analysis-javascript.md +++ b/change-notes/1.25/analysis-javascript.md @@ -3,8 +3,10 @@ ## General improvements * Support for the following frameworks and libraries has been improved: + - [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) - [bluebird](http://bluebirdjs.com/) - [express](https://www.npmjs.com/package/express) + - [fastify](https://www.npmjs.com/package/fastify) - [fstream](https://www.npmjs.com/package/fstream) - [jGrowl](https://github.com/stanlemon/jGrowl) - [jQuery](https://jquery.com/) @@ -13,12 +15,11 @@ - [mssql](https://www.npmjs.com/package/mssql) - [mysql](https://www.npmjs.com/package/mysql) - [pg](https://www.npmjs.com/package/pg) - - [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) - [sequelize](https://www.npmjs.com/package/sequelize) - [spanner](https://www.npmjs.com/package/spanner) - [sqlite](https://www.npmjs.com/package/sqlite) - - [ssh2](https://www.npmjs.com/package/ssh2) - [ssh2-streams](https://www.npmjs.com/package/ssh2-streams) + - [ssh2](https://www.npmjs.com/package/ssh2) * TypeScript 3.9 is now supported. @@ -35,41 +36,43 @@ | **Query** | **Expected impact** | **Change** | |--------------------------------|------------------------------|---------------------------------------------------------------------------| -| Misspelled variable name (`js/misspelled-variable-name`) | Message changed | The message for this query now correctly identifies the misspelled variable in additional cases. | -| Uncontrolled data used in path expression (`js/path-injection`) | More results | This query now recognizes additional file system calls. | -| Uncontrolled command line (`js/command-line-injection`) | More results | This query now recognizes additional command execution calls. | -| Client-side URL redirect (`js/client-side-unvalidated-url-redirection`) | Less results | This query now recognizes additional safe patterns of doing URL redirects. | -| Client-side cross-site scripting (`js/xss`) | Less results | This query now recognizes additional safe strings based on URLs. | -| Incomplete URL scheme check (`js/incomplete-url-scheme-check`) | More results | This query now recognizes additional url scheme checks. | -| Prototype pollution in utility function (`js/prototype-pollution-utility`) | More results | This query now recognizes additional utility functions as vulnerable to prototype polution. | -| Expression has no effect (`js/useless-expression`) | Less results | This query no longer flags an expression when that expression is the only content of the containing file. | -| Unknown directive (`js/unknown-directive`) | Less results | This query no longer flags directives generated by the Babel compiler. | +| Client-side cross-site scripting (`js/xss`) | Fewer results | This query no longer flags optionally sanitized values. | +| Client-side URL redirect (`js/client-side-unvalidated-url-redirection`) | Fewer results | This query now recognizes additional safe patterns of doing URL redirects. | +| Client-side cross-site scripting (`js/xss`) | Fewer results | This query now recognizes additional safe patterns of constructing HTML. | | Code injection (`js/code-injection`) | More results | More potential vulnerabilities involving NoSQL code operators are now recognized. | +| Expression has no effect (`js/useless-expression`) | Fewer results | This query no longer flags an expression when that expression is the only content of the containing file. | +| Incomplete URL scheme check (`js/incomplete-url-scheme-check`) | More results | This query now recognizes additional url scheme checks. | +| Misspelled variable name (`js/misspelled-variable-name`) | Message changed | The message for this query now correctly identifies the misspelled variable in additional cases. | +| Prototype pollution in utility function (`js/prototype-pollution-utility`) | More results | This query now recognizes additional utility functions as vulnerable to prototype polution. | +| Prototype pollution in utility function (`js/prototype-pollution-utility`) | More results | This query now recognizes more coding patterns that are vulnerable to prototype pollution. | +| Uncontrolled command line (`js/command-line-injection`) | More results | This query now recognizes additional command execution calls. | +| Uncontrolled data used in path expression (`js/path-injection`) | More results | This query now recognizes additional file system calls. | +| Unknown directive (`js/unknown-directive`) | Fewer results | This query no longer flags directives generated by the Babel compiler. | +| Unused property (`js/unused-property`) | Fewer results | This query no longer flags properties of objects that are operands of `yield` expressions. | | Zip Slip (`js/zipslip`) | More results | This query now recognizes additional vulnerabilities. | -| Unused property (`js/unused-property`) | Less results | This query no longer flags properties of objects that are operands of `yield` expressions. | The following low-precision queries are no longer run by default on LGTM (their results already were not displayed): - `js/angular/dead-event-listener` - `js/angular/unused-dependency` - - `js/conflicting-html-attribute` - - `js/useless-assignment-to-global` - - `js/too-many-parameters` - - `js/unused-property` - `js/bitwise-sign-check` - `js/comparison-of-identical-expressions` - - `js/misspelled-identifier` - - `js/jsdoc/malformed-param-tag` - - `js/jsdoc/unknown-parameter` - - `js/jsdoc/missing-parameter` - - `js/omitted-array-element` + - `js/conflicting-html-attribute` - `js/ignored-setter-parameter` + - `js/jsdoc/malformed-param-tag` + - `js/jsdoc/missing-parameter` + - `js/jsdoc/unknown-parameter` - `js/json-in-javascript-file` + - `js/misspelled-identifier` + - `js/nested-loops-with-same-variable` - `js/node/cyclic-import` - `js/node/unused-npm-dependency` - - `js/single-run-loop` - - `js/nested-loops-with-same-variable` + - `js/omitted-array-element` - `js/return-outside-function` + - `js/single-run-loop` + - `js/too-many-parameters` + - `js/unused-property` + - `js/useless-assignment-to-global` ## Changes to libraries @@ -79,3 +82,4 @@ The following low-precision queries are no longer run by default on LGTM (their - `Parameter.flow()` now gets the correct data flow node for a parameter. Previously this had a result, but the node was disconnected from the data flow graph. - `ParameterNode.asExpr()` and `.getAstNode()` now gets the parameter's AST node, whereas previously it had no result. - `Expr.flow()` now has a more meaningful result for destructuring patterns. Previously this node was disconnected from the data flow graph. Now it represents the values being destructured by the pattern. +* The global data-flow and taint-tracking libraries now model indirect parameter accesses through the `arguments` object in some cases, which may lead to additional results from some of the security queries, particularly "Prototype pollution in utility function". diff --git a/cpp/ql/src/Likely Bugs/Underspecified Functions/TooFewArguments.qll b/cpp/ql/src/Likely Bugs/Underspecified Functions/TooFewArguments.qll index b65d256f45c..6f3f4d43e9a 100644 --- a/cpp/ql/src/Likely Bugs/Underspecified Functions/TooFewArguments.qll +++ b/cpp/ql/src/Likely Bugs/Underspecified Functions/TooFewArguments.qll @@ -6,29 +6,50 @@ import cpp -// True if function was ()-declared, but not (void)-declared or K&R-defined +/** + * Holds if `fde` has a parameter declaration that's clear on the minimum + * number of parameters. This is essentially true for everything except + * `()`-declarations. + */ +private predicate hasDefiniteNumberOfParameters(FunctionDeclarationEntry fde) { + fde.hasVoidParamList() + or + fde.getNumberOfParameters() > 0 + or + fde.isDefinition() +} + +/* Holds if function was ()-declared, but not (void)-declared or K&R-defined. */ private predicate hasZeroParamDecl(Function f) { exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() | - not fde.hasVoidParamList() and fde.getNumberOfParameters() = 0 and not fde.isDefinition() + not hasDefiniteNumberOfParameters(fde) ) } -// True if this file (or header) was compiled as a C file +/* Holds if this file (or header) was compiled as a C file. */ private predicate isCompiledAsC(File f) { f.compiledAsC() or exists(File src | isCompiledAsC(src) | src.getAnIncludedFile() = f) } +/** Holds if `fc` is a call to `f` with too few arguments. */ predicate tooFewArguments(FunctionCall fc, Function f) { f = fc.getTarget() and not f.isVarargs() and not f instanceof BuiltInFunction and + // This query should only have results on C (not C++) functions that have a + // `()` parameter list somewhere. If it has results on other functions, then + // it's probably because the extractor only saw a partial compilation. hasZeroParamDecl(f) and isCompiledAsC(f.getFile()) and - // There is an explicit declaration of the function whose parameter count is larger - // than the number of call arguments - exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() | + // Produce an alert when all declarations that are authoritative on the + // parameter count specify a parameter count larger than the number of call + // arguments. + forex(FunctionDeclarationEntry fde | + fde = f.getADeclarationEntry() and + hasDefiniteNumberOfParameters(fde) + | fde.getNumberOfParameters() > fc.getNumberOfArguments() ) } diff --git a/cpp/ql/src/Metrics/History/HChurn.ql b/cpp/ql/src/Metrics/History/HChurn.ql deleted file mode 100644 index 7ff156b5300..00000000000 --- a/cpp/ql/src/Metrics/History/HChurn.ql +++ /dev/null @@ -1,27 +0,0 @@ -/** - * @name Churned lines per file - * @description Number of churned lines per file, across the revision - * history in the database. - * @kind treemap - * @id cpp/historical-churn - * @treemap.warnOn highValues - * @metricType file - * @metricAggregate avg sum max - * @tags external-data - * @deprecated - */ - -import cpp -import external.VCS - -from File f, int n -where - n = - sum(Commit entry, int churn | - churn = entry.getRecentChurnForFile(f) and - not artificialChange(entry) - | - churn - ) and - exists(f.getMetrics().getNumberOfLinesOfCode()) -select f, n order by n desc diff --git a/cpp/ql/src/Metrics/History/HLinesAdded.ql b/cpp/ql/src/Metrics/History/HLinesAdded.ql deleted file mode 100644 index ce03c5b4190..00000000000 --- a/cpp/ql/src/Metrics/History/HLinesAdded.ql +++ /dev/null @@ -1,27 +0,0 @@ -/** - * @name Added lines per file - * @description Number of added lines per file, across the revision - * history in the database. - * @kind treemap - * @id cpp/historical-lines-added - * @treemap.warnOn highValues - * @metricType file - * @metricAggregate avg sum max - * @tags external-data - * @deprecated - */ - -import cpp -import external.VCS - -from File f, int n -where - n = - sum(Commit entry, int churn | - churn = entry.getRecentAdditionsForFile(f) and - not artificialChange(entry) - | - churn - ) and - exists(f.getMetrics().getNumberOfLinesOfCode()) -select f, n order by n desc diff --git a/cpp/ql/src/Metrics/History/HLinesDeleted.ql b/cpp/ql/src/Metrics/History/HLinesDeleted.ql deleted file mode 100644 index 3d8ce78b6c4..00000000000 --- a/cpp/ql/src/Metrics/History/HLinesDeleted.ql +++ /dev/null @@ -1,27 +0,0 @@ -/** - * @name Deleted lines per file - * @description Number of deleted lines per file, across the revision - * history in the database. - * @kind treemap - * @id cpp/historical-lines-deleted - * @treemap.warnOn highValues - * @metricType file - * @metricAggregate avg sum max - * @tags external-data - * @deprecated - */ - -import cpp -import external.VCS - -from File f, int n -where - n = - sum(Commit entry, int churn | - churn = entry.getRecentDeletionsForFile(f) and - not artificialChange(entry) - | - churn - ) and - exists(f.getMetrics().getNumberOfLinesOfCode()) -select f, n order by n desc diff --git a/cpp/ql/src/Metrics/History/HNumberOfAuthors.ql b/cpp/ql/src/Metrics/History/HNumberOfAuthors.ql deleted file mode 100644 index 2d8f1f382c3..00000000000 --- a/cpp/ql/src/Metrics/History/HNumberOfAuthors.ql +++ /dev/null @@ -1,18 +0,0 @@ -/** - * @name Number of authors - * @description Number of distinct authors for each file. - * @kind treemap - * @id cpp/historical-number-of-authors - * @treemap.warnOn highValues - * @metricType file - * @metricAggregate avg min max - * @tags external-data - * @deprecated - */ - -import cpp -import external.VCS - -from File f -where exists(f.getMetrics().getNumberOfLinesOfCode()) -select f, count(Author author | author.getAnEditedFile() = f) diff --git a/cpp/ql/src/Metrics/History/HNumberOfChanges.ql b/cpp/ql/src/Metrics/History/HNumberOfChanges.ql deleted file mode 100644 index 451dc575636..00000000000 --- a/cpp/ql/src/Metrics/History/HNumberOfChanges.ql +++ /dev/null @@ -1,19 +0,0 @@ -/** - * @name Number of file-level changes - * @description The number of file-level changes made (by version - * control history). - * @kind treemap - * @id cpp/historical-number-of-changes - * @treemap.warnOn highValues - * @metricType file - * @metricAggregate avg min max sum - * @tags external-data - * @deprecated - */ - -import cpp -import external.VCS - -from File f -where exists(f.getMetrics().getNumberOfLinesOfCode()) -select f, count(Commit svn | f = svn.getAnAffectedFile() and not artificialChange(svn)) diff --git a/cpp/ql/src/Metrics/History/HNumberOfCoCommits.ql b/cpp/ql/src/Metrics/History/HNumberOfCoCommits.ql deleted file mode 100644 index ecd5c4ccb0d..00000000000 --- a/cpp/ql/src/Metrics/History/HNumberOfCoCommits.ql +++ /dev/null @@ -1,21 +0,0 @@ -/** - * @name Number of co-committed files - * @description The average number of other files that are touched - * whenever a file is affected by a commit. - * @kind treemap - * @id cpp/historical-number-of-co-commits - * @treemap.warnOn highValues - * @metricType file - * @metricAggregate avg min max - * @tags external-data - * @deprecated - */ - -import cpp -import external.VCS - -int committedFiles(Commit commit) { result = count(commit.getAnAffectedFile()) } - -from File f -where exists(f.getMetrics().getNumberOfLinesOfCode()) -select f, avg(Commit commit | commit.getAnAffectedFile() = f | committedFiles(commit) - 1) diff --git a/cpp/ql/src/Metrics/History/HNumberOfReCommits.ql b/cpp/ql/src/Metrics/History/HNumberOfReCommits.ql deleted file mode 100644 index fe98e66964e..00000000000 --- a/cpp/ql/src/Metrics/History/HNumberOfReCommits.ql +++ /dev/null @@ -1,37 +0,0 @@ -/** - * @name Number of re-commits for each file - * @description A re-commit is taken to mean a commit to a file that - * was touched less than five days ago. - * @kind treemap - * @id cpp/historical-number-of-re-commits - * @treemap.warnOn highValues - * @metricType file - * @metricAggregate avg min max - * @tags external-data - * @deprecated - */ - -import cpp -import external.VCS - -predicate inRange(Commit first, Commit second) { - first.getAnAffectedFile() = second.getAnAffectedFile() and - first != second and - exists(int n | - n = first.getDate().daysTo(second.getDate()) and - n >= 0 and - n < 5 - ) -} - -int recommitsForFile(File f) { - result = - count(Commit recommit | - f = recommit.getAnAffectedFile() and - exists(Commit prev | inRange(prev, recommit)) - ) -} - -from File f -where exists(f.getMetrics().getNumberOfLinesOfCode()) -select f, recommitsForFile(f) diff --git a/cpp/ql/src/Metrics/History/HNumberOfRecentAuthors.ql b/cpp/ql/src/Metrics/History/HNumberOfRecentAuthors.ql deleted file mode 100644 index 7f40c8706d9..00000000000 --- a/cpp/ql/src/Metrics/History/HNumberOfRecentAuthors.ql +++ /dev/null @@ -1,27 +0,0 @@ -/** - * @name Number of recent authors - * @description Number of distinct authors that have recently made - * changes. - * @kind treemap - * @id cpp/historical-number-of-recent-authors - * @treemap.warnOn highValues - * @metricType file - * @metricAggregate avg min max - * @tags external-data - * @deprecated - */ - -import cpp -import external.VCS - -from File f -where exists(f.getMetrics().getNumberOfLinesOfCode()) -select f, - count(Author author | - exists(Commit e | - e = author.getACommit() and - f = e.getAnAffectedFile() and - e.daysToNow() <= 180 and - not artificialChange(e) - ) - ) diff --git a/cpp/ql/src/Metrics/History/HNumberOfRecentChangedFiles.ql b/cpp/ql/src/Metrics/History/HNumberOfRecentChangedFiles.ql deleted file mode 100644 index 751449eb5aa..00000000000 --- a/cpp/ql/src/Metrics/History/HNumberOfRecentChangedFiles.ql +++ /dev/null @@ -1,24 +0,0 @@ -/** - * @name Recently changed files - * @description Number of files recently edited. - * @kind treemap - * @id cpp/historical-number-of-recent-changed-files - * @treemap.warnOn highValues - * @metricType file - * @metricAggregate avg min max sum - * @tags external-data - * @deprecated - */ - -import cpp -import external.VCS - -from File f -where - exists(Commit e | - e.getAnAffectedFile() = f and - e.daysToNow() <= 180 and - not artificialChange(e) - ) and - exists(f.getMetrics().getNumberOfLinesOfCode()) -select f, 1 diff --git a/cpp/ql/src/Metrics/History/HNumberOfRecentChanges.ql b/cpp/ql/src/Metrics/History/HNumberOfRecentChanges.ql deleted file mode 100644 index 886e8da6ca8..00000000000 --- a/cpp/ql/src/Metrics/History/HNumberOfRecentChanges.ql +++ /dev/null @@ -1,25 +0,0 @@ -/** - * @name Recent changes - * @description Number of recent commits to this file. - * @kind treemap - * @id cpp/historical-number-of-recent-changes - * @treemap.warnOn highValues - * @metricType file - * @metricAggregate avg min max sum - * @tags external-data - * @deprecated - */ - -import cpp -import external.VCS - -from File f, int n -where - n = - count(Commit e | - e.getAnAffectedFile() = f and - e.daysToNow() <= 180 and - not artificialChange(e) - ) and - exists(f.getMetrics().getNumberOfLinesOfCode()) -select f, n order by n desc diff --git a/cpp/ql/src/default.qll b/cpp/ql/src/default.qll index 4996ace8452..6bc0f1b009d 100644 --- a/cpp/ql/src/default.qll +++ b/cpp/ql/src/default.qll @@ -1 +1,7 @@ +/** + * DEPRECATED: use `import cpp` instead of `import default`. + * + * Provides classes and predicates for working with C/C++ code. + */ + import cpp diff --git a/cpp/ql/src/external/CodeDuplication.qll b/cpp/ql/src/external/CodeDuplication.qll index 2f4fd0d05da..4548e0be85e 100644 --- a/cpp/ql/src/external/CodeDuplication.qll +++ b/cpp/ql/src/external/CodeDuplication.qll @@ -1,3 +1,5 @@ +/** Provides classes for detecting duplicate or similar code. */ + import cpp private string relativePath(File file) { result = file.getRelativePath().replaceAll("\\", "/") } @@ -8,9 +10,12 @@ private predicate tokenLocation(string path, int sl, int sc, int ec, int el, Cop tokens(copy, index, sl, sc, ec, el) } +/** A token block used for detection of duplicate and similar code. */ class Copy extends @duplication_or_similarity { + /** Gets the index of the last token in this block. */ private int lastToken() { result = max(int i | tokens(this, i, _, _, _, _) | i) } + /** Gets the index of the token in this block starting at the location `loc`, if any. */ int tokenStartingAt(Location loc) { exists(string filepath, int startline, int startcol | loc.hasLocationInfo(filepath, startline, startcol, _, _) and @@ -18,6 +23,7 @@ class Copy extends @duplication_or_similarity { ) } + /** Gets the index of the token in this block ending at the location `loc`, if any. */ int tokenEndingAt(Location loc) { exists(string filepath, int endline, int endcol | loc.hasLocationInfo(filepath, _, _, endline, endcol) and @@ -25,24 +31,38 @@ class Copy extends @duplication_or_similarity { ) } + /** Gets the line on which the first token in this block starts. */ int sourceStartLine() { tokens(this, 0, result, _, _, _) } + /** Gets the column on which the first token in this block starts. */ int sourceStartColumn() { tokens(this, 0, _, result, _, _) } + /** Gets the line on which the last token in this block ends. */ int sourceEndLine() { tokens(this, lastToken(), _, _, result, _) } + /** Gets the column on which the last token in this block ends. */ int sourceEndColumn() { tokens(this, lastToken(), _, _, _, result) } + /** Gets the number of lines containing at least (part of) one token in this block. */ int sourceLines() { result = this.sourceEndLine() + 1 - this.sourceStartLine() } + /** Gets an opaque identifier for the equivalence class of this block. */ int getEquivalenceClass() { duplicateCode(this, _, result) or similarCode(this, _, result) } + /** Gets the source file in which this block appears. */ File sourceFile() { exists(string name | duplicateCode(this, name, _) or similarCode(this, name, _) | name.replaceAll("\\", "/") = relativePath(result) ) } + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn ) { @@ -53,25 +73,30 @@ class Copy extends @duplication_or_similarity { endcolumn = sourceEndColumn() } + /** Gets a textual representation of this element. */ string toString() { none() } } +/** A block of duplicated code. */ class DuplicateBlock extends Copy, @duplication { override string toString() { result = "Duplicate code: " + sourceLines() + " duplicated lines." } } +/** A block of similar code. */ class SimilarBlock extends Copy, @similarity { override string toString() { result = "Similar code: " + sourceLines() + " almost duplicated lines." } } +/** Gets a function with a body and a location. */ FunctionDeclarationEntry sourceMethod() { result.isDefinition() and exists(result.getLocation()) and numlines(unresolveElement(result.getFunction()), _, _, _) } +/** Gets the number of member functions in `c` with a body and a location. */ int numberOfSourceMethods(Class c) { result = count(FunctionDeclarationEntry m | @@ -108,6 +133,10 @@ private predicate duplicateStatement( ) } +/** + * Holds if `m1` is a function with `total` lines, and `m2` is a function + * that has `duplicate` lines in common with `m1`. + */ predicate duplicateStatements( FunctionDeclarationEntry m1, FunctionDeclarationEntry m2, int duplicate, int total ) { @@ -115,13 +144,16 @@ predicate duplicateStatements( total = strictcount(statementInMethod(m1)) } -/** - * Find pairs of methods are identical - */ +/** Holds if `m` and other are identical functions. */ predicate duplicateMethod(FunctionDeclarationEntry m, FunctionDeclarationEntry other) { exists(int total | duplicateStatements(m, other, total, total)) } +/** + * INTERNAL: do not use. + * + * Holds if `line` in `f` is similar to a line somewhere else. + */ predicate similarLines(File f, int line) { exists(SimilarBlock b | b.sourceFile() = f and line in [b.sourceStartLine() .. b.sourceEndLine()]) } @@ -152,6 +184,7 @@ private predicate similarLinesCoveredFiles(File f, File otherFile) { ) } +/** Holds if `coveredLines` lines of `f` are similar to lines in `otherFile`. */ predicate similarLinesCovered(File f, int coveredLines, File otherFile) { exists(int numLines | numLines = f.getMetrics().getNumberOfLines() | similarLinesCoveredFiles(f, otherFile) and @@ -166,6 +199,11 @@ predicate similarLinesCovered(File f, int coveredLines, File otherFile) { ) } +/** + * INTERNAL: do not use. + * + * Holds if `line` in `f` is duplicated by a line somewhere else. + */ predicate duplicateLines(File f, int line) { exists(DuplicateBlock b | b.sourceFile() = f and line in [b.sourceStartLine() .. b.sourceEndLine()] @@ -182,6 +220,7 @@ private predicate duplicateLinesPerEquivalenceClass(int equivClass, int lines, F ) } +/** Holds if `coveredLines` lines of `f` are duplicates of lines in `otherFile`. */ predicate duplicateLinesCovered(File f, int coveredLines, File otherFile) { exists(int numLines | numLines = f.getMetrics().getNumberOfLines() | exists(int coveredApprox | @@ -206,6 +245,7 @@ predicate duplicateLinesCovered(File f, int coveredLines, File otherFile) { ) } +/** Holds if most of `f` (`percent`%) is similar to `other`. */ predicate similarFiles(File f, File other, int percent) { exists(int covered, int total | similarLinesCovered(f, covered, other) and @@ -216,6 +256,7 @@ predicate similarFiles(File f, File other, int percent) { not duplicateFiles(f, other, _) } +/** Holds if most of `f` (`percent`%) is duplicated by `other`. */ predicate duplicateFiles(File f, File other, int percent) { exists(int covered, int total | duplicateLinesCovered(f, covered, other) and @@ -225,6 +266,10 @@ predicate duplicateFiles(File f, File other, int percent) { ) } +/** + * Holds if most member functions of `c` (`numDup` out of `total`) are + * duplicates of member functions in `other`. + */ predicate mostlyDuplicateClassBase(Class c, Class other, int numDup, int total) { numDup = strictcount(FunctionDeclarationEntry m1 | @@ -240,6 +285,11 @@ predicate mostlyDuplicateClassBase(Class c, Class other, int numDup, int total) (numDup * 100) / total > 80 } +/** + * Holds if most member functions of `c` are duplicates of member functions in + * `other`. Provides the human-readable `message` to describe the amount of + * duplication. + */ predicate mostlyDuplicateClass(Class c, Class other, string message) { exists(int numDup, int total | mostlyDuplicateClassBase(c, other, numDup, total) and @@ -264,12 +314,21 @@ predicate mostlyDuplicateClass(Class c, Class other, string message) { ) } +/** Holds if `f` and `other` are similar or duplicates. */ predicate fileLevelDuplication(File f, File other) { similarFiles(f, other, _) or duplicateFiles(f, other, _) } +/** + * Holds if most member functions of `c` are duplicates of member functions in + * `other`. + */ predicate classLevelDuplication(Class c, Class other) { mostlyDuplicateClass(c, other, _) } +/** + * Holds if `line` in `f` should be allowed to be duplicated. This is the case + * for `#include` directives. + */ predicate whitelistedLineForDuplication(File f, int line) { exists(Include i | i.getFile() = f and i.getLocation().getStartLine() = line) } diff --git a/cpp/ql/src/external/DefectFilter.qll b/cpp/ql/src/external/DefectFilter.qll index da63893b8bb..675f3b25faa 100644 --- a/cpp/ql/src/external/DefectFilter.qll +++ b/cpp/ql/src/external/DefectFilter.qll @@ -1,31 +1,52 @@ +/** Provides a class for working with defect query results stored in dashboard databases. */ + import cpp +/** + * Holds if `id` in the opaque identifier of a result reported by query `queryPath`, + * such that `message` is the associated message and the location of the result spans + * column `startcolumn` of line `startline` to column `endcolumn` of line `endline` + * in file `filepath`. + * + * For more information, see [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ external predicate defectResults( int id, string queryPath, string file, int startline, int startcol, int endline, int endcol, string message ); +/** + * A defect query result stored in a dashboard database. + */ class DefectResult extends int { DefectResult() { defectResults(this, _, _, _, _, _, _, _) } + /** Gets the path of the query that reported the result. */ string getQueryPath() { defectResults(this, result, _, _, _, _, _, _) } + /** Gets the file in which this query result was reported. */ File getFile() { exists(string path | defectResults(this, _, path, _, _, _, _, _) and result.getAbsolutePath() = path ) } + /** Gets the line on which the location of this query result starts. */ int getStartLine() { defectResults(this, _, _, result, _, _, _, _) } + /** Gets the column on which the location of this query result starts. */ int getStartColumn() { defectResults(this, _, _, _, result, _, _, _) } + /** Gets the line on which the location of this query result ends. */ int getEndLine() { defectResults(this, _, _, _, _, result, _, _) } + /** Gets the column on which the location of this query result ends. */ int getEndColumn() { defectResults(this, _, _, _, _, _, result, _) } + /** Gets the message associated with this query result. */ string getMessage() { defectResults(this, _, _, _, _, _, _, result) } + /** Gets the URL corresponding to the location of this query result. */ string getURL() { result = "file://" + getFile().getAbsolutePath() + ":" + getStartLine() + ":" + getStartColumn() + ":" + diff --git a/cpp/ql/src/external/ExternalArtifact.qll b/cpp/ql/src/external/ExternalArtifact.qll index 94fa0d7e31a..abbc96a7b47 100644 --- a/cpp/ql/src/external/ExternalArtifact.qll +++ b/cpp/ql/src/external/ExternalArtifact.qll @@ -1,26 +1,45 @@ +/** + * Provides classes for working with external data. + */ + import cpp +/** + * An external data item. + */ class ExternalData extends @externalDataElement { + /** Gets the path of the file this data was loaded from. */ string getDataPath() { externalData(this, result, _, _) } + /** + * Gets the path of the file this data was loaded from, with its + * extension replaced by `.ql`. + */ string getQueryPath() { result = getDataPath().regexpReplaceAll("\\.[^.]*$", ".ql") } + /** Gets the number of fields in this data item. */ int getNumFields() { result = 1 + max(int i | externalData(this, _, i, _) | i) } - string getField(int index) { externalData(this, _, index, result) } + /** Gets the value of the `i`th field of this data item. */ + string getField(int i) { externalData(this, _, i, result) } - int getFieldAsInt(int index) { result = getField(index).toInt() } + /** Gets the integer value of the `i`th field of this data item. */ + int getFieldAsInt(int i) { result = getField(i).toInt() } - float getFieldAsFloat(int index) { result = getField(index).toFloat() } + /** Gets the floating-point value of the `i`th field of this data item. */ + float getFieldAsFloat(int i) { result = getField(i).toFloat() } - date getFieldAsDate(int index) { result = getField(index).toDate() } + /** Gets the value of the `i`th field of this data item, interpreted as a date. */ + date getFieldAsDate(int i) { result = getField(i).toDate() } + /** Gets a textual representation of this data item. */ string toString() { result = getQueryPath() + ": " + buildTupleString(0) } - private string buildTupleString(int start) { - start = getNumFields() - 1 and result = getField(start) + /** Gets a textual representation of this data item, starting with the `n`th field. */ + private string buildTupleString(int n) { + n = getNumFields() - 1 and result = getField(n) or - start < getNumFields() - 1 and result = getField(start) + "," + buildTupleString(start + 1) + n < getNumFields() - 1 and result = getField(n) + "," + buildTupleString(n + 1) } } @@ -33,7 +52,9 @@ class DefectExternalData extends ExternalData { this.getNumFields() = 2 } + /** Gets the URL associated with this data item. */ string getURL() { result = getField(0) } + /** Gets the message associated with this data item. */ string getMessage() { result = getField(1) } } diff --git a/cpp/ql/src/external/MetricFilter.qll b/cpp/ql/src/external/MetricFilter.qll index b159b4cad5c..dd9cece78ce 100644 --- a/cpp/ql/src/external/MetricFilter.qll +++ b/cpp/ql/src/external/MetricFilter.qll @@ -1,31 +1,58 @@ +/** Provides a class for working with metric query results stored in dashboard databases. */ + import cpp +/** + * Holds if `id` in the opaque identifier of a result reported by query `queryPath`, + * such that `value` is the reported metric value and the location of the result spans + * column `startcolumn` of line `startline` to column `endcolumn` of line `endline` + * in file `filepath`. + * + * For more information, see [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html). + */ external predicate metricResults( int id, string queryPath, string file, int startline, int startcol, int endline, int endcol, float value ); +/** + * A metric query result stored in a dashboard database. + */ class MetricResult extends int { MetricResult() { metricResults(this, _, _, _, _, _, _, _) } + /** Gets the path of the query that reported the result. */ string getQueryPath() { metricResults(this, result, _, _, _, _, _, _) } + /** Gets the file in which this query result was reported. */ File getFile() { exists(string path | metricResults(this, _, path, _, _, _, _, _) and result.getAbsolutePath() = path ) } + /** Gets the line on which the location of this query result starts. */ int getStartLine() { metricResults(this, _, _, result, _, _, _, _) } + /** Gets the column on which the location of this query result starts. */ int getStartColumn() { metricResults(this, _, _, _, result, _, _, _) } + /** Gets the line on which the location of this query result ends. */ int getEndLine() { metricResults(this, _, _, _, _, result, _, _) } + /** Gets the column on which the location of this query result ends. */ int getEndColumn() { metricResults(this, _, _, _, _, _, result, _) } + /** + * Holds if there is a `Location` entity whose location is the same as + * the location of this query result. + */ predicate hasMatchingLocation() { exists(this.getMatchingLocation()) } + /** + * Gets the `Location` entity whose location is the same as the location + * of this query result. + */ Location getMatchingLocation() { result.getFile() = this.getFile() and result.getStartLine() = this.getStartLine() and @@ -34,8 +61,10 @@ class MetricResult extends int { result.getEndColumn() = this.getEndColumn() } + /** Gets the value associated with this query result. */ float getValue() { metricResults(this, _, _, _, _, _, _, result) } + /** Gets the URL corresponding to the location of this query result. */ string getURL() { result = "file://" + getFile().getAbsolutePath() + ":" + getStartLine() + ":" + getStartColumn() + ":" + diff --git a/cpp/ql/src/external/VCS.qll b/cpp/ql/src/external/VCS.qll deleted file mode 100644 index ce36ace56d9..00000000000 --- a/cpp/ql/src/external/VCS.qll +++ /dev/null @@ -1,92 +0,0 @@ -import cpp - -class Commit extends @svnentry { - Commit() { - svnaffectedfiles(this, _, _) and - exists(date svnDate, date snapshotDate | - svnentries(this, _, _, svnDate, _) and - snapshotDate(snapshotDate) and - svnDate <= snapshotDate - ) - } - - string toString() { result = this.getRevisionName() } - - string getRevisionName() { svnentries(this, result, _, _, _) } - - string getAuthor() { svnentries(this, _, result, _, _) } - - date getDate() { svnentries(this, _, _, result, _) } - - int getChangeSize() { svnentries(this, _, _, _, result) } - - string getMessage() { svnentrymsg(this, result) } - - string getAnAffectedFilePath(string action) { - exists(File rawFile | svnaffectedfiles(this, unresolveElement(rawFile), action) | - result = rawFile.getAbsolutePath() - ) - } - - string getAnAffectedFilePath() { result = getAnAffectedFilePath(_) } - - File getAnAffectedFile(string action) { - // Workaround for incorrect keys in SVN data - exists(File svnFile | svnFile.getAbsolutePath() = result.getAbsolutePath() | - svnaffectedfiles(this, unresolveElement(svnFile), action) - ) and - exists(result.getMetrics().getNumberOfLinesOfCode()) - } - - File getAnAffectedFile() { exists(string action | result = this.getAnAffectedFile(action)) } - - private predicate churnForFile(File f, int added, int deleted) { - // Workaround for incorrect keys in SVN data - exists(File svnFile | svnFile.getAbsolutePath() = f.getAbsolutePath() | - svnchurn(this, unresolveElement(svnFile), added, deleted) - ) and - exists(f.getMetrics().getNumberOfLinesOfCode()) - } - - int getRecentChurnForFile(File f) { - exists(int added, int deleted | churnForFile(f, added, deleted) and result = added + deleted) - } - - int getRecentAdditionsForFile(File f) { churnForFile(f, result, _) } - - int getRecentDeletionsForFile(File f) { churnForFile(f, _, result) } - - predicate isRecent() { recentCommit(this) } - - int daysToNow() { - exists(date now | snapshotDate(now) | result = getDate().daysTo(now) and result >= 0) - } -} - -class Author extends string { - Author() { exists(Commit e | this = e.getAuthor()) } - - Commit getACommit() { result.getAuthor() = this } - - File getAnEditedFile() { result = this.getACommit().getAnAffectedFile() } -} - -predicate recentCommit(Commit e) { - exists(date snapshotDate, date commitDate, int days | - snapshotDate(snapshotDate) and - e.getDate() = commitDate and - days = commitDate.daysTo(snapshotDate) and - days >= 0 and - days <= 60 - ) -} - -date firstChange(File f) { - result = min(Commit e, date toMin | f = e.getAnAffectedFile() and toMin = e.getDate() | toMin) -} - -predicate firstCommit(Commit e) { - not exists(File f | f = e.getAnAffectedFile() | firstChange(f) < e.getDate()) -} - -predicate artificialChange(Commit e) { firstCommit(e) or e.getChangeSize() >= 50000 } diff --git a/cpp/ql/src/external/tests/DefectFromSVN.ql b/cpp/ql/src/external/tests/DefectFromSVN.ql deleted file mode 100644 index 64dd69148eb..00000000000 --- a/cpp/ql/src/external/tests/DefectFromSVN.ql +++ /dev/null @@ -1,20 +0,0 @@ -/** - * @name Defect from SVN - * @description A test case for creating a defect from SVN data. - * @kind problem - * @problem.severity warning - * @tags external-data - * @deprecated - */ - -import cpp -import external.ExternalArtifact -import external.VCS - -predicate numCommits(File f, int i) { i = count(Commit e | e.getAnAffectedFile() = f) } - -predicate maxCommits(int i) { i = max(File f, int j | numCommits(f, j) | j) } - -from File f, int i -where numCommits(f, i) and maxCommits(i) -select f, "This file has " + i + " commits." diff --git a/cpp/ql/src/external/tests/MetricFromSVN.ql b/cpp/ql/src/external/tests/MetricFromSVN.ql deleted file mode 100644 index e81eec18ea5..00000000000 --- a/cpp/ql/src/external/tests/MetricFromSVN.ql +++ /dev/null @@ -1,17 +0,0 @@ -/** - * @name Metric from SVN - * @description Find number of commits for a file - * @treemap.warnOn lowValues - * @metricType file - * @tags external-data - * @deprecated - */ - -import cpp -import external.VCS - -predicate numCommits(File f, int i) { i = count(Commit e | e.getAnAffectedFile() = f) } - -from File f, int i -where numCommits(f, i) -select f, i diff --git a/cpp/ql/src/filters/RecentDefects.ql b/cpp/ql/src/filters/RecentDefects.ql deleted file mode 100644 index 4b742849fda..00000000000 --- a/cpp/ql/src/filters/RecentDefects.ql +++ /dev/null @@ -1,25 +0,0 @@ -/** - * @name Filter: exclude results from files that have not recently been - * edited - * @description Use this filter to return results only if they are - * located in files that have been modified in the 60 days - * before the date of the snapshot. - * @kind problem - * @id cpp/recent-defects-filter - * @tags filter - * external-data - * @deprecated - */ - -import cpp -import external.DefectFilter -import external.VCS - -pragma[noopt] -private predicate recent(File file) { - exists(Commit e | file = e.getAnAffectedFile() | e.isRecent() and not artificialChange(e)) -} - -from DefectResult res -where recent(res.getFile()) -select res, res.getMessage() diff --git a/cpp/ql/src/filters/RecentDefectsForMetric.ql b/cpp/ql/src/filters/RecentDefectsForMetric.ql deleted file mode 100644 index ee057fe71ca..00000000000 --- a/cpp/ql/src/filters/RecentDefectsForMetric.ql +++ /dev/null @@ -1,25 +0,0 @@ -/** - * @name Metric filter: exclude results from files that have not - * recently been edited - * @description Use this filter to return results only if they are - * located in files that have been modified in the 60 days - * before the snapshot. - * @kind treemap - * @id cpp/recent-defects-for-metric-filter - * @tags filter - * external-data - * @deprecated - */ - -import cpp -import external.MetricFilter -import external.VCS - -pragma[noopt] -private predicate recent(File file) { - exists(Commit e | file = e.getAnAffectedFile() | e.isRecent() and not artificialChange(e)) -} - -from MetricResult res -where recent(res.getFile()) -select res, res.getValue() diff --git a/cpp/ql/src/objc.qll b/cpp/ql/src/objc.qll index 4996ace8452..58a9ec50ff7 100644 --- a/cpp/ql/src/objc.qll +++ b/cpp/ql/src/objc.qll @@ -1 +1,7 @@ +/** + * DEPRECATED: Objective C is no longer supported. + * + * Import `cpp` instead of `objc`. + */ + import cpp diff --git a/cpp/ql/src/semmle/code/cpp/Declaration.qll b/cpp/ql/src/semmle/code/cpp/Declaration.qll index 840705b01b5..a4a4a4af2e6 100644 --- a/cpp/ql/src/semmle/code/cpp/Declaration.qll +++ b/cpp/ql/src/semmle/code/cpp/Declaration.qll @@ -98,7 +98,12 @@ class Declaration extends Locatable, @declaration { this.hasQualifiedName(namespaceQualifier, "", baseName) } - override string toString() { result = this.getName() } + /** + * Gets a description of this `Declaration` for display purposes. + */ + string getDescription() { result = this.getName() } + + final override string toString() { result = this.getDescription() } /** * Gets the name of this declaration. diff --git a/cpp/ql/src/semmle/code/cpp/Namespace.qll b/cpp/ql/src/semmle/code/cpp/Namespace.qll index 839a85d6b8f..9b9b12aaef0 100644 --- a/cpp/ql/src/semmle/code/cpp/Namespace.qll +++ b/cpp/ql/src/semmle/code/cpp/Namespace.qll @@ -79,7 +79,10 @@ class Namespace extends NameQualifyingElement, @namespace { /** Gets the metric namespace. */ MetricNamespace getMetrics() { result = this } - override string toString() { result = this.getQualifiedName() } + /** Gets a version of the `QualifiedName` that is more suitable for display purposes. */ + string getFriendlyName() { result = this.getQualifiedName() } + + final override string toString() { result = getFriendlyName() } /** Gets a declaration of (part of) this namespace. */ NamespaceDeclarationEntry getADeclarationEntry() { result.getNamespace() = this } @@ -104,7 +107,7 @@ class NamespaceDeclarationEntry extends Locatable, @namespace_decl { namespace_decls(underlyingElement(this), unresolveElement(result), _, _) } - override string toString() { result = this.getNamespace().toString() } + override string toString() { result = this.getNamespace().getFriendlyName() } /** * Gets the location of the token preceding the namespace declaration @@ -150,7 +153,7 @@ class UsingDeclarationEntry extends UsingEntry { */ Declaration getDeclaration() { usings(underlyingElement(this), unresolveElement(result), _) } - override string toString() { result = "using " + this.getDeclaration().toString() } + override string toString() { result = "using " + this.getDeclaration().getDescription() } } /** @@ -169,7 +172,7 @@ class UsingDirectiveEntry extends UsingEntry { */ Namespace getNamespace() { usings(underlyingElement(this), unresolveElement(result), _) } - override string toString() { result = "using namespace " + this.getNamespace().toString() } + override string toString() { result = "using namespace " + this.getNamespace().getFriendlyName() } } /** @@ -204,7 +207,7 @@ class GlobalNamespace extends Namespace { */ deprecated string getFullName() { result = this.getName() } - override string toString() { result = "(global namespace)" } + override string getFriendlyName() { result = "(global namespace)" } } /** diff --git a/cpp/ql/src/semmle/code/cpp/Variable.qll b/cpp/ql/src/semmle/code/cpp/Variable.qll index 4d546a736dc..54f39ab2bb6 100644 --- a/cpp/ql/src/semmle/code/cpp/Variable.qll +++ b/cpp/ql/src/semmle/code/cpp/Variable.qll @@ -260,24 +260,33 @@ class ParameterDeclarationEntry extends VariableDeclarationEntry { */ int getIndex() { param_decl_bind(underlyingElement(this), result, _) } + private string getAnonymousParameterDescription() { + not exists(getName()) and + exists(string idx | + idx = + ((getIndex() + 1).toString() + "th") + .replaceAll("1th", "1st") + .replaceAll("2th", "2nd") + .replaceAll("3th", "3rd") + .replaceAll("11st", "11th") + .replaceAll("12nd", "12th") + .replaceAll("13rd", "13th") and + if exists(getCanonicalName()) + then result = "declaration of " + getCanonicalName() + " as anonymous " + idx + " parameter" + else result = "declaration of " + idx + " parameter" + ) + } + override string toString() { - if exists(getName()) - then result = super.toString() - else - exists(string idx | - idx = - ((getIndex() + 1).toString() + "th") - .replaceAll("1th", "1st") - .replaceAll("2th", "2nd") - .replaceAll("3th", "3rd") - .replaceAll("11st", "11th") - .replaceAll("12nd", "12th") - .replaceAll("13rd", "13th") - | - if exists(getCanonicalName()) - then result = "declaration of " + getCanonicalName() + " as anonymous " + idx + " parameter" - else result = "declaration of " + idx + " parameter" - ) + isDefinition() and + result = "definition of " + getName() + or + not isDefinition() and + if getName() = getCanonicalName() + then result = "declaration of " + getName() + else result = "declaration of " + getCanonicalName() + " as " + getName() + or + result = getAnonymousParameterDescription() } /** diff --git a/cpp/ql/src/semmle/code/cpp/XML.qll b/cpp/ql/src/semmle/code/cpp/XML.qll index dc7836aaabe..713903b63e6 100755 --- a/cpp/ql/src/semmle/code/cpp/XML.qll +++ b/cpp/ql/src/semmle/code/cpp/XML.qll @@ -116,7 +116,7 @@ class XMLFile extends XMLParent, File { XMLFile() { xmlEncoding(this, _) } /** Gets a printable representation of this XML file. */ - override string toString() { result = XMLParent.super.toString() } + override string toString() { result = getName() } /** Gets the name of this XML file. */ override string getName() { result = File.super.getAbsolutePath() } @@ -236,7 +236,7 @@ class XMLElement extends @xmlelement, XMLParent, XMLLocatable { string getAttributeValue(string name) { result = this.getAttribute(name).getValue() } /** Gets a printable representation of this XML element. */ - override string toString() { result = XMLParent.super.toString() } + override string toString() { result = getName() } } /** diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/BasicBlocks.qll b/cpp/ql/src/semmle/code/cpp/controlflow/BasicBlocks.qll index 681d0b710f6..16947019f54 100644 --- a/cpp/ql/src/semmle/code/cpp/controlflow/BasicBlocks.qll +++ b/cpp/ql/src/semmle/code/cpp/controlflow/BasicBlocks.qll @@ -1,3 +1,8 @@ +/** + * Provides a library for reasoning about control flow at the granularity of basic blocks. + * This is usually much more efficient than reasoning directly at the level of `ControlFlowNode`s. + */ + import cpp private import internal.PrimitiveBasicBlocks private import internal.ConstantExprs @@ -148,22 +153,37 @@ predicate bb_successor = bb_successor_cached/2; class BasicBlock extends ControlFlowNodeBase { BasicBlock() { basic_block_entry_node(this) } + /** Holds if this basic block contains `node`. */ predicate contains(ControlFlowNode node) { basic_block_member(node, this, _) } + /** Gets the `ControlFlowNode` at position `pos` in this basic block. */ ControlFlowNode getNode(int pos) { basic_block_member(result, this, pos) } + /** Gets a `ControlFlowNode` in this basic block. */ ControlFlowNode getANode() { basic_block_member(result, this, _) } + /** Gets a `BasicBlock` that is a direct successor of this basic block. */ BasicBlock getASuccessor() { bb_successor(this, result) } + /** Gets a `BasicBlock` that is a direct predecessor of this basic block. */ BasicBlock getAPredecessor() { bb_successor(result, this) } + /** + * Gets a `BasicBlock` such that the control-flow edge `(this, result)` may be taken + * when the outgoing edge of this basic block is an expression that is true. + */ BasicBlock getATrueSuccessor() { result.getStart() = this.getEnd().getATrueSuccessor() } + /** + * Gets a `BasicBlock` such that the control-flow edge `(this, result)` may be taken + * when the outgoing edge of this basic block is an expression that is false. + */ BasicBlock getAFalseSuccessor() { result.getStart() = this.getEnd().getAFalseSuccessor() } + /** Gets the final `ControlFlowNode` of this basic block. */ ControlFlowNode getEnd() { basic_block_member(result, this, bb_length(this) - 1) } + /** Gets the first `ControlFlowNode` of this basic block. */ ControlFlowNode getStart() { result = this } /** Gets the number of `ControlFlowNode`s in this basic block. */ @@ -192,6 +212,7 @@ class BasicBlock extends ControlFlowNodeBase { this.getEnd().getLocation().hasLocationInfo(endf, _, _, endl, endc) } + /** Gets the function containing this basic block. */ Function getEnclosingFunction() { result = this.getStart().getControlFlowScope() } /** diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/ControlFlowGraph.qll b/cpp/ql/src/semmle/code/cpp/controlflow/ControlFlowGraph.qll index 9174f474a8f..1f79d6c5bc2 100644 --- a/cpp/ql/src/semmle/code/cpp/controlflow/ControlFlowGraph.qll +++ b/cpp/ql/src/semmle/code/cpp/controlflow/ControlFlowGraph.qll @@ -1,3 +1,8 @@ +/** + * Provides a library for reasoning about control flow at the granularity of + * individual nodes in the control-flow graph. + */ + import cpp import BasicBlocks private import semmle.code.cpp.controlflow.internal.ConstantExprs @@ -29,8 +34,10 @@ private import semmle.code.cpp.controlflow.internal.CFG * `Handler`. There are no edges from function calls to `Handler`s. */ class ControlFlowNode extends Locatable, ControlFlowNodeBase { + /** Gets a direct successor of this control-flow node, if any. */ ControlFlowNode getASuccessor() { successors_adapted(this, result) } + /** Gets a direct predecessor of this control-flow node, if any. */ ControlFlowNode getAPredecessor() { this = result.getASuccessor() } /** Gets the function containing this control-flow node. */ @@ -71,6 +78,7 @@ class ControlFlowNode extends Locatable, ControlFlowNodeBase { result = getASuccessor() } + /** Gets the `BasicBlock` containing this control-flow node. */ BasicBlock getBasicBlock() { result.getANode() = this } } @@ -86,10 +94,18 @@ import ControlFlowGraphPublic */ class ControlFlowNodeBase extends ElementBase, @cfgnode { } +/** + * Holds when `n2` is a control-flow node such that the control-flow + * edge `(n1, n2)` may be taken when `n1` is an expression that is true. + */ predicate truecond_base(ControlFlowNodeBase n1, ControlFlowNodeBase n2) { qlCFGTrueSuccessor(n1, n2) } +/** + * Holds when `n2` is a control-flow node such that the control-flow + * edge `(n1, n2)` may be taken when `n1` is an expression that is false. + */ predicate falsecond_base(ControlFlowNodeBase n1, ControlFlowNodeBase n2) { qlCFGFalseSuccessor(n1, n2) } diff --git a/cpp/ql/src/semmle/code/cpp/controlflow/Dataflow.qll b/cpp/ql/src/semmle/code/cpp/controlflow/Dataflow.qll index 99fb6966099..1a5d81ee9f3 100644 --- a/cpp/ql/src/semmle/code/cpp/controlflow/Dataflow.qll +++ b/cpp/ql/src/semmle/code/cpp/controlflow/Dataflow.qll @@ -15,14 +15,25 @@ import Dereferenced abstract class DataflowAnnotation extends string { DataflowAnnotation() { this = "pointer-null" or this = "pointer-valid" } + /** Holds if this annotation is the default annotation. */ abstract predicate isDefault(); + /** Holds if this annotation is generated when analyzing expression `e`. */ abstract predicate generatedOn(Expr e); + /** + * Holds if this annotation is generated for the variable `v` when + * the control-flow edge `(src, dest)` is taken. + */ abstract predicate generatedBy(LocalScopeVariable v, ControlFlowNode src, ControlFlowNode dest); + /** + * Holds if this annotation is removed for the variable `v` when + * the control-flow edge `(src, dest)` is taken. + */ abstract predicate killedBy(LocalScopeVariable v, ControlFlowNode src, ControlFlowNode dest); + /** Holds if expression `e` is given this annotation. */ predicate marks(Expr e) { this.generatedOn(e) and reachable(e) or @@ -31,6 +42,7 @@ abstract class DataflowAnnotation extends string { exists(LocalScopeVariable v | this.marks(v, e) and e = v.getAnAccess()) } + /** Holds if the variable `v` accessed in control-flow node `n` is given this annotation. */ predicate marks(LocalScopeVariable v, ControlFlowNode n) { v.getAnAccess().getEnclosingFunction().getBlock() = n and this.isDefault() @@ -57,6 +69,10 @@ abstract class DataflowAnnotation extends string { ) } + /** + * Holds if the variable `v` preserves this annotation when the control-flow + * edge `(src, dest)` is taken. + */ predicate preservedBy(LocalScopeVariable v, ControlFlowNode src, ControlFlowNode dest) { this.marks(v, src) and src.getASuccessor() = dest and @@ -64,6 +80,10 @@ abstract class DataflowAnnotation extends string { not v.getAnAssignment() = src } + /** + * Holds if the variable `v` is assigned this annotation when `src` is an assignment + * expression that assigns to `v` and the control-flow edge `(src, dest)` is taken. + */ predicate assignedBy(LocalScopeVariable v, ControlFlowNode src, ControlFlowNode dest) { this.marks(src.(AssignExpr).getRValue()) and src = v.getAnAssignment() and diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll index 9587ea5f274..f876c04d6c6 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll @@ -66,9 +66,6 @@ abstract class Configuration extends string { */ predicate isBarrier(Node node) { none() } - /** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */ - deprecated predicate isBarrierEdge(Node node1, Node node2) { none() } - /** Holds if data flow into `node` is prohibited. */ predicate isBarrierIn(Node node) { none() } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll index 9587ea5f274..f876c04d6c6 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll @@ -66,9 +66,6 @@ abstract class Configuration extends string { */ predicate isBarrier(Node node) { none() } - /** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */ - deprecated predicate isBarrierEdge(Node node1, Node node2) { none() } - /** Holds if data flow into `node` is prohibited. */ predicate isBarrierIn(Node node) { none() } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll index 9587ea5f274..f876c04d6c6 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll @@ -66,9 +66,6 @@ abstract class Configuration extends string { */ predicate isBarrier(Node node) { none() } - /** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */ - deprecated predicate isBarrierEdge(Node node1, Node node2) { none() } - /** Holds if data flow into `node` is prohibited. */ predicate isBarrierIn(Node node) { none() } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll index 9587ea5f274..f876c04d6c6 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll @@ -66,9 +66,6 @@ abstract class Configuration extends string { */ predicate isBarrier(Node node) { none() } - /** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */ - deprecated predicate isBarrierEdge(Node node1, Node node2) { none() } - /** Holds if data flow into `node` is prohibited. */ predicate isBarrierIn(Node node) { none() } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll index 9587ea5f274..f876c04d6c6 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll @@ -66,9 +66,6 @@ abstract class Configuration extends string { */ predicate isBarrier(Node node) { none() } - /** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */ - deprecated predicate isBarrierEdge(Node node1, Node node2) { none() } - /** Holds if data flow into `node` is prohibited. */ predicate isBarrierIn(Node node) { none() } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll index e8b828f5b3e..0f0607662e9 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll @@ -79,13 +79,6 @@ abstract class Configuration extends DataFlow::Configuration { defaultTaintBarrier(node) } - /** DEPRECATED: override `isSanitizerIn` and `isSanitizerOut` instead. */ - deprecated predicate isSanitizerEdge(DataFlow::Node node1, DataFlow::Node node2) { none() } - - deprecated final override predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) { - isSanitizerEdge(node1, node2) - } - /** Holds if data flow into `node` is prohibited. */ predicate isSanitizerIn(DataFlow::Node node) { none() } diff --git a/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll b/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll index e8b828f5b3e..0f0607662e9 100644 --- a/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll @@ -79,13 +79,6 @@ abstract class Configuration extends DataFlow::Configuration { defaultTaintBarrier(node) } - /** DEPRECATED: override `isSanitizerIn` and `isSanitizerOut` instead. */ - deprecated predicate isSanitizerEdge(DataFlow::Node node1, DataFlow::Node node2) { none() } - - deprecated final override predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) { - isSanitizerEdge(node1, node2) - } - /** Holds if data flow into `node` is prohibited. */ predicate isSanitizerIn(DataFlow::Node node) { none() } diff --git a/cpp/ql/src/semmle/code/cpp/exprs/Lambda.qll b/cpp/ql/src/semmle/code/cpp/exprs/Lambda.qll index f634d9239b3..4a5b1b21c8d 100644 --- a/cpp/ql/src/semmle/code/cpp/exprs/Lambda.qll +++ b/cpp/ql/src/semmle/code/cpp/exprs/Lambda.qll @@ -86,7 +86,7 @@ class Closure extends Class { result.getName() = "operator()" } - override string toString() { result = "decltype([...](...){...})" } + override string getDescription() { result = "decltype([...](...){...})" } } /** @@ -99,7 +99,7 @@ class Closure extends Class { * ``` */ class LambdaCapture extends Locatable, @lambdacapture { - override string toString() { result = getField().toString() } + override string toString() { result = getField().getName() } override string getCanonicalQLClass() { result = "LambdaCapture" } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll index dbeefae4880..d13a6b58d83 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/DefaultTaintTracking.qll @@ -1,10 +1,12 @@ import cpp import semmle.code.cpp.security.Security private import semmle.code.cpp.ir.dataflow.DataFlow +private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil private import semmle.code.cpp.ir.dataflow.DataFlow2 private import semmle.code.cpp.ir.dataflow.DataFlow3 private import semmle.code.cpp.ir.IR private import semmle.code.cpp.ir.dataflow.internal.DataFlowDispatch as Dispatch +private import semmle.code.cpp.controlflow.IRGuards private import semmle.code.cpp.models.interfaces.Taint private import semmle.code.cpp.models.interfaces.DataFlow @@ -170,11 +172,34 @@ private predicate hasUpperBoundsCheck(Variable var) { ) } +private predicate nodeIsBarrierEqualityCandidate( + DataFlow::Node node, Operand access, Variable checkedVar +) { + readsVariable(node.asInstruction(), checkedVar) and + any(IRGuardCondition guard).ensuresEq(access, _, _, node.asInstruction().getBlock(), true) +} + private predicate nodeIsBarrier(DataFlow::Node node) { exists(Variable checkedVar | readsVariable(node.asInstruction(), checkedVar) and hasUpperBoundsCheck(checkedVar) ) + or + exists(Variable checkedVar, Operand access | + /* + * This node is guarded by a condition that forces the accessed variable + * to equal something else. For example: + * ``` + * x = taintsource() + * if (x == 10) { + * taintsink(x); // not considered tainted + * } + * ``` + */ + + nodeIsBarrierEqualityCandidate(node, access, checkedVar) and + readsVariable(access.getDef(), checkedVar) + ) } private predicate nodeIsBarrierIn(DataFlow::Node node) { diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll index 9587ea5f274..f876c04d6c6 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll @@ -66,9 +66,6 @@ abstract class Configuration extends string { */ predicate isBarrier(Node node) { none() } - /** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */ - deprecated predicate isBarrierEdge(Node node1, Node node2) { none() } - /** Holds if data flow into `node` is prohibited. */ predicate isBarrierIn(Node node) { none() } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll index 9587ea5f274..f876c04d6c6 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll @@ -66,9 +66,6 @@ abstract class Configuration extends string { */ predicate isBarrier(Node node) { none() } - /** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */ - deprecated predicate isBarrierEdge(Node node1, Node node2) { none() } - /** Holds if data flow into `node` is prohibited. */ predicate isBarrierIn(Node node) { none() } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll index 9587ea5f274..f876c04d6c6 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll @@ -66,9 +66,6 @@ abstract class Configuration extends string { */ predicate isBarrier(Node node) { none() } - /** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */ - deprecated predicate isBarrierEdge(Node node1, Node node2) { none() } - /** Holds if data flow into `node` is prohibited. */ predicate isBarrierIn(Node node) { none() } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll index 9587ea5f274..f876c04d6c6 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll @@ -66,9 +66,6 @@ abstract class Configuration extends string { */ predicate isBarrier(Node node) { none() } - /** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */ - deprecated predicate isBarrierEdge(Node node1, Node node2) { none() } - /** Holds if data flow into `node` is prohibited. */ predicate isBarrierIn(Node node) { none() } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll index 05eede88f52..96e4d9137df 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll @@ -71,9 +71,7 @@ class Node extends TIRDataFlowNode { * `x.set(taint())` is a partial definition of `x`, and `transfer(&x, taint())` is * a partial definition of `&x`). */ - Expr asPartialDefinition() { - result = this.(PartialDefinitionNode).getInstruction().getUnconvertedResultExpression() - } + Expr asPartialDefinition() { result = this.(PartialDefinitionNode).getDefinedExpr() } /** * DEPRECATED: See UninitializedNode. @@ -247,14 +245,17 @@ abstract class PostUpdateNode extends InstructionNode { * setY(&x); // a partial definition of the object `x`. * ``` */ -abstract private class PartialDefinitionNode extends PostUpdateNode, TInstructionNode { } +abstract private class PartialDefinitionNode extends PostUpdateNode, TInstructionNode { + abstract Expr getDefinedExpr(); +} private class ExplicitFieldStoreQualifierNode extends PartialDefinitionNode { override ChiInstruction instr; + FieldAddressInstruction field; ExplicitFieldStoreQualifierNode() { not instr.isResultConflated() and - exists(StoreInstruction store, FieldInstruction field | + exists(StoreInstruction store | instr.getPartial() = store and field = store.getDestinationAddress() ) } @@ -264,6 +265,10 @@ private class ExplicitFieldStoreQualifierNode extends PartialDefinitionNode { // DataFlowImplConsistency::Consistency. However, it's not clear what (if any) implications // this consistency failure has. override Node getPreUpdateNode() { result.asInstruction() = instr.getTotal() } + + override Expr getDefinedExpr() { + result = field.getObjectAddress().getUnconvertedResultExpression() + } } /** @@ -274,15 +279,18 @@ private class ExplicitFieldStoreQualifierNode extends PartialDefinitionNode { */ private class ExplicitSingleFieldStoreQualifierNode extends PartialDefinitionNode { override StoreInstruction instr; + FieldAddressInstruction field; ExplicitSingleFieldStoreQualifierNode() { - exists(FieldAddressInstruction field | - field = instr.getDestinationAddress() and - not exists(ChiInstruction chi | chi.getPartial() = instr) - ) + field = instr.getDestinationAddress() and + not exists(ChiInstruction chi | chi.getPartial() = instr) } override Node getPreUpdateNode() { none() } + + override Expr getDefinedExpr() { + result = field.getObjectAddress().getUnconvertedResultExpression() + } } /** @@ -454,9 +462,9 @@ private predicate simpleInstructionLocalFlowStep(Instruction iFrom, Instruction // for now. iTo.getAnOperand().(ChiTotalOperand).getDef() = iFrom or - // The next two rules allow flow from partial definitions in setters to succeeding loads in the caller. - // First, we add flow from write side-effects to non-conflated chi instructions through their - // partial operands. Consider the following example: + // Add flow from write side-effects to non-conflated chi instructions through their + // partial operands. From there, a `readStep` will find subsequent reads of that field. + // Consider the following example: // ``` // void setX(Point* p, int new_x) { // p->x = new_x; @@ -466,14 +474,9 @@ private predicate simpleInstructionLocalFlowStep(Instruction iFrom, Instruction // ``` // Here, a `WriteSideEffectInstruction` will provide a new definition for `p->x` after the call to // `setX`, which will be melded into `p` through a chi instruction. - iTo.getAnOperand().(ChiPartialOperand).getDef() = iFrom.(WriteSideEffectInstruction) and - not iTo.isResultConflated() - or - // Next, we add flow from non-conflated chi instructions to loads (even when they are not precise). - // This ensures that loads of `p->x` gets data flow from the `WriteSideEffectInstruction` above. - exists(ChiInstruction chi | iFrom = chi | - not chi.isResultConflated() and - iTo.(LoadInstruction).getSourceValueOperand().getAnyDef() = chi + exists(ChiInstruction chi | chi = iTo | + chi.getPartialOperand().getDef() = iFrom.(WriteSideEffectInstruction) and + not chi.isResultConflated() ) or // Flow from stores to structs with a single field to a load of that field. diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll index e8b828f5b3e..0f0607662e9 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll @@ -79,13 +79,6 @@ abstract class Configuration extends DataFlow::Configuration { defaultTaintBarrier(node) } - /** DEPRECATED: override `isSanitizerIn` and `isSanitizerOut` instead. */ - deprecated predicate isSanitizerEdge(DataFlow::Node node1, DataFlow::Node node2) { none() } - - deprecated final override predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) { - isSanitizerEdge(node1, node2) - } - /** Holds if data flow into `node` is prohibited. */ predicate isSanitizerIn(DataFlow::Node node) { none() } diff --git a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingImpl.qll b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingImpl.qll index e8b828f5b3e..0f0607662e9 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingImpl.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingImpl.qll @@ -79,13 +79,6 @@ abstract class Configuration extends DataFlow::Configuration { defaultTaintBarrier(node) } - /** DEPRECATED: override `isSanitizerIn` and `isSanitizerOut` instead. */ - deprecated predicate isSanitizerEdge(DataFlow::Node node1, DataFlow::Node node2) { none() } - - deprecated final override predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) { - isSanitizerEdge(node1, node2) - } - /** Holds if data flow into `node` is prohibited. */ predicate isSanitizerIn(DataFlow::Node node) { none() } diff --git a/cpp/ql/src/semmle/code/cpp/ir/internal/CppType.qll b/cpp/ql/src/semmle/code/cpp/ir/internal/CppType.qll index 7a164012845..f8c4ceaf904 100644 --- a/cpp/ql/src/semmle/code/cpp/ir/internal/CppType.qll +++ b/cpp/ql/src/semmle/code/cpp/ir/internal/CppType.qll @@ -362,7 +362,7 @@ CppType getTypeForPRValueOrUnknown(Type type) { /** * Gets the `CppType` that represents a glvalue of type `type`. */ -CppType getTypeForGLValue(Type type) { result.hasType(type, true) } +CppGLValueAddressType getTypeForGLValue(Type type) { result.hasType(type, true) } /** * Gets the `CppType` that represents a prvalue of type `int`. diff --git a/cpp/ql/test/header-variant-tests/deduplication/classes.ql b/cpp/ql/test/header-variant-tests/deduplication/classes.ql index 2fe0098c9cf..ed44ab124dc 100644 --- a/cpp/ql/test/header-variant-tests/deduplication/classes.ql +++ b/cpp/ql/test/header-variant-tests/deduplication/classes.ql @@ -1,4 +1,4 @@ -import default +import cpp from Class c, string n where n = count(Class x | x.getName() = c.getName()) + " distinct class(es) called " + c.getName() diff --git a/cpp/ql/test/library-tests/allocators/allocators.ql b/cpp/ql/test/library-tests/allocators/allocators.ql index 235ec0451e7..a2126cdfbce 100644 --- a/cpp/ql/test/library-tests/allocators/allocators.ql +++ b/cpp/ql/test/library-tests/allocators/allocators.ql @@ -1,4 +1,4 @@ -import default +import cpp import semmle.code.cpp.models.implementations.Allocation query predicate newExprs( diff --git a/cpp/ql/test/library-tests/conversions/conversions.ql b/cpp/ql/test/library-tests/conversions/conversions.ql index 6ba2bd8f365..c26881ecbbe 100644 --- a/cpp/ql/test/library-tests/conversions/conversions.ql +++ b/cpp/ql/test/library-tests/conversions/conversions.ql @@ -1,4 +1,4 @@ -import default +import cpp string getValueCategoryString(Expr expr) { if expr.isLValueCategory() diff --git a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/defaulttainttracking.cpp b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/defaulttainttracking.cpp index a456bc856ff..4dfba0aebf9 100644 --- a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/defaulttainttracking.cpp +++ b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/defaulttainttracking.cpp @@ -115,5 +115,5 @@ void test_conflated_fields3() { XY xy; xy.x = 0; taint_y(&xy); - sink(xy.x); // not tainted [FALSE POSITIVE] + sink(xy.x); // not tainted } diff --git a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/tainted.expected b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/tainted.expected index a2acc1a7dbf..e1620e55f65 100644 --- a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/tainted.expected +++ b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/tainted.expected @@ -103,8 +103,6 @@ | defaulttainttracking.cpp:110:17:110:22 | call to getenv | defaulttainttracking.cpp:110:17:110:32 | (int)... | | defaulttainttracking.cpp:110:17:110:22 | call to getenv | defaulttainttracking.cpp:110:17:110:32 | access to array | | defaulttainttracking.cpp:110:17:110:22 | call to getenv | defaulttainttracking.cpp:111:12:111:18 | tainted | -| defaulttainttracking.cpp:110:17:110:22 | call to getenv | defaulttainttracking.cpp:118:11:118:11 | x | -| defaulttainttracking.cpp:110:17:110:22 | call to getenv | shared.h:6:15:6:23 | sinkparam | | dispatch.cpp:28:29:28:34 | call to getenv | dispatch.cpp:28:24:28:27 | call to atoi | | dispatch.cpp:28:29:28:34 | call to getenv | dispatch.cpp:28:29:28:34 | call to getenv | | dispatch.cpp:28:29:28:34 | call to getenv | dispatch.cpp:28:29:28:45 | (const char *)... | diff --git a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/test_diff.expected b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/test_diff.expected index 26c83f57cb7..b02339f160c 100644 --- a/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/test_diff.expected +++ b/cpp/ql/test/library-tests/dataflow/DefaultTaintTracking/test_diff.expected @@ -21,8 +21,6 @@ | defaulttainttracking.cpp:97:27:97:32 | call to getenv | defaulttainttracking.cpp:98:10:98:11 | p2 | IR only | | defaulttainttracking.cpp:97:27:97:32 | call to getenv | shared.h:5:23:5:31 | sinkparam | IR only | | defaulttainttracking.cpp:110:17:110:22 | call to getenv | defaulttainttracking.cpp:111:8:111:8 | y | AST only | -| defaulttainttracking.cpp:110:17:110:22 | call to getenv | defaulttainttracking.cpp:118:11:118:11 | x | IR only | -| defaulttainttracking.cpp:110:17:110:22 | call to getenv | shared.h:6:15:6:23 | sinkparam | IR only | | globals.cpp:13:15:13:20 | call to getenv | globals.cpp:13:5:13:11 | global1 | AST only | | globals.cpp:23:15:23:20 | call to getenv | globals.cpp:23:5:23:11 | global2 | AST only | | test_diff.cpp:104:12:104:15 | argv | test_diff.cpp:104:11:104:20 | (...) | IR only | diff --git a/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.expected b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.expected new file mode 100644 index 00000000000..0b4a738b7df --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.expected @@ -0,0 +1,370 @@ +| A.cpp:25:7:25:10 | this | AST only | +| A.cpp:25:13:25:13 | c | AST only | +| A.cpp:27:22:27:25 | this | AST only | +| A.cpp:27:28:27:28 | c | AST only | +| A.cpp:31:20:31:20 | c | AST only | +| A.cpp:40:5:40:6 | cc | AST only | +| A.cpp:41:5:41:6 | ct | AST only | +| A.cpp:42:10:42:12 | & ... | AST only | +| A.cpp:43:10:43:12 | & ... | AST only | +| A.cpp:48:20:48:20 | c | AST only | +| A.cpp:49:10:49:10 | b | AST only | +| A.cpp:49:13:49:13 | c | AST only | +| A.cpp:55:5:55:5 | b | AST only | +| A.cpp:56:10:56:10 | b | AST only | +| A.cpp:56:13:56:15 | call to get | AST only | +| A.cpp:57:28:57:30 | call to get | AST only | +| A.cpp:64:17:64:18 | b1 | AST only | +| A.cpp:65:10:65:11 | b1 | AST only | +| A.cpp:65:14:65:14 | c | AST only | +| A.cpp:66:10:66:11 | b2 | AST only | +| A.cpp:66:14:66:14 | c | AST only | +| A.cpp:73:21:73:22 | b1 | AST only | +| A.cpp:74:10:74:11 | b1 | AST only | +| A.cpp:74:14:74:14 | c | AST only | +| A.cpp:75:10:75:11 | b2 | AST only | +| A.cpp:75:14:75:14 | c | AST only | +| A.cpp:81:17:81:18 | b1 | AST only | +| A.cpp:81:21:81:21 | c | AST only | +| A.cpp:90:7:90:8 | b2 | AST only | +| A.cpp:90:15:90:15 | c | AST only | +| A.cpp:100:9:100:9 | a | AST only | +| A.cpp:101:8:101:9 | c1 | AST only | +| A.cpp:107:12:107:13 | c1 | AST only | +| A.cpp:107:16:107:16 | a | AST only | +| A.cpp:120:12:120:13 | c1 | AST only | +| A.cpp:120:16:120:16 | a | AST only | +| A.cpp:126:5:126:5 | b | AST only | +| A.cpp:131:8:131:8 | b | AST only | +| A.cpp:132:10:132:10 | b | AST only | +| A.cpp:132:13:132:13 | c | AST only | +| A.cpp:142:10:142:10 | c | AST only | +| A.cpp:143:7:143:10 | this | AST only | +| A.cpp:143:13:143:13 | b | AST only | +| A.cpp:151:18:151:18 | b | AST only | +| A.cpp:152:10:152:10 | d | AST only | +| A.cpp:152:13:152:13 | b | AST only | +| A.cpp:153:10:153:10 | d | AST only | +| A.cpp:153:13:153:13 | b | AST only | +| A.cpp:153:16:153:16 | c | AST only | +| A.cpp:154:10:154:10 | b | AST only | +| A.cpp:154:13:154:13 | c | AST only | +| A.cpp:160:29:160:29 | b | AST only | +| A.cpp:161:38:161:39 | l1 | AST only | +| A.cpp:162:38:162:39 | l2 | AST only | +| A.cpp:163:10:163:11 | l3 | AST only | +| A.cpp:163:14:163:17 | head | AST only | +| A.cpp:164:10:164:11 | l3 | AST only | +| A.cpp:164:14:164:17 | next | AST only | +| A.cpp:164:20:164:23 | head | AST only | +| A.cpp:165:10:165:11 | l3 | AST only | +| A.cpp:165:14:165:17 | next | AST only | +| A.cpp:165:20:165:23 | next | AST only | +| A.cpp:165:26:165:29 | head | AST only | +| A.cpp:166:10:166:11 | l3 | AST only | +| A.cpp:166:14:166:17 | next | AST only | +| A.cpp:166:20:166:23 | next | AST only | +| A.cpp:166:26:166:29 | next | AST only | +| A.cpp:166:32:166:35 | head | AST only | +| A.cpp:169:12:169:12 | l | AST only | +| A.cpp:169:15:169:18 | head | AST only | +| A.cpp:183:7:183:10 | head | AST only | +| A.cpp:183:7:183:10 | this | AST only | +| A.cpp:184:7:184:10 | this | AST only | +| A.cpp:184:13:184:16 | next | AST only | +| B.cpp:7:25:7:25 | e | AST only | +| B.cpp:8:25:8:26 | b1 | AST only | +| B.cpp:9:10:9:11 | b2 | AST only | +| B.cpp:9:14:9:17 | box1 | AST only | +| B.cpp:9:20:9:24 | elem1 | AST only | +| B.cpp:10:10:10:11 | b2 | AST only | +| B.cpp:10:14:10:17 | box1 | AST only | +| B.cpp:10:20:10:24 | elem2 | AST only | +| B.cpp:16:37:16:37 | e | AST only | +| B.cpp:17:25:17:26 | b1 | AST only | +| B.cpp:18:10:18:11 | b2 | AST only | +| B.cpp:18:14:18:17 | box1 | AST only | +| B.cpp:18:20:18:24 | elem1 | AST only | +| B.cpp:19:10:19:11 | b2 | AST only | +| B.cpp:19:14:19:17 | box1 | AST only | +| B.cpp:19:20:19:24 | elem2 | AST only | +| B.cpp:35:7:35:10 | this | AST only | +| B.cpp:35:13:35:17 | elem1 | AST only | +| B.cpp:36:7:36:10 | this | AST only | +| B.cpp:36:13:36:17 | elem2 | AST only | +| B.cpp:46:7:46:10 | this | AST only | +| B.cpp:46:13:46:16 | box1 | AST only | +| C.cpp:19:5:19:5 | c | AST only | +| C.cpp:24:5:24:8 | this | AST only | +| C.cpp:24:11:24:12 | s3 | AST only | +| D.cpp:9:21:9:24 | elem | AST only | +| D.cpp:9:21:9:24 | this | AST only | +| D.cpp:11:29:11:32 | elem | AST only | +| D.cpp:11:29:11:32 | this | AST only | +| D.cpp:16:21:16:23 | box | AST only | +| D.cpp:16:21:16:23 | this | AST only | +| D.cpp:18:29:18:31 | box | AST only | +| D.cpp:18:29:18:31 | this | AST only | +| D.cpp:22:10:22:11 | b2 | AST only | +| D.cpp:22:14:22:20 | call to getBox1 | AST only | +| D.cpp:22:25:22:31 | call to getElem | AST only | +| D.cpp:30:5:30:5 | b | AST only | +| D.cpp:30:8:30:10 | box | AST only | +| D.cpp:30:13:30:16 | elem | AST only | +| D.cpp:31:14:31:14 | b | AST only | +| D.cpp:37:5:37:5 | b | AST only | +| D.cpp:37:8:37:10 | box | AST only | +| D.cpp:37:21:37:21 | e | AST only | +| D.cpp:38:14:38:14 | b | AST only | +| D.cpp:44:5:44:5 | b | AST only | +| D.cpp:44:8:44:14 | call to getBox1 | AST only | +| D.cpp:44:19:44:22 | elem | AST only | +| D.cpp:45:14:45:14 | b | AST only | +| D.cpp:51:5:51:5 | b | AST only | +| D.cpp:51:8:51:14 | call to getBox1 | AST only | +| D.cpp:51:27:51:27 | e | AST only | +| D.cpp:52:14:52:14 | b | AST only | +| D.cpp:57:5:57:12 | boxfield | AST only | +| D.cpp:57:5:57:12 | this | AST only | +| D.cpp:58:5:58:12 | boxfield | AST only | +| D.cpp:58:5:58:12 | this | AST only | +| D.cpp:58:15:58:17 | box | AST only | +| D.cpp:58:20:58:23 | elem | AST only | +| D.cpp:64:10:64:17 | boxfield | AST only | +| D.cpp:64:10:64:17 | this | AST only | +| D.cpp:64:20:64:22 | box | AST only | +| D.cpp:64:25:64:28 | elem | AST only | +| E.cpp:21:10:21:10 | p | AST only | +| E.cpp:21:13:21:16 | data | AST only | +| E.cpp:21:18:21:23 | buffer | AST only | +| E.cpp:28:21:28:23 | raw | AST only | +| E.cpp:29:21:29:21 | b | AST only | +| E.cpp:29:24:29:29 | buffer | AST only | +| E.cpp:30:21:30:21 | p | AST only | +| E.cpp:30:23:30:26 | data | AST only | +| E.cpp:30:28:30:33 | buffer | AST only | +| E.cpp:31:10:31:12 | raw | AST only | +| E.cpp:32:10:32:10 | b | AST only | +| E.cpp:32:13:32:18 | buffer | AST only | +| E.cpp:33:18:33:19 | & ... | AST only | +| aliasing.cpp:9:6:9:7 | m1 | AST only | +| aliasing.cpp:13:5:13:6 | m1 | AST only | +| aliasing.cpp:17:5:17:6 | m1 | AST only | +| aliasing.cpp:25:17:25:19 | & ... | AST only | +| aliasing.cpp:26:19:26:20 | s2 | AST only | +| aliasing.cpp:37:8:37:9 | m1 | AST only | +| aliasing.cpp:42:6:42:7 | m1 | AST only | +| aliasing.cpp:49:9:49:10 | m1 | AST only | +| aliasing.cpp:54:6:54:7 | m1 | AST only | +| aliasing.cpp:60:6:60:7 | m1 | AST only | +| aliasing.cpp:72:5:72:6 | m1 | AST only | +| aliasing.cpp:79:6:79:7 | m1 | AST only | +| aliasing.cpp:86:5:86:6 | m1 | AST only | +| aliasing.cpp:92:3:92:3 | w | AST only | +| aliasing.cpp:92:7:92:8 | m1 | AST only | +| by_reference.cpp:12:8:12:8 | a | AST only | +| by_reference.cpp:16:5:16:8 | this | AST only | +| by_reference.cpp:16:11:16:11 | a | AST only | +| by_reference.cpp:20:5:20:8 | this | AST only | +| by_reference.cpp:20:23:20:27 | value | AST only | +| by_reference.cpp:24:19:24:22 | this | AST only | +| by_reference.cpp:24:25:24:29 | value | AST only | +| by_reference.cpp:50:3:50:3 | s | AST only | +| by_reference.cpp:50:17:50:26 | call to user_input | AST only | +| by_reference.cpp:51:10:51:20 | call to getDirectly | AST only | +| by_reference.cpp:56:3:56:3 | s | AST only | +| by_reference.cpp:56:19:56:28 | call to user_input | AST only | +| by_reference.cpp:57:10:57:22 | call to getIndirectly | AST only | +| by_reference.cpp:62:3:62:3 | s | AST only | +| by_reference.cpp:62:25:62:34 | call to user_input | AST only | +| by_reference.cpp:63:10:63:28 | call to getThroughNonMember | AST only | +| by_reference.cpp:68:17:68:18 | & ... | AST only | +| by_reference.cpp:68:21:68:30 | call to user_input | AST only | +| by_reference.cpp:69:8:69:20 | call to nonMemberGetA | AST only | +| by_reference.cpp:84:10:84:10 | a | AST only | +| by_reference.cpp:88:9:88:9 | a | AST only | +| by_reference.cpp:102:21:102:39 | & ... | AST only | +| by_reference.cpp:102:22:102:26 | outer | AST only | +| by_reference.cpp:103:21:103:25 | outer | AST only | +| by_reference.cpp:103:27:103:35 | inner_ptr | AST only | +| by_reference.cpp:104:15:104:22 | & ... | AST only | +| by_reference.cpp:104:16:104:20 | outer | AST only | +| by_reference.cpp:106:21:106:41 | & ... | AST only | +| by_reference.cpp:106:22:106:27 | pouter | AST only | +| by_reference.cpp:107:21:107:26 | pouter | AST only | +| by_reference.cpp:107:29:107:37 | inner_ptr | AST only | +| by_reference.cpp:108:15:108:24 | & ... | AST only | +| by_reference.cpp:108:16:108:21 | pouter | AST only | +| by_reference.cpp:110:8:110:12 | outer | AST only | +| by_reference.cpp:110:14:110:25 | inner_nested | AST only | +| by_reference.cpp:110:27:110:27 | a | AST only | +| by_reference.cpp:111:8:111:12 | outer | AST only | +| by_reference.cpp:111:14:111:22 | inner_ptr | AST only | +| by_reference.cpp:111:25:111:25 | a | AST only | +| by_reference.cpp:112:8:112:12 | outer | AST only | +| by_reference.cpp:112:14:112:14 | a | AST only | +| by_reference.cpp:114:8:114:13 | pouter | AST only | +| by_reference.cpp:114:16:114:27 | inner_nested | AST only | +| by_reference.cpp:114:29:114:29 | a | AST only | +| by_reference.cpp:115:8:115:13 | pouter | AST only | +| by_reference.cpp:115:16:115:24 | inner_ptr | AST only | +| by_reference.cpp:115:27:115:27 | a | AST only | +| by_reference.cpp:116:8:116:13 | pouter | AST only | +| by_reference.cpp:116:16:116:16 | a | AST only | +| by_reference.cpp:122:21:122:25 | outer | AST only | +| by_reference.cpp:122:27:122:38 | inner_nested | AST only | +| by_reference.cpp:123:21:123:36 | * ... | AST only | +| by_reference.cpp:123:22:123:26 | outer | AST only | +| by_reference.cpp:124:15:124:19 | outer | AST only | +| by_reference.cpp:124:21:124:21 | a | AST only | +| by_reference.cpp:126:21:126:26 | pouter | AST only | +| by_reference.cpp:126:29:126:40 | inner_nested | AST only | +| by_reference.cpp:127:21:127:38 | * ... | AST only | +| by_reference.cpp:127:22:127:27 | pouter | AST only | +| by_reference.cpp:128:15:128:20 | pouter | AST only | +| by_reference.cpp:128:23:128:23 | a | AST only | +| by_reference.cpp:130:8:130:12 | outer | AST only | +| by_reference.cpp:130:14:130:25 | inner_nested | AST only | +| by_reference.cpp:130:27:130:27 | a | AST only | +| by_reference.cpp:131:8:131:12 | outer | AST only | +| by_reference.cpp:131:14:131:22 | inner_ptr | AST only | +| by_reference.cpp:131:25:131:25 | a | AST only | +| by_reference.cpp:132:8:132:12 | outer | AST only | +| by_reference.cpp:132:14:132:14 | a | AST only | +| by_reference.cpp:134:8:134:13 | pouter | AST only | +| by_reference.cpp:134:16:134:27 | inner_nested | AST only | +| by_reference.cpp:134:29:134:29 | a | AST only | +| by_reference.cpp:135:8:135:13 | pouter | AST only | +| by_reference.cpp:135:16:135:24 | inner_ptr | AST only | +| by_reference.cpp:135:27:135:27 | a | AST only | +| by_reference.cpp:136:8:136:13 | pouter | AST only | +| by_reference.cpp:136:16:136:16 | a | AST only | +| complex.cpp:11:22:11:23 | a_ | AST only | +| complex.cpp:11:22:11:23 | this | AST only | +| complex.cpp:12:22:12:23 | b_ | AST only | +| complex.cpp:12:22:12:23 | this | AST only | +| complex.cpp:51:8:51:8 | b | AST only | +| complex.cpp:51:10:51:14 | inner | AST only | +| complex.cpp:51:16:51:16 | f | AST only | +| complex.cpp:52:8:52:8 | b | AST only | +| complex.cpp:52:10:52:14 | inner | AST only | +| complex.cpp:52:16:52:16 | f | AST only | +| complex.cpp:62:3:62:4 | b1 | AST only | +| complex.cpp:62:6:62:10 | inner | AST only | +| complex.cpp:62:12:62:12 | f | AST only | +| complex.cpp:63:3:63:4 | b2 | AST only | +| complex.cpp:63:6:63:10 | inner | AST only | +| complex.cpp:63:12:63:12 | f | AST only | +| complex.cpp:64:3:64:4 | b3 | AST only | +| complex.cpp:64:6:64:10 | inner | AST only | +| complex.cpp:64:12:64:12 | f | AST only | +| complex.cpp:65:3:65:4 | b3 | AST only | +| complex.cpp:65:6:65:10 | inner | AST only | +| complex.cpp:65:12:65:12 | f | AST only | +| complex.cpp:68:7:68:8 | b1 | AST only | +| complex.cpp:71:7:71:8 | b2 | AST only | +| complex.cpp:74:7:74:8 | b3 | AST only | +| complex.cpp:77:7:77:8 | b4 | AST only | +| constructors.cpp:20:24:20:25 | a_ | AST only | +| constructors.cpp:20:24:20:25 | this | AST only | +| constructors.cpp:21:24:21:25 | b_ | AST only | +| constructors.cpp:21:24:21:25 | this | AST only | +| constructors.cpp:28:10:28:10 | f | AST only | +| constructors.cpp:29:10:29:10 | f | AST only | +| constructors.cpp:40:9:40:9 | f | AST only | +| constructors.cpp:43:9:43:9 | g | AST only | +| constructors.cpp:46:9:46:9 | h | AST only | +| constructors.cpp:49:9:49:9 | i | AST only | +| file://:0:0:0:0 | this | AST only | +| file://:0:0:0:0 | this | AST only | +| file://:0:0:0:0 | this | AST only | +| file://:0:0:0:0 | this | AST only | +| file://:0:0:0:0 | this | AST only | +| file://:0:0:0:0 | this | AST only | +| file://:0:0:0:0 | this | AST only | +| file://:0:0:0:0 | this | AST only | +| file://:0:0:0:0 | this | AST only | +| file://:0:0:0:0 | this | AST only | +| qualifiers.cpp:9:30:9:33 | this | AST only | +| qualifiers.cpp:9:36:9:36 | a | AST only | +| qualifiers.cpp:12:56:12:56 | a | AST only | +| qualifiers.cpp:13:57:13:57 | a | AST only | +| qualifiers.cpp:22:5:22:9 | outer | AST only | +| qualifiers.cpp:22:11:22:18 | call to getInner | AST only | +| qualifiers.cpp:22:23:22:23 | a | AST only | +| qualifiers.cpp:23:10:23:14 | outer | AST only | +| qualifiers.cpp:23:16:23:20 | inner | AST only | +| qualifiers.cpp:23:23:23:23 | a | AST only | +| qualifiers.cpp:27:5:27:9 | outer | AST only | +| qualifiers.cpp:27:11:27:18 | call to getInner | AST only | +| qualifiers.cpp:27:28:27:37 | call to user_input | AST only | +| qualifiers.cpp:28:10:28:14 | outer | AST only | +| qualifiers.cpp:28:16:28:20 | inner | AST only | +| qualifiers.cpp:28:23:28:23 | a | AST only | +| qualifiers.cpp:32:17:32:21 | outer | AST only | +| qualifiers.cpp:32:23:32:30 | call to getInner | AST only | +| qualifiers.cpp:32:35:32:44 | call to user_input | AST only | +| qualifiers.cpp:33:10:33:14 | outer | AST only | +| qualifiers.cpp:33:16:33:20 | inner | AST only | +| qualifiers.cpp:33:23:33:23 | a | AST only | +| qualifiers.cpp:37:19:37:35 | * ... | AST only | +| qualifiers.cpp:37:20:37:24 | outer | AST only | +| qualifiers.cpp:37:38:37:47 | call to user_input | AST only | +| qualifiers.cpp:38:10:38:14 | outer | AST only | +| qualifiers.cpp:38:16:38:20 | inner | AST only | +| qualifiers.cpp:38:23:38:23 | a | AST only | +| qualifiers.cpp:42:6:42:22 | * ... | AST only | +| qualifiers.cpp:42:7:42:11 | outer | AST only | +| qualifiers.cpp:42:25:42:25 | a | AST only | +| qualifiers.cpp:43:10:43:14 | outer | AST only | +| qualifiers.cpp:43:16:43:20 | inner | AST only | +| qualifiers.cpp:43:23:43:23 | a | AST only | +| qualifiers.cpp:47:6:47:11 | & ... | AST only | +| qualifiers.cpp:47:15:47:22 | call to getInner | AST only | +| qualifiers.cpp:47:27:47:27 | a | AST only | +| qualifiers.cpp:48:10:48:14 | outer | AST only | +| qualifiers.cpp:48:16:48:20 | inner | AST only | +| qualifiers.cpp:48:23:48:23 | a | AST only | +| simple.cpp:20:24:20:25 | a_ | AST only | +| simple.cpp:20:24:20:25 | this | AST only | +| simple.cpp:21:24:21:25 | b_ | AST only | +| simple.cpp:21:24:21:25 | this | AST only | +| simple.cpp:28:10:28:10 | f | AST only | +| simple.cpp:29:10:29:10 | f | AST only | +| simple.cpp:39:5:39:5 | f | AST only | +| simple.cpp:40:5:40:5 | g | AST only | +| simple.cpp:41:5:41:5 | h | AST only | +| simple.cpp:42:5:42:5 | h | AST only | +| simple.cpp:45:9:45:9 | f | AST only | +| simple.cpp:48:9:48:9 | g | AST only | +| simple.cpp:51:9:51:9 | h | AST only | +| simple.cpp:54:9:54:9 | i | AST only | +| simple.cpp:65:7:65:7 | i | AST only | +| simple.cpp:83:9:83:10 | f2 | AST only | +| simple.cpp:83:9:83:10 | this | AST only | +| simple.cpp:83:12:83:13 | f1 | AST only | +| struct_init.c:15:8:15:9 | ab | AST only | +| struct_init.c:15:12:15:12 | a | AST only | +| struct_init.c:16:8:16:9 | ab | AST only | +| struct_init.c:16:12:16:12 | b | AST only | +| struct_init.c:22:8:22:9 | ab | AST only | +| struct_init.c:22:11:22:11 | a | AST only | +| struct_init.c:23:8:23:9 | ab | AST only | +| struct_init.c:23:11:23:11 | b | AST only | +| struct_init.c:24:10:24:12 | & ... | AST only | +| struct_init.c:31:8:31:12 | outer | AST only | +| struct_init.c:31:14:31:21 | nestedAB | AST only | +| struct_init.c:31:23:31:23 | a | AST only | +| struct_init.c:32:8:32:12 | outer | AST only | +| struct_init.c:32:14:32:21 | nestedAB | AST only | +| struct_init.c:32:23:32:23 | b | AST only | +| struct_init.c:33:8:33:12 | outer | AST only | +| struct_init.c:33:14:33:22 | pointerAB | AST only | +| struct_init.c:33:25:33:25 | a | AST only | +| struct_init.c:34:8:34:12 | outer | AST only | +| struct_init.c:34:14:34:22 | pointerAB | AST only | +| struct_init.c:34:25:34:25 | b | AST only | +| struct_init.c:36:10:36:24 | & ... | AST only | +| struct_init.c:36:11:36:15 | outer | AST only | +| struct_init.c:46:10:46:14 | outer | AST only | +| struct_init.c:46:16:46:24 | pointerAB | AST only | diff --git a/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.ql b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.ql new file mode 100644 index 00000000000..8f6296290e9 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.ql @@ -0,0 +1,58 @@ +/** + * @kind problem + */ + +import cpp +import semmle.code.cpp.ir.dataflow.DataFlow::DataFlow as IR +import semmle.code.cpp.dataflow.DataFlow::DataFlow as AST + +newtype TNode = + TASTNode(AST::Node n) or + TIRNode(IR::Node n) + +class Node extends TNode { + string toString() { none() } + + IR::Node asIR() { none() } + + AST::Node asAST() { none() } + + Location getLocation() { none() } +} + +class ASTNode extends Node, TASTNode { + AST::Node n; + + ASTNode() { this = TASTNode(n) } + + override string toString() { result = n.asPartialDefinition().toString() } + + override AST::Node asAST() { result = n } + + override Location getLocation() { result = n.getLocation() } +} + +class IRNode extends Node, TIRNode { + IR::Node n; + + IRNode() { this = TIRNode(n) } + + override string toString() { result = n.asPartialDefinition().toString() } + + override IR::Node asIR() { result = n } + + override Location getLocation() { result = n.getLocation() } +} + +from Node node, AST::Node astNode, IR::Node irNode, string msg +where + node.asIR() = irNode and + exists(irNode.asPartialDefinition()) and + not exists(AST::Node otherNode | otherNode.asPartialDefinition() = irNode.asPartialDefinition()) and + msg = "IR only" + or + node.asAST() = astNode and + exists(astNode.asPartialDefinition()) and + not exists(IR::Node otherNode | otherNode.asPartialDefinition() = astNode.asPartialDefinition()) and + msg = "AST only" +select node, msg diff --git a/cpp/ql/test/library-tests/dataflow/fields/partial-definition-ir.expected b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-ir.expected new file mode 100644 index 00000000000..fdaab4a95e9 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-ir.expected @@ -0,0 +1,20 @@ +| A.cpp:100:5:100:6 | c1 | +| A.cpp:142:7:142:7 | b | +| aliasing.cpp:9:3:9:3 | s | +| aliasing.cpp:13:3:13:3 | s | +| aliasing.cpp:17:3:17:3 | s | +| aliasing.cpp:37:3:37:6 | ref1 | +| aliasing.cpp:42:3:42:4 | s2 | +| aliasing.cpp:49:3:49:7 | copy1 | +| aliasing.cpp:54:3:54:4 | s2 | +| aliasing.cpp:60:3:60:4 | s2 | +| aliasing.cpp:72:3:72:3 | s | +| aliasing.cpp:79:3:79:3 | s | +| aliasing.cpp:86:3:86:3 | s | +| aliasing.cpp:92:5:92:5 | s | +| by_reference.cpp:12:5:12:5 | s | +| by_reference.cpp:84:3:84:7 | inner | +| by_reference.cpp:88:3:88:7 | inner | +| qualifiers.cpp:12:49:12:53 | inner | +| qualifiers.cpp:13:51:13:55 | inner | +| simple.cpp:65:5:65:5 | a | diff --git a/cpp/ql/test/library-tests/dataflow/fields/partial-definition-ir.ql b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-ir.ql new file mode 100644 index 00000000000..0f0d3c41b88 --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-ir.ql @@ -0,0 +1,8 @@ +/** + * @kind problem + */ + +import cpp +import semmle.code.cpp.ir.dataflow.DataFlow::DataFlow + +select any(Node n).asPartialDefinition() diff --git a/cpp/ql/test/library-tests/dataflow/fields/partial-definition.expected b/cpp/ql/test/library-tests/dataflow/fields/partial-definition.expected new file mode 100644 index 00000000000..8ba1638dc8d --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/fields/partial-definition.expected @@ -0,0 +1,390 @@ +| A.cpp:25:7:25:10 | this | +| A.cpp:25:13:25:13 | c | +| A.cpp:27:22:27:25 | this | +| A.cpp:27:28:27:28 | c | +| A.cpp:31:20:31:20 | c | +| A.cpp:40:5:40:6 | cc | +| A.cpp:41:5:41:6 | ct | +| A.cpp:42:10:42:12 | & ... | +| A.cpp:43:10:43:12 | & ... | +| A.cpp:48:20:48:20 | c | +| A.cpp:49:10:49:10 | b | +| A.cpp:49:13:49:13 | c | +| A.cpp:55:5:55:5 | b | +| A.cpp:56:10:56:10 | b | +| A.cpp:56:13:56:15 | call to get | +| A.cpp:57:28:57:30 | call to get | +| A.cpp:64:17:64:18 | b1 | +| A.cpp:65:10:65:11 | b1 | +| A.cpp:65:14:65:14 | c | +| A.cpp:66:10:66:11 | b2 | +| A.cpp:66:14:66:14 | c | +| A.cpp:73:21:73:22 | b1 | +| A.cpp:74:10:74:11 | b1 | +| A.cpp:74:14:74:14 | c | +| A.cpp:75:10:75:11 | b2 | +| A.cpp:75:14:75:14 | c | +| A.cpp:81:17:81:18 | b1 | +| A.cpp:81:21:81:21 | c | +| A.cpp:90:7:90:8 | b2 | +| A.cpp:90:15:90:15 | c | +| A.cpp:100:5:100:6 | c1 | +| A.cpp:100:9:100:9 | a | +| A.cpp:101:8:101:9 | c1 | +| A.cpp:107:12:107:13 | c1 | +| A.cpp:107:16:107:16 | a | +| A.cpp:120:12:120:13 | c1 | +| A.cpp:120:16:120:16 | a | +| A.cpp:126:5:126:5 | b | +| A.cpp:131:8:131:8 | b | +| A.cpp:132:10:132:10 | b | +| A.cpp:132:13:132:13 | c | +| A.cpp:142:7:142:7 | b | +| A.cpp:142:10:142:10 | c | +| A.cpp:143:7:143:10 | this | +| A.cpp:143:13:143:13 | b | +| A.cpp:151:18:151:18 | b | +| A.cpp:152:10:152:10 | d | +| A.cpp:152:13:152:13 | b | +| A.cpp:153:10:153:10 | d | +| A.cpp:153:13:153:13 | b | +| A.cpp:153:16:153:16 | c | +| A.cpp:154:10:154:10 | b | +| A.cpp:154:13:154:13 | c | +| A.cpp:160:29:160:29 | b | +| A.cpp:161:38:161:39 | l1 | +| A.cpp:162:38:162:39 | l2 | +| A.cpp:163:10:163:11 | l3 | +| A.cpp:163:14:163:17 | head | +| A.cpp:164:10:164:11 | l3 | +| A.cpp:164:14:164:17 | next | +| A.cpp:164:20:164:23 | head | +| A.cpp:165:10:165:11 | l3 | +| A.cpp:165:14:165:17 | next | +| A.cpp:165:20:165:23 | next | +| A.cpp:165:26:165:29 | head | +| A.cpp:166:10:166:11 | l3 | +| A.cpp:166:14:166:17 | next | +| A.cpp:166:20:166:23 | next | +| A.cpp:166:26:166:29 | next | +| A.cpp:166:32:166:35 | head | +| A.cpp:169:12:169:12 | l | +| A.cpp:169:15:169:18 | head | +| A.cpp:183:7:183:10 | head | +| A.cpp:184:7:184:10 | this | +| A.cpp:184:13:184:16 | next | +| B.cpp:7:25:7:25 | e | +| B.cpp:8:25:8:26 | b1 | +| B.cpp:9:10:9:11 | b2 | +| B.cpp:9:14:9:17 | box1 | +| B.cpp:9:20:9:24 | elem1 | +| B.cpp:10:10:10:11 | b2 | +| B.cpp:10:14:10:17 | box1 | +| B.cpp:10:20:10:24 | elem2 | +| B.cpp:16:37:16:37 | e | +| B.cpp:17:25:17:26 | b1 | +| B.cpp:18:10:18:11 | b2 | +| B.cpp:18:14:18:17 | box1 | +| B.cpp:18:20:18:24 | elem1 | +| B.cpp:19:10:19:11 | b2 | +| B.cpp:19:14:19:17 | box1 | +| B.cpp:19:20:19:24 | elem2 | +| B.cpp:35:7:35:10 | this | +| B.cpp:35:13:35:17 | elem1 | +| B.cpp:36:7:36:10 | this | +| B.cpp:36:13:36:17 | elem2 | +| B.cpp:46:7:46:10 | this | +| B.cpp:46:13:46:16 | box1 | +| C.cpp:19:5:19:5 | c | +| C.cpp:24:5:24:8 | this | +| C.cpp:24:11:24:12 | s3 | +| D.cpp:9:21:9:24 | elem | +| D.cpp:11:29:11:32 | elem | +| D.cpp:16:21:16:23 | box | +| D.cpp:18:29:18:31 | box | +| D.cpp:22:10:22:11 | b2 | +| D.cpp:22:14:22:20 | call to getBox1 | +| D.cpp:22:25:22:31 | call to getElem | +| D.cpp:30:5:30:5 | b | +| D.cpp:30:8:30:10 | box | +| D.cpp:30:13:30:16 | elem | +| D.cpp:31:14:31:14 | b | +| D.cpp:37:5:37:5 | b | +| D.cpp:37:8:37:10 | box | +| D.cpp:37:21:37:21 | e | +| D.cpp:38:14:38:14 | b | +| D.cpp:44:5:44:5 | b | +| D.cpp:44:8:44:14 | call to getBox1 | +| D.cpp:44:19:44:22 | elem | +| D.cpp:45:14:45:14 | b | +| D.cpp:51:5:51:5 | b | +| D.cpp:51:8:51:14 | call to getBox1 | +| D.cpp:51:27:51:27 | e | +| D.cpp:52:14:52:14 | b | +| D.cpp:57:5:57:12 | boxfield | +| D.cpp:58:5:58:12 | boxfield | +| D.cpp:58:15:58:17 | box | +| D.cpp:58:20:58:23 | elem | +| D.cpp:64:10:64:17 | boxfield | +| D.cpp:64:20:64:22 | box | +| D.cpp:64:25:64:28 | elem | +| E.cpp:21:10:21:10 | p | +| E.cpp:21:13:21:16 | data | +| E.cpp:21:18:21:23 | buffer | +| E.cpp:28:21:28:23 | raw | +| E.cpp:29:21:29:21 | b | +| E.cpp:29:24:29:29 | buffer | +| E.cpp:30:21:30:21 | p | +| E.cpp:30:23:30:26 | data | +| E.cpp:30:28:30:33 | buffer | +| E.cpp:31:10:31:12 | raw | +| E.cpp:32:10:32:10 | b | +| E.cpp:32:13:32:18 | buffer | +| E.cpp:33:18:33:19 | & ... | +| aliasing.cpp:9:3:9:3 | s | +| aliasing.cpp:9:6:9:7 | m1 | +| aliasing.cpp:13:3:13:3 | s | +| aliasing.cpp:13:5:13:6 | m1 | +| aliasing.cpp:17:3:17:3 | s | +| aliasing.cpp:17:5:17:6 | m1 | +| aliasing.cpp:25:17:25:19 | & ... | +| aliasing.cpp:26:19:26:20 | s2 | +| aliasing.cpp:37:3:37:6 | ref1 | +| aliasing.cpp:37:8:37:9 | m1 | +| aliasing.cpp:42:3:42:4 | s2 | +| aliasing.cpp:42:6:42:7 | m1 | +| aliasing.cpp:49:3:49:7 | copy1 | +| aliasing.cpp:49:9:49:10 | m1 | +| aliasing.cpp:54:3:54:4 | s2 | +| aliasing.cpp:54:6:54:7 | m1 | +| aliasing.cpp:60:3:60:4 | s2 | +| aliasing.cpp:60:6:60:7 | m1 | +| aliasing.cpp:72:3:72:3 | s | +| aliasing.cpp:72:5:72:6 | m1 | +| aliasing.cpp:79:3:79:3 | s | +| aliasing.cpp:79:6:79:7 | m1 | +| aliasing.cpp:86:3:86:3 | s | +| aliasing.cpp:86:5:86:6 | m1 | +| aliasing.cpp:92:3:92:3 | w | +| aliasing.cpp:92:5:92:5 | s | +| aliasing.cpp:92:7:92:8 | m1 | +| by_reference.cpp:12:5:12:5 | s | +| by_reference.cpp:12:8:12:8 | a | +| by_reference.cpp:16:5:16:8 | this | +| by_reference.cpp:16:11:16:11 | a | +| by_reference.cpp:20:5:20:8 | this | +| by_reference.cpp:20:23:20:27 | value | +| by_reference.cpp:24:19:24:22 | this | +| by_reference.cpp:24:25:24:29 | value | +| by_reference.cpp:50:3:50:3 | s | +| by_reference.cpp:50:17:50:26 | call to user_input | +| by_reference.cpp:51:10:51:20 | call to getDirectly | +| by_reference.cpp:56:3:56:3 | s | +| by_reference.cpp:56:19:56:28 | call to user_input | +| by_reference.cpp:57:10:57:22 | call to getIndirectly | +| by_reference.cpp:62:3:62:3 | s | +| by_reference.cpp:62:25:62:34 | call to user_input | +| by_reference.cpp:63:10:63:28 | call to getThroughNonMember | +| by_reference.cpp:68:17:68:18 | & ... | +| by_reference.cpp:68:21:68:30 | call to user_input | +| by_reference.cpp:69:8:69:20 | call to nonMemberGetA | +| by_reference.cpp:84:3:84:7 | inner | +| by_reference.cpp:84:10:84:10 | a | +| by_reference.cpp:88:3:88:7 | inner | +| by_reference.cpp:88:9:88:9 | a | +| by_reference.cpp:102:21:102:39 | & ... | +| by_reference.cpp:102:22:102:26 | outer | +| by_reference.cpp:103:21:103:25 | outer | +| by_reference.cpp:103:27:103:35 | inner_ptr | +| by_reference.cpp:104:15:104:22 | & ... | +| by_reference.cpp:104:16:104:20 | outer | +| by_reference.cpp:106:21:106:41 | & ... | +| by_reference.cpp:106:22:106:27 | pouter | +| by_reference.cpp:107:21:107:26 | pouter | +| by_reference.cpp:107:29:107:37 | inner_ptr | +| by_reference.cpp:108:15:108:24 | & ... | +| by_reference.cpp:108:16:108:21 | pouter | +| by_reference.cpp:110:8:110:12 | outer | +| by_reference.cpp:110:14:110:25 | inner_nested | +| by_reference.cpp:110:27:110:27 | a | +| by_reference.cpp:111:8:111:12 | outer | +| by_reference.cpp:111:14:111:22 | inner_ptr | +| by_reference.cpp:111:25:111:25 | a | +| by_reference.cpp:112:8:112:12 | outer | +| by_reference.cpp:112:14:112:14 | a | +| by_reference.cpp:114:8:114:13 | pouter | +| by_reference.cpp:114:16:114:27 | inner_nested | +| by_reference.cpp:114:29:114:29 | a | +| by_reference.cpp:115:8:115:13 | pouter | +| by_reference.cpp:115:16:115:24 | inner_ptr | +| by_reference.cpp:115:27:115:27 | a | +| by_reference.cpp:116:8:116:13 | pouter | +| by_reference.cpp:116:16:116:16 | a | +| by_reference.cpp:122:21:122:25 | outer | +| by_reference.cpp:122:27:122:38 | inner_nested | +| by_reference.cpp:123:21:123:36 | * ... | +| by_reference.cpp:123:22:123:26 | outer | +| by_reference.cpp:124:15:124:19 | outer | +| by_reference.cpp:124:21:124:21 | a | +| by_reference.cpp:126:21:126:26 | pouter | +| by_reference.cpp:126:29:126:40 | inner_nested | +| by_reference.cpp:127:21:127:38 | * ... | +| by_reference.cpp:127:22:127:27 | pouter | +| by_reference.cpp:128:15:128:20 | pouter | +| by_reference.cpp:128:23:128:23 | a | +| by_reference.cpp:130:8:130:12 | outer | +| by_reference.cpp:130:14:130:25 | inner_nested | +| by_reference.cpp:130:27:130:27 | a | +| by_reference.cpp:131:8:131:12 | outer | +| by_reference.cpp:131:14:131:22 | inner_ptr | +| by_reference.cpp:131:25:131:25 | a | +| by_reference.cpp:132:8:132:12 | outer | +| by_reference.cpp:132:14:132:14 | a | +| by_reference.cpp:134:8:134:13 | pouter | +| by_reference.cpp:134:16:134:27 | inner_nested | +| by_reference.cpp:134:29:134:29 | a | +| by_reference.cpp:135:8:135:13 | pouter | +| by_reference.cpp:135:16:135:24 | inner_ptr | +| by_reference.cpp:135:27:135:27 | a | +| by_reference.cpp:136:8:136:13 | pouter | +| by_reference.cpp:136:16:136:16 | a | +| complex.cpp:11:22:11:23 | a_ | +| complex.cpp:12:22:12:23 | b_ | +| complex.cpp:51:8:51:8 | b | +| complex.cpp:51:10:51:14 | inner | +| complex.cpp:51:16:51:16 | f | +| complex.cpp:52:8:52:8 | b | +| complex.cpp:52:10:52:14 | inner | +| complex.cpp:52:16:52:16 | f | +| complex.cpp:62:3:62:4 | b1 | +| complex.cpp:62:6:62:10 | inner | +| complex.cpp:62:12:62:12 | f | +| complex.cpp:63:3:63:4 | b2 | +| complex.cpp:63:6:63:10 | inner | +| complex.cpp:63:12:63:12 | f | +| complex.cpp:64:3:64:4 | b3 | +| complex.cpp:64:6:64:10 | inner | +| complex.cpp:64:12:64:12 | f | +| complex.cpp:65:3:65:4 | b3 | +| complex.cpp:65:6:65:10 | inner | +| complex.cpp:65:12:65:12 | f | +| complex.cpp:68:7:68:8 | b1 | +| complex.cpp:71:7:71:8 | b2 | +| complex.cpp:74:7:74:8 | b3 | +| complex.cpp:77:7:77:8 | b4 | +| constructors.cpp:20:24:20:25 | a_ | +| constructors.cpp:21:24:21:25 | b_ | +| constructors.cpp:28:10:28:10 | f | +| constructors.cpp:29:10:29:10 | f | +| constructors.cpp:40:9:40:9 | f | +| constructors.cpp:43:9:43:9 | g | +| constructors.cpp:46:9:46:9 | h | +| constructors.cpp:49:9:49:9 | i | +| file://:0:0:0:0 | this | +| file://:0:0:0:0 | this | +| file://:0:0:0:0 | this | +| file://:0:0:0:0 | this | +| file://:0:0:0:0 | this | +| file://:0:0:0:0 | this | +| file://:0:0:0:0 | this | +| file://:0:0:0:0 | this | +| file://:0:0:0:0 | this | +| file://:0:0:0:0 | this | +| file://:0:0:0:0 | this | +| file://:0:0:0:0 | this | +| file://:0:0:0:0 | this | +| file://:0:0:0:0 | this | +| file://:0:0:0:0 | this | +| file://:0:0:0:0 | this | +| file://:0:0:0:0 | this | +| file://:0:0:0:0 | this | +| file://:0:0:0:0 | this | +| file://:0:0:0:0 | this | +| file://:0:0:0:0 | this | +| file://:0:0:0:0 | this | +| file://:0:0:0:0 | this | +| file://:0:0:0:0 | this | +| file://:0:0:0:0 | this | +| qualifiers.cpp:9:30:9:33 | this | +| qualifiers.cpp:9:36:9:36 | a | +| qualifiers.cpp:12:49:12:53 | inner | +| qualifiers.cpp:12:56:12:56 | a | +| qualifiers.cpp:13:51:13:55 | inner | +| qualifiers.cpp:13:57:13:57 | a | +| qualifiers.cpp:22:5:22:9 | outer | +| qualifiers.cpp:22:11:22:18 | call to getInner | +| qualifiers.cpp:22:23:22:23 | a | +| qualifiers.cpp:23:10:23:14 | outer | +| qualifiers.cpp:23:16:23:20 | inner | +| qualifiers.cpp:23:23:23:23 | a | +| qualifiers.cpp:27:5:27:9 | outer | +| qualifiers.cpp:27:11:27:18 | call to getInner | +| qualifiers.cpp:27:28:27:37 | call to user_input | +| qualifiers.cpp:28:10:28:14 | outer | +| qualifiers.cpp:28:16:28:20 | inner | +| qualifiers.cpp:28:23:28:23 | a | +| qualifiers.cpp:32:17:32:21 | outer | +| qualifiers.cpp:32:23:32:30 | call to getInner | +| qualifiers.cpp:32:35:32:44 | call to user_input | +| qualifiers.cpp:33:10:33:14 | outer | +| qualifiers.cpp:33:16:33:20 | inner | +| qualifiers.cpp:33:23:33:23 | a | +| qualifiers.cpp:37:19:37:35 | * ... | +| qualifiers.cpp:37:20:37:24 | outer | +| qualifiers.cpp:37:38:37:47 | call to user_input | +| qualifiers.cpp:38:10:38:14 | outer | +| qualifiers.cpp:38:16:38:20 | inner | +| qualifiers.cpp:38:23:38:23 | a | +| qualifiers.cpp:42:6:42:22 | * ... | +| qualifiers.cpp:42:7:42:11 | outer | +| qualifiers.cpp:42:25:42:25 | a | +| qualifiers.cpp:43:10:43:14 | outer | +| qualifiers.cpp:43:16:43:20 | inner | +| qualifiers.cpp:43:23:43:23 | a | +| qualifiers.cpp:47:6:47:11 | & ... | +| qualifiers.cpp:47:15:47:22 | call to getInner | +| qualifiers.cpp:47:27:47:27 | a | +| qualifiers.cpp:48:10:48:14 | outer | +| qualifiers.cpp:48:16:48:20 | inner | +| qualifiers.cpp:48:23:48:23 | a | +| simple.cpp:20:24:20:25 | a_ | +| simple.cpp:21:24:21:25 | b_ | +| simple.cpp:28:10:28:10 | f | +| simple.cpp:29:10:29:10 | f | +| simple.cpp:39:5:39:5 | f | +| simple.cpp:40:5:40:5 | g | +| simple.cpp:41:5:41:5 | h | +| simple.cpp:42:5:42:5 | h | +| simple.cpp:45:9:45:9 | f | +| simple.cpp:48:9:48:9 | g | +| simple.cpp:51:9:51:9 | h | +| simple.cpp:54:9:54:9 | i | +| simple.cpp:65:5:65:5 | a | +| simple.cpp:65:7:65:7 | i | +| simple.cpp:83:9:83:10 | f2 | +| simple.cpp:83:12:83:13 | f1 | +| struct_init.c:15:8:15:9 | ab | +| struct_init.c:15:12:15:12 | a | +| struct_init.c:16:8:16:9 | ab | +| struct_init.c:16:12:16:12 | b | +| struct_init.c:22:8:22:9 | ab | +| struct_init.c:22:11:22:11 | a | +| struct_init.c:23:8:23:9 | ab | +| struct_init.c:23:11:23:11 | b | +| struct_init.c:24:10:24:12 | & ... | +| struct_init.c:31:8:31:12 | outer | +| struct_init.c:31:14:31:21 | nestedAB | +| struct_init.c:31:23:31:23 | a | +| struct_init.c:32:8:32:12 | outer | +| struct_init.c:32:14:32:21 | nestedAB | +| struct_init.c:32:23:32:23 | b | +| struct_init.c:33:8:33:12 | outer | +| struct_init.c:33:14:33:22 | pointerAB | +| struct_init.c:33:25:33:25 | a | +| struct_init.c:34:8:34:12 | outer | +| struct_init.c:34:14:34:22 | pointerAB | +| struct_init.c:34:25:34:25 | b | +| struct_init.c:36:10:36:24 | & ... | +| struct_init.c:36:11:36:15 | outer | +| struct_init.c:46:10:46:14 | outer | +| struct_init.c:46:16:46:24 | pointerAB | diff --git a/cpp/ql/test/library-tests/dataflow/fields/partial-definition.ql b/cpp/ql/test/library-tests/dataflow/fields/partial-definition.ql new file mode 100644 index 00000000000..8acd1f3e5fe --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/fields/partial-definition.ql @@ -0,0 +1,8 @@ +/** + * @kind problem + */ + +import cpp +import semmle.code.cpp.dataflow.DataFlow::DataFlow + +select any(Node n).asPartialDefinition() diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/format.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/format.cpp index 2080707f17f..974ea6040e8 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/format.cpp +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/format.cpp @@ -15,10 +15,14 @@ int vsnprintf(char *s, size_t n, const char *format, va_list arg); int mysprintf(char *s, size_t n, const char *format, ...) { + int result; + va_list args; va_start(args, format); - vsnprintf(s, n, format, args); + result = vsnprintf(s, n, format, args); va_end(args); + + return result; } int sscanf(const char *s, const char *format, ...); diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected index 764cc9f24e9..c7d58f99966 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/localTaint.expected @@ -3,112 +3,114 @@ | file://:0:0:0:0 | p#0 | file://:0:0:0:0 | p#0 | | | file://:0:0:0:0 | p#0 | file://:0:0:0:0 | p#0 | | | file://:0:0:0:0 | p#0 | file://:0:0:0:0 | p#0 | | -| format.cpp:16:21:16:21 | s | format.cpp:20:13:20:13 | s | | -| format.cpp:16:31:16:31 | n | format.cpp:20:16:20:16 | n | | -| format.cpp:16:46:16:51 | format | format.cpp:20:19:20:24 | format | | -| format.cpp:18:10:18:13 | args | format.cpp:20:27:20:30 | args | | -| format.cpp:46:21:46:24 | {...} | format.cpp:47:17:47:22 | buffer | | -| format.cpp:46:21:46:24 | {...} | format.cpp:48:8:48:13 | buffer | | -| format.cpp:46:23:46:23 | 0 | format.cpp:46:21:46:24 | {...} | TAINT | -| format.cpp:47:17:47:22 | ref arg buffer | format.cpp:48:8:48:13 | buffer | | -| format.cpp:47:30:47:33 | %s | format.cpp:47:17:47:22 | ref arg buffer | TAINT | -| format.cpp:47:36:47:43 | Hello. | format.cpp:47:17:47:22 | ref arg buffer | TAINT | -| format.cpp:51:21:51:24 | {...} | format.cpp:52:17:52:22 | buffer | | -| format.cpp:51:21:51:24 | {...} | format.cpp:53:8:53:13 | buffer | | -| format.cpp:51:23:51:23 | 0 | format.cpp:51:21:51:24 | {...} | TAINT | -| format.cpp:52:17:52:22 | ref arg buffer | format.cpp:53:8:53:13 | buffer | | -| format.cpp:52:30:52:33 | %s | format.cpp:52:17:52:22 | ref arg buffer | TAINT | -| format.cpp:52:36:52:49 | call to source | format.cpp:52:17:52:22 | ref arg buffer | TAINT | -| format.cpp:56:21:56:24 | {...} | format.cpp:57:17:57:22 | buffer | | -| format.cpp:56:21:56:24 | {...} | format.cpp:58:8:58:13 | buffer | | -| format.cpp:56:23:56:23 | 0 | format.cpp:56:21:56:24 | {...} | TAINT | -| format.cpp:57:17:57:22 | ref arg buffer | format.cpp:58:8:58:13 | buffer | | -| format.cpp:57:30:57:43 | call to source | format.cpp:57:17:57:22 | ref arg buffer | TAINT | -| format.cpp:57:48:57:55 | Hello. | format.cpp:57:17:57:22 | ref arg buffer | TAINT | -| format.cpp:61:21:61:24 | {...} | format.cpp:62:17:62:22 | buffer | | -| format.cpp:61:21:61:24 | {...} | format.cpp:63:8:63:13 | buffer | | -| format.cpp:61:23:61:23 | 0 | format.cpp:61:21:61:24 | {...} | TAINT | -| format.cpp:62:17:62:22 | ref arg buffer | format.cpp:63:8:63:13 | buffer | | -| format.cpp:62:30:62:39 | %s %s %s | format.cpp:62:17:62:22 | ref arg buffer | TAINT | -| format.cpp:62:42:62:44 | a | format.cpp:62:17:62:22 | ref arg buffer | TAINT | -| format.cpp:62:47:62:49 | b | format.cpp:62:17:62:22 | ref arg buffer | TAINT | -| format.cpp:62:52:62:65 | call to source | format.cpp:62:17:62:22 | ref arg buffer | TAINT | -| format.cpp:66:21:66:24 | {...} | format.cpp:67:17:67:22 | buffer | | -| format.cpp:66:21:66:24 | {...} | format.cpp:68:8:68:13 | buffer | | -| format.cpp:66:23:66:23 | 0 | format.cpp:66:21:66:24 | {...} | TAINT | -| format.cpp:67:17:67:22 | ref arg buffer | format.cpp:68:8:68:13 | buffer | | -| format.cpp:67:30:67:35 | %.*s | format.cpp:67:17:67:22 | ref arg buffer | TAINT | -| format.cpp:67:38:67:39 | 10 | format.cpp:67:17:67:22 | ref arg buffer | TAINT | -| format.cpp:67:42:67:55 | call to source | format.cpp:67:17:67:22 | ref arg buffer | TAINT | -| format.cpp:72:21:72:24 | {...} | format.cpp:73:17:73:22 | buffer | | -| format.cpp:72:21:72:24 | {...} | format.cpp:74:8:74:13 | buffer | | -| format.cpp:72:23:72:23 | 0 | format.cpp:72:21:72:24 | {...} | TAINT | -| format.cpp:73:17:73:22 | ref arg buffer | format.cpp:74:8:74:13 | buffer | | -| format.cpp:73:30:73:33 | %i | format.cpp:73:17:73:22 | ref arg buffer | TAINT | -| format.cpp:73:36:73:36 | 0 | format.cpp:73:17:73:22 | ref arg buffer | TAINT | -| format.cpp:77:21:77:24 | {...} | format.cpp:78:17:78:22 | buffer | | -| format.cpp:77:21:77:24 | {...} | format.cpp:79:8:79:13 | buffer | | -| format.cpp:77:23:77:23 | 0 | format.cpp:77:21:77:24 | {...} | TAINT | -| format.cpp:78:17:78:22 | ref arg buffer | format.cpp:79:8:79:13 | buffer | | -| format.cpp:78:30:78:33 | %i | format.cpp:78:17:78:22 | ref arg buffer | TAINT | -| format.cpp:78:36:78:41 | call to source | format.cpp:78:17:78:22 | ref arg buffer | TAINT | -| format.cpp:82:21:82:24 | {...} | format.cpp:83:17:83:22 | buffer | | -| format.cpp:82:21:82:24 | {...} | format.cpp:84:8:84:13 | buffer | | -| format.cpp:82:23:82:23 | 0 | format.cpp:82:21:82:24 | {...} | TAINT | -| format.cpp:83:17:83:22 | ref arg buffer | format.cpp:84:8:84:13 | buffer | | -| format.cpp:83:30:83:35 | %.*s | format.cpp:83:17:83:22 | ref arg buffer | TAINT | -| format.cpp:83:38:83:43 | call to source | format.cpp:83:17:83:22 | ref arg buffer | TAINT | -| format.cpp:83:48:83:55 | Hello. | format.cpp:83:17:83:22 | ref arg buffer | TAINT | -| format.cpp:88:21:88:24 | {...} | format.cpp:89:17:89:22 | buffer | | -| format.cpp:88:21:88:24 | {...} | format.cpp:90:8:90:13 | buffer | | -| format.cpp:88:23:88:23 | 0 | format.cpp:88:21:88:24 | {...} | TAINT | -| format.cpp:89:17:89:22 | ref arg buffer | format.cpp:90:8:90:13 | buffer | | -| format.cpp:89:30:89:33 | %p | format.cpp:89:17:89:22 | ref arg buffer | TAINT | -| format.cpp:89:36:89:49 | call to source | format.cpp:89:17:89:22 | ref arg buffer | TAINT | -| format.cpp:94:21:94:24 | {...} | format.cpp:95:16:95:21 | buffer | | -| format.cpp:94:21:94:24 | {...} | format.cpp:96:8:96:13 | buffer | | -| format.cpp:94:23:94:23 | 0 | format.cpp:94:21:94:24 | {...} | TAINT | -| format.cpp:95:16:95:21 | ref arg buffer | format.cpp:96:8:96:13 | buffer | | -| format.cpp:95:24:95:27 | %s | format.cpp:95:16:95:21 | ref arg buffer | TAINT | -| format.cpp:95:30:95:43 | call to source | format.cpp:95:16:95:21 | ref arg buffer | TAINT | -| format.cpp:99:21:99:24 | {...} | format.cpp:100:16:100:21 | buffer | | -| format.cpp:99:21:99:24 | {...} | format.cpp:101:8:101:13 | buffer | | -| format.cpp:99:23:99:23 | 0 | format.cpp:99:21:99:24 | {...} | TAINT | -| format.cpp:100:16:100:21 | ref arg buffer | format.cpp:101:8:101:13 | buffer | | -| format.cpp:100:24:100:28 | %ls | format.cpp:100:16:100:21 | ref arg buffer | TAINT | -| format.cpp:100:31:100:45 | call to source | format.cpp:100:16:100:21 | ref arg buffer | TAINT | -| format.cpp:104:25:104:28 | {...} | format.cpp:105:17:105:23 | wbuffer | | -| format.cpp:104:25:104:28 | {...} | format.cpp:106:8:106:14 | wbuffer | | -| format.cpp:104:27:104:27 | 0 | format.cpp:104:25:104:28 | {...} | TAINT | -| format.cpp:105:17:105:23 | ref arg wbuffer | format.cpp:106:8:106:14 | wbuffer | | -| format.cpp:105:31:105:35 | %s | format.cpp:105:17:105:23 | ref arg wbuffer | TAINT | -| format.cpp:105:38:105:52 | call to source | format.cpp:105:17:105:23 | ref arg wbuffer | TAINT | -| format.cpp:109:21:109:24 | {...} | format.cpp:110:18:110:23 | buffer | | -| format.cpp:109:21:109:24 | {...} | format.cpp:111:8:111:13 | buffer | | -| format.cpp:109:23:109:23 | 0 | format.cpp:109:21:109:24 | {...} | TAINT | -| format.cpp:110:18:110:23 | ref arg buffer | format.cpp:111:8:111:13 | buffer | | -| format.cpp:115:10:115:11 | 0 | format.cpp:116:29:116:29 | i | | -| format.cpp:115:10:115:11 | 0 | format.cpp:117:8:117:8 | i | | -| format.cpp:116:28:116:29 | ref arg & ... | format.cpp:116:29:116:29 | i [inner post update] | | -| format.cpp:116:28:116:29 | ref arg & ... | format.cpp:117:8:117:8 | i | | -| format.cpp:116:29:116:29 | i | format.cpp:116:28:116:29 | & ... | | -| format.cpp:120:10:120:11 | 0 | format.cpp:121:40:121:40 | i | | -| format.cpp:120:10:120:11 | 0 | format.cpp:122:8:122:8 | i | | -| format.cpp:121:39:121:40 | ref arg & ... | format.cpp:121:40:121:40 | i [inner post update] | | -| format.cpp:121:39:121:40 | ref arg & ... | format.cpp:122:8:122:8 | i | | -| format.cpp:121:40:121:40 | i | format.cpp:121:39:121:40 | & ... | | -| format.cpp:125:21:125:24 | {...} | format.cpp:126:32:126:37 | buffer | | -| format.cpp:125:21:125:24 | {...} | format.cpp:127:8:127:13 | buffer | | -| format.cpp:125:23:125:23 | 0 | format.cpp:125:21:125:24 | {...} | TAINT | -| format.cpp:126:31:126:37 | ref arg & ... | format.cpp:126:32:126:37 | buffer [inner post update] | | -| format.cpp:126:31:126:37 | ref arg & ... | format.cpp:127:8:127:13 | buffer | | -| format.cpp:126:32:126:37 | buffer | format.cpp:126:31:126:37 | & ... | | -| format.cpp:130:21:130:24 | {...} | format.cpp:131:40:131:45 | buffer | | -| format.cpp:130:21:130:24 | {...} | format.cpp:132:8:132:13 | buffer | | -| format.cpp:130:23:130:23 | 0 | format.cpp:130:21:130:24 | {...} | TAINT | -| format.cpp:131:39:131:45 | ref arg & ... | format.cpp:131:40:131:45 | buffer [inner post update] | | -| format.cpp:131:39:131:45 | ref arg & ... | format.cpp:132:8:132:13 | buffer | | -| format.cpp:131:40:131:45 | buffer | format.cpp:131:39:131:45 | & ... | | +| format.cpp:16:21:16:21 | s | format.cpp:22:22:22:22 | s | | +| format.cpp:16:31:16:31 | n | format.cpp:22:25:22:25 | n | | +| format.cpp:16:46:16:51 | format | format.cpp:22:28:22:33 | format | | +| format.cpp:20:10:20:13 | args | format.cpp:22:36:22:39 | args | | +| format.cpp:22:12:22:20 | call to vsnprintf | format.cpp:22:3:22:40 | ... = ... | | +| format.cpp:22:12:22:20 | call to vsnprintf | format.cpp:25:9:25:14 | result | | +| format.cpp:50:21:50:24 | {...} | format.cpp:51:17:51:22 | buffer | | +| format.cpp:50:21:50:24 | {...} | format.cpp:52:8:52:13 | buffer | | +| format.cpp:50:23:50:23 | 0 | format.cpp:50:21:50:24 | {...} | TAINT | +| format.cpp:51:17:51:22 | ref arg buffer | format.cpp:52:8:52:13 | buffer | | +| format.cpp:51:30:51:33 | %s | format.cpp:51:17:51:22 | ref arg buffer | TAINT | +| format.cpp:51:36:51:43 | Hello. | format.cpp:51:17:51:22 | ref arg buffer | TAINT | +| format.cpp:55:21:55:24 | {...} | format.cpp:56:17:56:22 | buffer | | +| format.cpp:55:21:55:24 | {...} | format.cpp:57:8:57:13 | buffer | | +| format.cpp:55:23:55:23 | 0 | format.cpp:55:21:55:24 | {...} | TAINT | +| format.cpp:56:17:56:22 | ref arg buffer | format.cpp:57:8:57:13 | buffer | | +| format.cpp:56:30:56:33 | %s | format.cpp:56:17:56:22 | ref arg buffer | TAINT | +| format.cpp:56:36:56:49 | call to source | format.cpp:56:17:56:22 | ref arg buffer | TAINT | +| format.cpp:60:21:60:24 | {...} | format.cpp:61:17:61:22 | buffer | | +| format.cpp:60:21:60:24 | {...} | format.cpp:62:8:62:13 | buffer | | +| format.cpp:60:23:60:23 | 0 | format.cpp:60:21:60:24 | {...} | TAINT | +| format.cpp:61:17:61:22 | ref arg buffer | format.cpp:62:8:62:13 | buffer | | +| format.cpp:61:30:61:43 | call to source | format.cpp:61:17:61:22 | ref arg buffer | TAINT | +| format.cpp:61:48:61:55 | Hello. | format.cpp:61:17:61:22 | ref arg buffer | TAINT | +| format.cpp:65:21:65:24 | {...} | format.cpp:66:17:66:22 | buffer | | +| format.cpp:65:21:65:24 | {...} | format.cpp:67:8:67:13 | buffer | | +| format.cpp:65:23:65:23 | 0 | format.cpp:65:21:65:24 | {...} | TAINT | +| format.cpp:66:17:66:22 | ref arg buffer | format.cpp:67:8:67:13 | buffer | | +| format.cpp:66:30:66:39 | %s %s %s | format.cpp:66:17:66:22 | ref arg buffer | TAINT | +| format.cpp:66:42:66:44 | a | format.cpp:66:17:66:22 | ref arg buffer | TAINT | +| format.cpp:66:47:66:49 | b | format.cpp:66:17:66:22 | ref arg buffer | TAINT | +| format.cpp:66:52:66:65 | call to source | format.cpp:66:17:66:22 | ref arg buffer | TAINT | +| format.cpp:70:21:70:24 | {...} | format.cpp:71:17:71:22 | buffer | | +| format.cpp:70:21:70:24 | {...} | format.cpp:72:8:72:13 | buffer | | +| format.cpp:70:23:70:23 | 0 | format.cpp:70:21:70:24 | {...} | TAINT | +| format.cpp:71:17:71:22 | ref arg buffer | format.cpp:72:8:72:13 | buffer | | +| format.cpp:71:30:71:35 | %.*s | format.cpp:71:17:71:22 | ref arg buffer | TAINT | +| format.cpp:71:38:71:39 | 10 | format.cpp:71:17:71:22 | ref arg buffer | TAINT | +| format.cpp:71:42:71:55 | call to source | format.cpp:71:17:71:22 | ref arg buffer | TAINT | +| format.cpp:76:21:76:24 | {...} | format.cpp:77:17:77:22 | buffer | | +| format.cpp:76:21:76:24 | {...} | format.cpp:78:8:78:13 | buffer | | +| format.cpp:76:23:76:23 | 0 | format.cpp:76:21:76:24 | {...} | TAINT | +| format.cpp:77:17:77:22 | ref arg buffer | format.cpp:78:8:78:13 | buffer | | +| format.cpp:77:30:77:33 | %i | format.cpp:77:17:77:22 | ref arg buffer | TAINT | +| format.cpp:77:36:77:36 | 0 | format.cpp:77:17:77:22 | ref arg buffer | TAINT | +| format.cpp:81:21:81:24 | {...} | format.cpp:82:17:82:22 | buffer | | +| format.cpp:81:21:81:24 | {...} | format.cpp:83:8:83:13 | buffer | | +| format.cpp:81:23:81:23 | 0 | format.cpp:81:21:81:24 | {...} | TAINT | +| format.cpp:82:17:82:22 | ref arg buffer | format.cpp:83:8:83:13 | buffer | | +| format.cpp:82:30:82:33 | %i | format.cpp:82:17:82:22 | ref arg buffer | TAINT | +| format.cpp:82:36:82:41 | call to source | format.cpp:82:17:82:22 | ref arg buffer | TAINT | +| format.cpp:86:21:86:24 | {...} | format.cpp:87:17:87:22 | buffer | | +| format.cpp:86:21:86:24 | {...} | format.cpp:88:8:88:13 | buffer | | +| format.cpp:86:23:86:23 | 0 | format.cpp:86:21:86:24 | {...} | TAINT | +| format.cpp:87:17:87:22 | ref arg buffer | format.cpp:88:8:88:13 | buffer | | +| format.cpp:87:30:87:35 | %.*s | format.cpp:87:17:87:22 | ref arg buffer | TAINT | +| format.cpp:87:38:87:43 | call to source | format.cpp:87:17:87:22 | ref arg buffer | TAINT | +| format.cpp:87:48:87:55 | Hello. | format.cpp:87:17:87:22 | ref arg buffer | TAINT | +| format.cpp:92:21:92:24 | {...} | format.cpp:93:17:93:22 | buffer | | +| format.cpp:92:21:92:24 | {...} | format.cpp:94:8:94:13 | buffer | | +| format.cpp:92:23:92:23 | 0 | format.cpp:92:21:92:24 | {...} | TAINT | +| format.cpp:93:17:93:22 | ref arg buffer | format.cpp:94:8:94:13 | buffer | | +| format.cpp:93:30:93:33 | %p | format.cpp:93:17:93:22 | ref arg buffer | TAINT | +| format.cpp:93:36:93:49 | call to source | format.cpp:93:17:93:22 | ref arg buffer | TAINT | +| format.cpp:98:21:98:24 | {...} | format.cpp:99:16:99:21 | buffer | | +| format.cpp:98:21:98:24 | {...} | format.cpp:100:8:100:13 | buffer | | +| format.cpp:98:23:98:23 | 0 | format.cpp:98:21:98:24 | {...} | TAINT | +| format.cpp:99:16:99:21 | ref arg buffer | format.cpp:100:8:100:13 | buffer | | +| format.cpp:99:24:99:27 | %s | format.cpp:99:16:99:21 | ref arg buffer | TAINT | +| format.cpp:99:30:99:43 | call to source | format.cpp:99:16:99:21 | ref arg buffer | TAINT | +| format.cpp:103:21:103:24 | {...} | format.cpp:104:16:104:21 | buffer | | +| format.cpp:103:21:103:24 | {...} | format.cpp:105:8:105:13 | buffer | | +| format.cpp:103:23:103:23 | 0 | format.cpp:103:21:103:24 | {...} | TAINT | +| format.cpp:104:16:104:21 | ref arg buffer | format.cpp:105:8:105:13 | buffer | | +| format.cpp:104:24:104:28 | %ls | format.cpp:104:16:104:21 | ref arg buffer | TAINT | +| format.cpp:104:31:104:45 | call to source | format.cpp:104:16:104:21 | ref arg buffer | TAINT | +| format.cpp:108:25:108:28 | {...} | format.cpp:109:17:109:23 | wbuffer | | +| format.cpp:108:25:108:28 | {...} | format.cpp:110:8:110:14 | wbuffer | | +| format.cpp:108:27:108:27 | 0 | format.cpp:108:25:108:28 | {...} | TAINT | +| format.cpp:109:17:109:23 | ref arg wbuffer | format.cpp:110:8:110:14 | wbuffer | | +| format.cpp:109:31:109:35 | %s | format.cpp:109:17:109:23 | ref arg wbuffer | TAINT | +| format.cpp:109:38:109:52 | call to source | format.cpp:109:17:109:23 | ref arg wbuffer | TAINT | +| format.cpp:113:21:113:24 | {...} | format.cpp:114:18:114:23 | buffer | | +| format.cpp:113:21:113:24 | {...} | format.cpp:115:8:115:13 | buffer | | +| format.cpp:113:23:113:23 | 0 | format.cpp:113:21:113:24 | {...} | TAINT | +| format.cpp:114:18:114:23 | ref arg buffer | format.cpp:115:8:115:13 | buffer | | +| format.cpp:119:10:119:11 | 0 | format.cpp:120:29:120:29 | i | | +| format.cpp:119:10:119:11 | 0 | format.cpp:121:8:121:8 | i | | +| format.cpp:120:28:120:29 | ref arg & ... | format.cpp:120:29:120:29 | i [inner post update] | | +| format.cpp:120:28:120:29 | ref arg & ... | format.cpp:121:8:121:8 | i | | +| format.cpp:120:29:120:29 | i | format.cpp:120:28:120:29 | & ... | | +| format.cpp:124:10:124:11 | 0 | format.cpp:125:40:125:40 | i | | +| format.cpp:124:10:124:11 | 0 | format.cpp:126:8:126:8 | i | | +| format.cpp:125:39:125:40 | ref arg & ... | format.cpp:125:40:125:40 | i [inner post update] | | +| format.cpp:125:39:125:40 | ref arg & ... | format.cpp:126:8:126:8 | i | | +| format.cpp:125:40:125:40 | i | format.cpp:125:39:125:40 | & ... | | +| format.cpp:129:21:129:24 | {...} | format.cpp:130:32:130:37 | buffer | | +| format.cpp:129:21:129:24 | {...} | format.cpp:131:8:131:13 | buffer | | +| format.cpp:129:23:129:23 | 0 | format.cpp:129:21:129:24 | {...} | TAINT | +| format.cpp:130:31:130:37 | ref arg & ... | format.cpp:130:32:130:37 | buffer [inner post update] | | +| format.cpp:130:31:130:37 | ref arg & ... | format.cpp:131:8:131:13 | buffer | | +| format.cpp:130:32:130:37 | buffer | format.cpp:130:31:130:37 | & ... | | +| format.cpp:134:21:134:24 | {...} | format.cpp:135:40:135:45 | buffer | | +| format.cpp:134:21:134:24 | {...} | format.cpp:136:8:136:13 | buffer | | +| format.cpp:134:23:134:23 | 0 | format.cpp:134:21:134:24 | {...} | TAINT | +| format.cpp:135:39:135:45 | ref arg & ... | format.cpp:135:40:135:45 | buffer [inner post update] | | +| format.cpp:135:39:135:45 | ref arg & ... | format.cpp:136:8:136:13 | buffer | | +| format.cpp:135:40:135:45 | buffer | format.cpp:135:39:135:45 | & ... | | | stl.cpp:67:12:67:17 | call to source | stl.cpp:71:7:71:7 | a | | | stl.cpp:68:16:68:20 | 123 | stl.cpp:68:16:68:21 | call to basic_string | TAINT | | stl.cpp:68:16:68:21 | call to basic_string | stl.cpp:72:7:72:7 | b | | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected index b9294f5b7ae..e873ff91915 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.expected @@ -1,13 +1,13 @@ -| format.cpp:53:8:53:13 | buffer | format.cpp:52:36:52:49 | call to source | -| format.cpp:58:8:58:13 | buffer | format.cpp:57:30:57:43 | call to source | -| format.cpp:63:8:63:13 | buffer | format.cpp:62:52:62:65 | call to source | -| format.cpp:68:8:68:13 | buffer | format.cpp:67:42:67:55 | call to source | -| format.cpp:79:8:79:13 | buffer | format.cpp:78:36:78:41 | call to source | -| format.cpp:84:8:84:13 | buffer | format.cpp:83:38:83:43 | call to source | -| format.cpp:90:8:90:13 | buffer | format.cpp:89:36:89:49 | call to source | -| format.cpp:96:8:96:13 | buffer | format.cpp:95:30:95:43 | call to source | -| format.cpp:101:8:101:13 | buffer | format.cpp:100:31:100:45 | call to source | -| format.cpp:106:8:106:14 | wbuffer | format.cpp:105:38:105:52 | call to source | +| format.cpp:57:8:57:13 | buffer | format.cpp:56:36:56:49 | call to source | +| format.cpp:62:8:62:13 | buffer | format.cpp:61:30:61:43 | call to source | +| format.cpp:67:8:67:13 | buffer | format.cpp:66:52:66:65 | call to source | +| format.cpp:72:8:72:13 | buffer | format.cpp:71:42:71:55 | call to source | +| format.cpp:83:8:83:13 | buffer | format.cpp:82:36:82:41 | call to source | +| format.cpp:88:8:88:13 | buffer | format.cpp:87:38:87:43 | call to source | +| format.cpp:94:8:94:13 | buffer | format.cpp:93:36:93:49 | call to source | +| format.cpp:100:8:100:13 | buffer | format.cpp:99:30:99:43 | call to source | +| format.cpp:105:8:105:13 | buffer | format.cpp:104:31:104:45 | call to source | +| format.cpp:110:8:110:14 | wbuffer | format.cpp:109:38:109:52 | call to source | | stl.cpp:71:7:71:7 | a | stl.cpp:67:12:67:17 | call to source | | stl.cpp:73:7:73:7 | c | stl.cpp:69:16:69:21 | call to source | | stl.cpp:75:9:75:13 | call to c_str | stl.cpp:69:16:69:21 | call to source | diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected index 58a7255accb..fa0807f204e 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/test_diff.expected @@ -1,13 +1,13 @@ -| format.cpp:53:8:53:13 | format.cpp:52:36:52:49 | AST only | -| format.cpp:58:8:58:13 | format.cpp:57:30:57:43 | AST only | -| format.cpp:63:8:63:13 | format.cpp:62:52:62:65 | AST only | -| format.cpp:68:8:68:13 | format.cpp:67:42:67:55 | AST only | -| format.cpp:79:8:79:13 | format.cpp:78:36:78:41 | AST only | -| format.cpp:84:8:84:13 | format.cpp:83:38:83:43 | AST only | -| format.cpp:90:8:90:13 | format.cpp:89:36:89:49 | AST only | -| format.cpp:96:8:96:13 | format.cpp:95:30:95:43 | AST only | -| format.cpp:101:8:101:13 | format.cpp:100:31:100:45 | AST only | -| format.cpp:106:8:106:14 | format.cpp:105:38:105:52 | AST only | +| format.cpp:57:8:57:13 | format.cpp:56:36:56:49 | AST only | +| format.cpp:62:8:62:13 | format.cpp:61:30:61:43 | AST only | +| format.cpp:67:8:67:13 | format.cpp:66:52:66:65 | AST only | +| format.cpp:72:8:72:13 | format.cpp:71:42:71:55 | AST only | +| format.cpp:83:8:83:13 | format.cpp:82:36:82:41 | AST only | +| format.cpp:88:8:88:13 | format.cpp:87:38:87:43 | AST only | +| format.cpp:94:8:94:13 | format.cpp:93:36:93:49 | AST only | +| format.cpp:100:8:100:13 | format.cpp:99:30:99:43 | AST only | +| format.cpp:105:8:105:13 | format.cpp:104:31:104:45 | AST only | +| format.cpp:110:8:110:14 | format.cpp:109:38:109:52 | AST only | | stl.cpp:73:7:73:7 | stl.cpp:69:16:69:21 | AST only | | stl.cpp:75:9:75:13 | stl.cpp:69:16:69:21 | AST only | | stl.cpp:125:13:125:17 | stl.cpp:117:10:117:15 | AST only | diff --git a/cpp/ql/test/library-tests/ir/constant_func/constant_func.ql b/cpp/ql/test/library-tests/ir/constant_func/constant_func.ql index 8701725a18d..8e25ba0e5d4 100644 --- a/cpp/ql/test/library-tests/ir/constant_func/constant_func.ql +++ b/cpp/ql/test/library-tests/ir/constant_func/constant_func.ql @@ -1,4 +1,4 @@ -import default +import cpp import semmle.code.cpp.ir.IR import semmle.code.cpp.ir.implementation.aliased_ssa.constant.ConstantAnalysis import semmle.code.cpp.ir.internal.IntegerConstant diff --git a/cpp/ql/test/library-tests/ir/escape/escape.ql b/cpp/ql/test/library-tests/ir/escape/escape.ql index 9099fea159e..d5c88827af9 100644 --- a/cpp/ql/test/library-tests/ir/escape/escape.ql +++ b/cpp/ql/test/library-tests/ir/escape/escape.ql @@ -1,4 +1,4 @@ -import default +import cpp import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.AliasAnalysis import semmle.code.cpp.ir.implementation.raw.IR import semmle.code.cpp.ir.implementation.UseSoundEscapeAnalysis diff --git a/cpp/ql/test/library-tests/ir/escape/ssa_escape.ql b/cpp/ql/test/library-tests/ir/escape/ssa_escape.ql index e97cea7670d..e1693ba3f38 100644 --- a/cpp/ql/test/library-tests/ir/escape/ssa_escape.ql +++ b/cpp/ql/test/library-tests/ir/escape/ssa_escape.ql @@ -1,4 +1,4 @@ -import default +import cpp import semmle.code.cpp.ir.implementation.aliased_ssa.internal.AliasAnalysis import semmle.code.cpp.ir.implementation.aliased_ssa.internal.AliasConfiguration import semmle.code.cpp.ir.implementation.unaliased_ssa.IR diff --git a/cpp/ql/test/library-tests/literals/uuidof/uuidof.ql b/cpp/ql/test/library-tests/literals/uuidof/uuidof.ql index b369e26e5bc..bff506957b9 100644 --- a/cpp/ql/test/library-tests/literals/uuidof/uuidof.ql +++ b/cpp/ql/test/library-tests/literals/uuidof/uuidof.ql @@ -1,4 +1,4 @@ -import default +import cpp query predicate classUuids(Class cls, string uuid) { if exists(cls.getUuid()) then uuid = cls.getUuid() else uuid = "" diff --git a/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/isfromtemplateinstantiation.ql b/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/isfromtemplateinstantiation.ql index 3a34cc2ca1b..454f901fe38 100644 --- a/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/isfromtemplateinstantiation.ql +++ b/cpp/ql/test/library-tests/templates/isfromtemplateinstantiation/isfromtemplateinstantiation.ql @@ -2,7 +2,7 @@ import cpp class FunctionMonkeyPatch extends Function { language[monotonicAggregates] - override string toString() { + override string getDescription() { exists(string name, string templateArgs, string args | result = name + templateArgs + args and name = this.getQualifiedName() and @@ -30,7 +30,9 @@ class FunctionMonkeyPatch extends Function { } class ParameterMonkeyPatch extends Parameter { - override string toString() { result = super.getType().getName() + " " + super.toString() } + override string getDescription() { + result = super.getType().getName() + " " + super.getDescription() + } } from Element e, Element ti diff --git a/cpp/ql/test/library-tests/usings/Usings1.expected b/cpp/ql/test/library-tests/usings/Usings1.expected index 29fc4ebd652..c29cf7dd11f 100644 --- a/cpp/ql/test/library-tests/usings/Usings1.expected +++ b/cpp/ql/test/library-tests/usings/Usings1.expected @@ -1,7 +1,7 @@ -| templates.cpp:9:5:9:14 | using c | -| usings.cpp:8:1:8:11 | using nf | -| usings.cpp:9:1:9:17 | using namespace N | -| usings.cpp:18:3:18:13 | using bf | -| usings.cpp:21:5:21:14 | using gf | -| usings.cpp:34:3:34:20 | using tbf | -| usings.cpp:42:5:42:22 | using foo | +| templates.cpp:9:5:9:14 | using c | UsingDeclarationEntry, enclosingElement:std | +| usings.cpp:8:1:8:11 | using nf | UsingDeclarationEntry, enclosingElement:(global namespace) | +| usings.cpp:9:1:9:17 | using namespace N | UsingDirectiveEntry, enclosingElement:(global namespace) | +| usings.cpp:18:3:18:13 | using bf | UsingDeclarationEntry, enclosingElement:D | +| usings.cpp:21:5:21:14 | using gf | UsingDeclarationEntry, enclosingElement:{ ... } | +| usings.cpp:34:3:34:20 | using tbf | UsingDeclarationEntry, enclosingElement:TD | +| usings.cpp:42:5:42:22 | using foo | UsingDeclarationEntry, enclosingElement:nsbar | diff --git a/cpp/ql/test/library-tests/usings/Usings1.ql b/cpp/ql/test/library-tests/usings/Usings1.ql index 5e374a82059..2011c196571 100644 --- a/cpp/ql/test/library-tests/usings/Usings1.ql +++ b/cpp/ql/test/library-tests/usings/Usings1.ql @@ -1,4 +1,14 @@ import cpp +string describe(UsingEntry ue) { + ue instanceof UsingDeclarationEntry and + result = "UsingDeclarationEntry" + or + ue instanceof UsingDirectiveEntry and + result = "UsingDirectiveEntry" + or + result = "enclosingElement:" + ue.getEnclosingElement().toString() +} + from UsingEntry ue -select ue +select ue, concat(describe(ue), ", ") diff --git a/cpp/ql/test/library-tests/usings/Usings2.expected b/cpp/ql/test/library-tests/usings/Usings2.expected deleted file mode 100644 index bb23b50e41a..00000000000 --- a/cpp/ql/test/library-tests/usings/Usings2.expected +++ /dev/null @@ -1,7 +0,0 @@ -| templates.cpp:9:5:9:14 | using c | file://:0:0:0:0 | std | -| usings.cpp:8:1:8:11 | using nf | file://:0:0:0:0 | (global namespace) | -| usings.cpp:9:1:9:17 | using namespace N | file://:0:0:0:0 | (global namespace) | -| usings.cpp:18:3:18:13 | using bf | usings.cpp:16:8:16:8 | D | -| usings.cpp:21:5:21:14 | using gf | usings.cpp:20:13:23:3 | { ... } | -| usings.cpp:34:3:34:20 | using tbf | usings.cpp:32:8:32:9 | TD | -| usings.cpp:42:5:42:22 | using foo | usings.cpp:41:11:41:15 | nsbar | diff --git a/cpp/ql/test/library-tests/usings/Usings2.ql b/cpp/ql/test/library-tests/usings/Usings2.ql deleted file mode 100644 index bc4a076b0c5..00000000000 --- a/cpp/ql/test/library-tests/usings/Usings2.ql +++ /dev/null @@ -1,5 +0,0 @@ -import cpp - -from UsingEntry ue, Element e -where e = ue.getEnclosingElement() -select ue, e diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected index 803b330716e..8aa79b5bb6d 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/TaintedAllocationSize.expected @@ -1,13 +1,4 @@ edges -| field_conflation.c:12:22:12:27 | call to getenv | field_conflation.c:13:3:13:18 | Chi | -| field_conflation.c:12:22:12:34 | (const char *)... | field_conflation.c:13:3:13:18 | Chi | -| field_conflation.c:13:3:13:18 | Chi | field_conflation.c:19:15:19:17 | taint_array output argument | -| field_conflation.c:19:15:19:17 | taint_array output argument | field_conflation.c:20:10:20:13 | (unsigned long)... | -| field_conflation.c:19:15:19:17 | taint_array output argument | field_conflation.c:20:13:20:13 | x | -| field_conflation.c:19:15:19:17 | taint_array output argument | field_conflation.c:20:13:20:13 | x | -| field_conflation.c:19:15:19:17 | taint_array output argument | field_conflation.c:20:13:20:13 | x | -| field_conflation.c:20:13:20:13 | x | field_conflation.c:20:10:20:13 | (unsigned long)... | -| field_conflation.c:20:13:20:13 | x | field_conflation.c:20:13:20:13 | x | | test.cpp:39:21:39:24 | argv | test.cpp:42:38:42:44 | (size_t)... | | test.cpp:39:21:39:24 | argv | test.cpp:42:38:42:44 | (size_t)... | | test.cpp:39:21:39:24 | argv | test.cpp:42:38:42:44 | tainted | @@ -68,16 +59,27 @@ edges | test.cpp:227:24:227:37 | (const char *)... | test.cpp:237:10:237:19 | (size_t)... | | test.cpp:235:11:235:20 | (size_t)... | test.cpp:214:23:214:23 | s | | test.cpp:237:10:237:19 | (size_t)... | test.cpp:220:21:220:21 | s | +| test.cpp:241:2:241:32 | Chi | test.cpp:279:17:279:20 | get_size output argument | +| test.cpp:241:2:241:32 | Chi | test.cpp:295:18:295:21 | get_size output argument | +| test.cpp:241:18:241:23 | call to getenv | test.cpp:241:2:241:32 | Chi | +| test.cpp:241:18:241:31 | (const char *)... | test.cpp:241:2:241:32 | Chi | +| test.cpp:249:20:249:25 | call to getenv | test.cpp:253:11:253:29 | ... * ... | +| test.cpp:249:20:249:25 | call to getenv | test.cpp:253:11:253:29 | ... * ... | +| test.cpp:249:20:249:33 | (const char *)... | test.cpp:253:11:253:29 | ... * ... | +| test.cpp:249:20:249:33 | (const char *)... | test.cpp:253:11:253:29 | ... * ... | +| test.cpp:279:17:279:20 | get_size output argument | test.cpp:281:11:281:28 | ... * ... | +| test.cpp:279:17:279:20 | get_size output argument | test.cpp:281:11:281:28 | ... * ... | +| test.cpp:295:18:295:21 | get_size output argument | test.cpp:298:10:298:27 | ... * ... | +| test.cpp:295:18:295:21 | get_size output argument | test.cpp:298:10:298:27 | ... * ... | +| test.cpp:301:19:301:24 | call to getenv | test.cpp:305:11:305:28 | ... * ... | +| test.cpp:301:19:301:24 | call to getenv | test.cpp:305:11:305:28 | ... * ... | +| test.cpp:301:19:301:32 | (const char *)... | test.cpp:305:11:305:28 | ... * ... | +| test.cpp:301:19:301:32 | (const char *)... | test.cpp:305:11:305:28 | ... * ... | +| test.cpp:309:19:309:24 | call to getenv | test.cpp:314:10:314:27 | ... * ... | +| test.cpp:309:19:309:24 | call to getenv | test.cpp:314:10:314:27 | ... * ... | +| test.cpp:309:19:309:32 | (const char *)... | test.cpp:314:10:314:27 | ... * ... | +| test.cpp:309:19:309:32 | (const char *)... | test.cpp:314:10:314:27 | ... * ... | nodes -| field_conflation.c:12:22:12:27 | call to getenv | semmle.label | call to getenv | -| field_conflation.c:12:22:12:34 | (const char *)... | semmle.label | (const char *)... | -| field_conflation.c:13:3:13:18 | Chi | semmle.label | Chi | -| field_conflation.c:19:15:19:17 | taint_array output argument | semmle.label | taint_array output argument | -| field_conflation.c:20:10:20:13 | (unsigned long)... | semmle.label | (unsigned long)... | -| field_conflation.c:20:10:20:13 | (unsigned long)... | semmle.label | (unsigned long)... | -| field_conflation.c:20:13:20:13 | x | semmle.label | x | -| field_conflation.c:20:13:20:13 | x | semmle.label | x | -| field_conflation.c:20:13:20:13 | x | semmle.label | x | | test.cpp:39:21:39:24 | argv | semmle.label | argv | | test.cpp:39:21:39:24 | argv | semmle.label | argv | | test.cpp:42:38:42:44 | (size_t)... | semmle.label | (size_t)... | @@ -140,8 +142,33 @@ nodes | test.cpp:231:9:231:24 | call to get_tainted_size | semmle.label | call to get_tainted_size | | test.cpp:235:11:235:20 | (size_t)... | semmle.label | (size_t)... | | test.cpp:237:10:237:19 | (size_t)... | semmle.label | (size_t)... | +| test.cpp:241:2:241:32 | Chi | semmle.label | Chi | +| test.cpp:241:18:241:23 | call to getenv | semmle.label | call to getenv | +| test.cpp:241:18:241:31 | (const char *)... | semmle.label | (const char *)... | +| test.cpp:249:20:249:25 | call to getenv | semmle.label | call to getenv | +| test.cpp:249:20:249:33 | (const char *)... | semmle.label | (const char *)... | +| test.cpp:253:11:253:29 | ... * ... | semmle.label | ... * ... | +| test.cpp:253:11:253:29 | ... * ... | semmle.label | ... * ... | +| test.cpp:253:11:253:29 | ... * ... | semmle.label | ... * ... | +| test.cpp:279:17:279:20 | get_size output argument | semmle.label | get_size output argument | +| test.cpp:281:11:281:28 | ... * ... | semmle.label | ... * ... | +| test.cpp:281:11:281:28 | ... * ... | semmle.label | ... * ... | +| test.cpp:281:11:281:28 | ... * ... | semmle.label | ... * ... | +| test.cpp:295:18:295:21 | get_size output argument | semmle.label | get_size output argument | +| test.cpp:298:10:298:27 | ... * ... | semmle.label | ... * ... | +| test.cpp:298:10:298:27 | ... * ... | semmle.label | ... * ... | +| test.cpp:298:10:298:27 | ... * ... | semmle.label | ... * ... | +| test.cpp:301:19:301:24 | call to getenv | semmle.label | call to getenv | +| test.cpp:301:19:301:32 | (const char *)... | semmle.label | (const char *)... | +| test.cpp:305:11:305:28 | ... * ... | semmle.label | ... * ... | +| test.cpp:305:11:305:28 | ... * ... | semmle.label | ... * ... | +| test.cpp:305:11:305:28 | ... * ... | semmle.label | ... * ... | +| test.cpp:309:19:309:24 | call to getenv | semmle.label | call to getenv | +| test.cpp:309:19:309:32 | (const char *)... | semmle.label | (const char *)... | +| test.cpp:314:10:314:27 | ... * ... | semmle.label | ... * ... | +| test.cpp:314:10:314:27 | ... * ... | semmle.label | ... * ... | +| test.cpp:314:10:314:27 | ... * ... | semmle.label | ... * ... | #select -| field_conflation.c:20:3:20:8 | call to malloc | field_conflation.c:12:22:12:27 | call to getenv | field_conflation.c:20:13:20:13 | x | This allocation size is derived from $@ and might overflow | field_conflation.c:12:22:12:27 | call to getenv | user input (getenv) | | test.cpp:42:31:42:36 | call to malloc | test.cpp:39:21:39:24 | argv | test.cpp:42:38:42:44 | tainted | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) | | test.cpp:43:31:43:36 | call to malloc | test.cpp:39:21:39:24 | argv | test.cpp:43:38:43:63 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) | | test.cpp:45:31:45:36 | call to malloc | test.cpp:39:21:39:24 | argv | test.cpp:45:38:45:63 | ... + ... | This allocation size is derived from $@ and might overflow | test.cpp:39:21:39:24 | argv | user input (argv) | @@ -155,3 +182,8 @@ nodes | test.cpp:221:14:221:19 | call to malloc | test.cpp:227:24:227:29 | call to getenv | test.cpp:221:21:221:21 | s | This allocation size is derived from $@ and might overflow | test.cpp:227:24:227:29 | call to getenv | user input (getenv) | | test.cpp:229:2:229:7 | call to malloc | test.cpp:227:24:227:29 | call to getenv | test.cpp:229:9:229:18 | local_size | This allocation size is derived from $@ and might overflow | test.cpp:227:24:227:29 | call to getenv | user input (getenv) | | test.cpp:231:2:231:7 | call to malloc | test.cpp:201:14:201:19 | call to getenv | test.cpp:231:9:231:24 | call to get_tainted_size | This allocation size is derived from $@ and might overflow | test.cpp:201:14:201:19 | call to getenv | user input (getenv) | +| test.cpp:253:4:253:9 | call to malloc | test.cpp:249:20:249:25 | call to getenv | test.cpp:253:11:253:29 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:249:20:249:25 | call to getenv | user input (getenv) | +| test.cpp:281:4:281:9 | call to malloc | test.cpp:241:18:241:23 | call to getenv | test.cpp:281:11:281:28 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:241:18:241:23 | call to getenv | user input (getenv) | +| test.cpp:298:3:298:8 | call to malloc | test.cpp:241:18:241:23 | call to getenv | test.cpp:298:10:298:27 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:241:18:241:23 | call to getenv | user input (getenv) | +| test.cpp:305:4:305:9 | call to malloc | test.cpp:301:19:301:24 | call to getenv | test.cpp:305:11:305:28 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:301:19:301:24 | call to getenv | user input (getenv) | +| test.cpp:314:3:314:8 | call to malloc | test.cpp:309:19:309:24 | call to getenv | test.cpp:314:10:314:27 | ... * ... | This allocation size is derived from $@ and might overflow | test.cpp:309:19:309:24 | call to getenv | user input (getenv) | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/field_conflation.c b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/field_conflation.c index 5f6cb730e80..9a69a420a79 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/field_conflation.c +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/field_conflation.c @@ -17,5 +17,5 @@ void test_conflated_fields3(void) { struct XY xy; xy.x = 4; taint_array(&xy); - malloc(xy.x); // not tainted [FALSE POSITIVE] + malloc(xy.x); // not tainted } diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp index cb21e8f1915..0683f7211e3 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/TaintedAllocationSize/test.cpp @@ -236,3 +236,81 @@ void more_cases() { my_func(100); // GOOD my_func(local_size); // GOOD } + +bool get_size(int &out_size) { + out_size = atoi(getenv("USER")); + + return true; +} + +void equality_cases() { + { + int size1 = atoi(getenv("USER")); + int size2 = atoi(getenv("USER")); + + if (size1 == 100) + { + malloc(size2 * sizeof(int)); // BAD + } + if (size2 == 100) + { + malloc(size2 * sizeof(int)); // GOOD + } + } + { + int size = atoi(getenv("USER")); + + if (size != 100) + return; + + malloc(size * sizeof(int)); // GOOD + } + { + int size; + + if ((get_size(size)) && (size == 100)) + { + malloc(size * sizeof(int)); // GOOD + } + } + { + int size; + + if ((get_size(size)) && (size != 100)) + { + malloc(size * sizeof(int)); // BAD + } + } + { + int size; + + if ((!get_size(size)) || (size != 100)) + return; + + malloc(size * sizeof(int)); // GOOD + } + { + int size; + + if ((!get_size(size)) || (size == 100)) + return; + + malloc(size * sizeof(int)); // BAD + } + { + int size = atoi(getenv("USER")); + + if ((size == 50) || (size == 100)) + { + malloc(size * sizeof(int)); // GOOD [FALSE POSITIVE] + } + } + { + int size = atoi(getenv("USER")); + + if (size != 50 && size != 100) + return; + + malloc(size * sizeof(int)); // GOOD [FALSE POSITIVE] + } +} diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/extreme/ArithmeticWithExtremeValues.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/extreme/ArithmeticWithExtremeValues.expected index 20e5eafbd3b..a46371f36b6 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/extreme/ArithmeticWithExtremeValues.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/extreme/ArithmeticWithExtremeValues.expected @@ -5,5 +5,4 @@ | test.c:63:3:63:5 | sc8 | $@ flows to here and is used in arithmetic, potentially causing an underflow. | test.c:62:9:62:16 | - ... | Extreme value | | test.c:75:3:75:5 | sc1 | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:74:9:74:16 | 127 | Extreme value | | test.c:76:3:76:5 | sc1 | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:74:9:74:16 | 127 | Extreme value | -| test.c:114:9:114:9 | x | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:108:17:108:23 | 2147483647 | Extreme value | | test.c:124:9:124:9 | x | $@ flows to here and is used in arithmetic, potentially causing an overflow. | test.c:118:17:118:23 | 2147483647 | Extreme value | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/extreme/test.c b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/extreme/test.c index e17d413e3fd..8c40d984ee0 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/extreme/test.c +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/extreme/test.c @@ -111,7 +111,7 @@ void test_guards3(int cond) { if (x != 0) return; - return x + 1; // GOOD [FALSE POSITIVE] + return x + 1; // GOOD } void test_guards4(int cond) { diff --git a/csharp/ql/src/semmle/code/csharp/Stmt.qll b/csharp/ql/src/semmle/code/csharp/Stmt.qll index 710a4ac8f95..df94a259f57 100644 --- a/csharp/ql/src/semmle/code/csharp/Stmt.qll +++ b/csharp/ql/src/semmle/code/csharp/Stmt.qll @@ -1160,26 +1160,6 @@ class UsingStmt extends Stmt, @using_stmt { * ``` */ Expr getAnExpr() { none() } - - /** - * DEPRECATED: Use UsingBlockStmt.getExpr() instead. - * Gets the expression directly used by this `using` statement, if any. For - * example, `f` on line 2 in - * - * ``` - * var f = File.Open("settings.xml"); - * using (f) { - * ... - * } - * ``` - */ - deprecated Expr getExpr() { none() } - - /** - * DEPRECATED: Use UsingBlockStmt.getBody() instead. - * Gets the body of this `using` statement. - */ - deprecated Stmt getBody() { none() } } /** @@ -1212,7 +1192,7 @@ class UsingBlockStmt extends UsingStmt, @using_block_stmt { * } * ``` */ - override Expr getExpr() { result = this.getChild(0) } + Expr getExpr() { result = this.getChild(0) } override Expr getAnExpr() { result = this.getAVariableDeclExpr().getInitializer() @@ -1221,7 +1201,7 @@ class UsingBlockStmt extends UsingStmt, @using_block_stmt { } /** Gets the body of this `using` statement. */ - override Stmt getBody() { result.getParent() = this } + Stmt getBody() { result.getParent() = this } override string toString() { result = "using (...) {...}" } } diff --git a/csharp/ql/src/semmle/code/csharp/Unification.qll b/csharp/ql/src/semmle/code/csharp/Unification.qll index 77aeae0813a..48256a59a29 100644 --- a/csharp/ql/src/semmle/code/csharp/Unification.qll +++ b/csharp/ql/src/semmle/code/csharp/Unification.qll @@ -474,7 +474,8 @@ module Gvn { sourceDecl = any(GenericType t).getSourceDeclaration() and not sourceDecl instanceof PointerType and not sourceDecl instanceof NullableType and - not sourceDecl instanceof ArrayType + not sourceDecl instanceof ArrayType and + not sourceDecl instanceof TupleType } cached diff --git a/csharp/ql/src/semmle/code/csharp/XML.qll b/csharp/ql/src/semmle/code/csharp/XML.qll index dc7836aaabe..713903b63e6 100755 --- a/csharp/ql/src/semmle/code/csharp/XML.qll +++ b/csharp/ql/src/semmle/code/csharp/XML.qll @@ -116,7 +116,7 @@ class XMLFile extends XMLParent, File { XMLFile() { xmlEncoding(this, _) } /** Gets a printable representation of this XML file. */ - override string toString() { result = XMLParent.super.toString() } + override string toString() { result = getName() } /** Gets the name of this XML file. */ override string getName() { result = File.super.getAbsolutePath() } @@ -236,7 +236,7 @@ class XMLElement extends @xmlelement, XMLParent, XMLLocatable { string getAttributeValue(string name) { result = this.getAttribute(name).getValue() } /** Gets a printable representation of this XML element. */ - override string toString() { result = XMLParent.super.toString() } + override string toString() { result = getName() } } /** diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll index 9587ea5f274..f876c04d6c6 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -66,9 +66,6 @@ abstract class Configuration extends string { */ predicate isBarrier(Node node) { none() } - /** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */ - deprecated predicate isBarrierEdge(Node node1, Node node2) { none() } - /** Holds if data flow into `node` is prohibited. */ predicate isBarrierIn(Node node) { none() } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll index 9587ea5f274..f876c04d6c6 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll @@ -66,9 +66,6 @@ abstract class Configuration extends string { */ predicate isBarrier(Node node) { none() } - /** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */ - deprecated predicate isBarrierEdge(Node node1, Node node2) { none() } - /** Holds if data flow into `node` is prohibited. */ predicate isBarrierIn(Node node) { none() } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll index 9587ea5f274..f876c04d6c6 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll @@ -66,9 +66,6 @@ abstract class Configuration extends string { */ predicate isBarrier(Node node) { none() } - /** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */ - deprecated predicate isBarrierEdge(Node node1, Node node2) { none() } - /** Holds if data flow into `node` is prohibited. */ predicate isBarrierIn(Node node) { none() } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll index 9587ea5f274..f876c04d6c6 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll @@ -66,9 +66,6 @@ abstract class Configuration extends string { */ predicate isBarrier(Node node) { none() } - /** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */ - deprecated predicate isBarrierEdge(Node node1, Node node2) { none() } - /** Holds if data flow into `node` is prohibited. */ predicate isBarrierIn(Node node) { none() } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll index 9587ea5f274..f876c04d6c6 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll @@ -66,9 +66,6 @@ abstract class Configuration extends string { */ predicate isBarrier(Node node) { none() } - /** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */ - deprecated predicate isBarrierEdge(Node node1, Node node2) { none() } - /** Holds if data flow into `node` is prohibited. */ predicate isBarrierIn(Node node) { none() } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll index e8b828f5b3e..0f0607662e9 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll @@ -79,13 +79,6 @@ abstract class Configuration extends DataFlow::Configuration { defaultTaintBarrier(node) } - /** DEPRECATED: override `isSanitizerIn` and `isSanitizerOut` instead. */ - deprecated predicate isSanitizerEdge(DataFlow::Node node1, DataFlow::Node node2) { none() } - - deprecated final override predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) { - isSanitizerEdge(node1, node2) - } - /** Holds if data flow into `node` is prohibited. */ predicate isSanitizerIn(DataFlow::Node node) { none() } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll index e8b828f5b3e..0f0607662e9 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll @@ -79,13 +79,6 @@ abstract class Configuration extends DataFlow::Configuration { defaultTaintBarrier(node) } - /** DEPRECATED: override `isSanitizerIn` and `isSanitizerOut` instead. */ - deprecated predicate isSanitizerEdge(DataFlow::Node node1, DataFlow::Node node2) { none() } - - deprecated final override predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) { - isSanitizerEdge(node1, node2) - } - /** Holds if data flow into `node` is prohibited. */ predicate isSanitizerIn(DataFlow::Node node) { none() } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking3/TaintTrackingImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking3/TaintTrackingImpl.qll index e8b828f5b3e..0f0607662e9 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking3/TaintTrackingImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking3/TaintTrackingImpl.qll @@ -79,13 +79,6 @@ abstract class Configuration extends DataFlow::Configuration { defaultTaintBarrier(node) } - /** DEPRECATED: override `isSanitizerIn` and `isSanitizerOut` instead. */ - deprecated predicate isSanitizerEdge(DataFlow::Node node1, DataFlow::Node node2) { none() } - - deprecated final override predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) { - isSanitizerEdge(node1, node2) - } - /** Holds if data flow into `node` is prohibited. */ predicate isSanitizerIn(DataFlow::Node node) { none() } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking4/TaintTrackingImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking4/TaintTrackingImpl.qll index e8b828f5b3e..0f0607662e9 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking4/TaintTrackingImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking4/TaintTrackingImpl.qll @@ -79,13 +79,6 @@ abstract class Configuration extends DataFlow::Configuration { defaultTaintBarrier(node) } - /** DEPRECATED: override `isSanitizerIn` and `isSanitizerOut` instead. */ - deprecated predicate isSanitizerEdge(DataFlow::Node node1, DataFlow::Node node2) { none() } - - deprecated final override predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) { - isSanitizerEdge(node1, node2) - } - /** Holds if data flow into `node` is prohibited. */ predicate isSanitizerIn(DataFlow::Node node) { none() } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking5/TaintTrackingImpl.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking5/TaintTrackingImpl.qll index e8b828f5b3e..0f0607662e9 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking5/TaintTrackingImpl.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/tainttracking5/TaintTrackingImpl.qll @@ -79,13 +79,6 @@ abstract class Configuration extends DataFlow::Configuration { defaultTaintBarrier(node) } - /** DEPRECATED: override `isSanitizerIn` and `isSanitizerOut` instead. */ - deprecated predicate isSanitizerEdge(DataFlow::Node node1, DataFlow::Node node2) { none() } - - deprecated final override predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) { - isSanitizerEdge(node1, node2) - } - /** Holds if data flow into `node` is prohibited. */ predicate isSanitizerIn(DataFlow::Node node) { none() } diff --git a/java/ql/src/experimental/CWE-939/IncorrectURLVerification.java b/java/ql/src/experimental/CWE-939/IncorrectURLVerification.java new file mode 100644 index 00000000000..1c06f05bc7e --- /dev/null +++ b/java/ql/src/experimental/CWE-939/IncorrectURLVerification.java @@ -0,0 +1,17 @@ +public boolean shouldOverrideUrlLoading(WebView view, String url) { + { + Uri uri = Uri.parse(url); + // BAD: partial domain match, which allows an attacker to register a domain like myexample.com to circumvent the verification + if (uri.getHost() != null && uri.getHost().endsWith("example.com")) { + return false; + } + } + + { + Uri uri = Uri.parse(url); + // GOOD: full domain match + if (uri.getHost() != null && uri.getHost().endsWith(".example.com")) { + return false; + } + } +} diff --git a/java/ql/src/experimental/CWE-939/IncorrectURLVerification.qhelp b/java/ql/src/experimental/CWE-939/IncorrectURLVerification.qhelp new file mode 100644 index 00000000000..850e9f3fa9d --- /dev/null +++ b/java/ql/src/experimental/CWE-939/IncorrectURLVerification.qhelp @@ -0,0 +1,28 @@ + + + + +

Apps that rely on URL Parsing to verify that a given URL is pointing to a trust server may be susceptible to many different ways to get URL parsing and verification wrong, which allows an attacker to register a fake site to break the access control.

+
+ + +

Verify the whole host and domain (FQDN) or check endsWith dot+domain.

+
+ + +

The following example shows two ways of verifying host domain. In the 'BAD' case, +verification is implemented as partial domain match. In the 'GOOD' case, full domain is verified.

+ +
+ + +
  • +Common Android app vulnerabilities from Sebastian Porst of Google +
  • +
  • +Common Android app vulnerabilities from bugcrowd +
  • +
    +
    diff --git a/java/ql/src/experimental/CWE-939/IncorrectURLVerification.ql b/java/ql/src/experimental/CWE-939/IncorrectURLVerification.ql new file mode 100644 index 00000000000..46541d28698 --- /dev/null +++ b/java/ql/src/experimental/CWE-939/IncorrectURLVerification.ql @@ -0,0 +1,97 @@ +/** + * @id java/incorrect-url-verification + * @name Incorrect URL verification + * @description Apps that rely on URL parsing to verify that a given URL is pointing to a trusted server are susceptible to wrong ways of URL parsing and verification. + * @kind problem + * @tags security + * external/cwe-939 + */ + +import java + +/** + * The Java class `android.R.string` specific to Android applications, which contains references to application specific resources defined in /res/values/strings.xml. + * For example, ...example.com... in the application com.example.android.web can be referred as R.string.host with the type com.example.android.web.R$string + */ +class AndroidRString extends RefType { + AndroidRString() { this.hasQualifiedName(_, "R$string") } +} + +/** + * The Java class `android.net.Uri` and `java.net.URL`. + */ +class Uri extends RefType { + Uri() { + hasQualifiedName("android.net", "Uri") or + hasQualifiedName("java.net", "URL") + } +} + +/** + * The method `getHost()` declared in `android.net.Uri` and `java.net.URL`. + */ +class UriGetHostMethod extends Method { + UriGetHostMethod() { + getDeclaringType() instanceof Uri and + hasName("getHost") and + getNumberOfParameters() = 0 + } +} + +/** + * The method access with incorrect string comparision + */ +class HostVerificationMethodAccess extends MethodAccess { + HostVerificationMethodAccess() { + ( + this.getMethod().hasName("endsWith") or + this.getMethod().hasName("contains") or + this.getMethod().hasName("indexOf") + ) and + this.getMethod().getNumberOfParameters() = 1 and + ( + this.getArgument(0).(StringLiteral).getRepresentedString().charAt(0) != "." //string constant comparison e.g. uri.getHost().endsWith("example.com") + or + this + .getArgument(0) + .(AddExpr) + .getLeftOperand() + .(VarAccess) + .getVariable() + .getAnAssignedValue() + .(StringLiteral) + .getRepresentedString() + .charAt(0) != "." //var1+var2, check var1 starts with "." e.g. String domainName = "example"; Uri.parse(url).getHost().endsWith(domainName+".com") + or + this + .getArgument(0) + .(AddExpr) + .getLeftOperand() + .(StringLiteral) + .getRepresentedString() + .charAt(0) != "." //"."+var2, check string constant "." e.g. String domainName = "example.com"; Uri.parse(url).getHost().endsWith("www."+domainName) + or + exists(MethodAccess ma, Method m, Field f | + this.getArgument(0) = ma and + ma.getMethod() = m and + m.hasName("getString") and + m.getDeclaringType().getQualifiedName() = "android.content.res.Resources" and + ma.getArgument(0).(FieldRead).getField() = f and + f.getDeclaringType() instanceof AndroidRString + ) //Check resource properties in /res/values/strings.xml in Android mobile applications using res.getString(R.string.key) + or + this + .getArgument(0) + .(VarAccess) + .getVariable() + .getAnAssignedValue() + .(StringLiteral) + .getRepresentedString() + .charAt(0) != "." //check variable starts with "." e.g. String domainName = "example.com"; Uri.parse(url).getHost().endsWith(domainName) + ) + } +} + +from UriGetHostMethod um, MethodAccess uma, HostVerificationMethodAccess hma +where hma.getQualifier() = uma and uma.getMethod() = um +select hma, "Method has potentially $@ ", hma.getArgument(0), "improper URL verification" diff --git a/java/ql/src/experimental/Security/CWE/CWE-016/SpringBootActuators.qll b/java/ql/src/experimental/Security/CWE/CWE-016/SpringBootActuators.qll index 658983f2437..c1ef873b1fa 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-016/SpringBootActuators.qll +++ b/java/ql/src/experimental/Security/CWE/CWE-016/SpringBootActuators.qll @@ -22,8 +22,7 @@ class TypeAuthorizedUrl extends Class { } /** - * The class - * `org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry`. + * The class `org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry`. */ class TypeAbstractRequestMatcherRegistry extends Class { TypeAbstractRequestMatcherRegistry() { @@ -34,38 +33,44 @@ class TypeAbstractRequestMatcherRegistry extends Class { } /** - * The class - * `org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest.EndpointRequestMatcher`. + * The class `org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest`. */ -class TypeEndpointRequestMatcher extends Class { - TypeEndpointRequestMatcher() { +class TypeEndpointRequest extends Class { + TypeEndpointRequest() { this .hasQualifiedName("org.springframework.boot.actuate.autoconfigure.security.servlet", - "EndpointRequest$EndpointRequestMatcher") + "EndpointRequest") + } +} + +/** A call to `EndpointRequest.toAnyEndpoint` method. */ +class ToAnyEndpointCall extends MethodAccess { + ToAnyEndpointCall() { + getMethod().hasName("toAnyEndpoint") and + getMethod().getDeclaringType() instanceof TypeEndpointRequest } } /** - * A call to `HttpSecurity.requestMatcher` method with argument of type - * `EndpointRequestMatcher`. + * A call to `HttpSecurity.requestMatcher` method with argument `RequestMatcher.toAnyEndpoint()`. */ class RequestMatcherCall extends MethodAccess { RequestMatcherCall() { getMethod().hasName("requestMatcher") and getMethod().getDeclaringType() instanceof TypeHttpSecurity and - getArgument(0).getType() instanceof TypeEndpointRequestMatcher + getArgument(0) instanceof ToAnyEndpointCall } } /** - * A call to `HttpSecurity.requestMatchers` method with lambda argument resolving to - * `EndpointRequestMatcher` type. + * A call to `HttpSecurity.requestMatchers` method with lambda argument + * `RequestMatcher.toAnyEndpoint()`. */ class RequestMatchersCall extends MethodAccess { RequestMatchersCall() { getMethod().hasName("requestMatchers") and getMethod().getDeclaringType() instanceof TypeHttpSecurity and - getArgument(0).(LambdaExpr).getExprBody().getType() instanceof TypeEndpointRequestMatcher + getArgument(0).(LambdaExpr).getExprBody() instanceof ToAnyEndpointCall } } @@ -92,9 +97,6 @@ class PermitAllCall extends MethodAccess { or // .requestMatchers(matcher -> EndpointRequest).authorizeRequests([...]).[...] authorizeRequestsCall.getQualifier() instanceof RequestMatchersCall - or - // http.authorizeRequests([...]).[...] - authorizeRequestsCall.getQualifier() instanceof VarAccess | // [...].authorizeRequests(r -> r.anyRequest().permitAll()) or // [...].authorizeRequests(r -> r.requestMatchers(EndpointRequest).permitAll()) @@ -117,6 +119,22 @@ class PermitAllCall extends MethodAccess { this.getQualifier() = anyRequestCall ) ) + or + exists(AuthorizeRequestsCall authorizeRequestsCall | + // http.authorizeRequests([...]).[...] + authorizeRequestsCall.getQualifier() instanceof VarAccess + | + // [...].authorizeRequests(r -> r.requestMatchers(EndpointRequest).permitAll()) + authorizeRequestsCall.getArgument(0).(LambdaExpr).getExprBody() = this and + this.getQualifier() instanceof RegistryRequestMatchersCall + or + // [...].authorizeRequests().requestMatchers(EndpointRequest).permitAll() or + authorizeRequestsCall.getNumArgument() = 0 and + exists(RegistryRequestMatchersCall registryRequestMatchersCall | + registryRequestMatchersCall.getQualifier() = authorizeRequestsCall and + this.getQualifier() = registryRequestMatchersCall + ) + ) } } @@ -129,13 +147,13 @@ class AnyRequestCall extends MethodAccess { } /** - * A call to `AbstractRequestMatcherRegistry.requestMatchers` method with an argument of type - * `EndpointRequestMatcher`. + * A call to `AbstractRequestMatcherRegistry.requestMatchers` method with an argument + * `RequestMatcher.toAnyEndpoint()`. */ class RegistryRequestMatchersCall extends MethodAccess { RegistryRequestMatchersCall() { getMethod().hasName("requestMatchers") and getMethod().getDeclaringType() instanceof TypeAbstractRequestMatcherRegistry and - getAnArgument().getType() instanceof TypeEndpointRequestMatcher + getAnArgument() instanceof ToAnyEndpointCall } } diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll index 9587ea5f274..f876c04d6c6 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl.qll @@ -66,9 +66,6 @@ abstract class Configuration extends string { */ predicate isBarrier(Node node) { none() } - /** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */ - deprecated predicate isBarrierEdge(Node node1, Node node2) { none() } - /** Holds if data flow into `node` is prohibited. */ predicate isBarrierIn(Node node) { none() } diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll index 9587ea5f274..f876c04d6c6 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl2.qll @@ -66,9 +66,6 @@ abstract class Configuration extends string { */ predicate isBarrier(Node node) { none() } - /** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */ - deprecated predicate isBarrierEdge(Node node1, Node node2) { none() } - /** Holds if data flow into `node` is prohibited. */ predicate isBarrierIn(Node node) { none() } diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll index 9587ea5f274..f876c04d6c6 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl3.qll @@ -66,9 +66,6 @@ abstract class Configuration extends string { */ predicate isBarrier(Node node) { none() } - /** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */ - deprecated predicate isBarrierEdge(Node node1, Node node2) { none() } - /** Holds if data flow into `node` is prohibited. */ predicate isBarrierIn(Node node) { none() } diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll index 9587ea5f274..f876c04d6c6 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl4.qll @@ -66,9 +66,6 @@ abstract class Configuration extends string { */ predicate isBarrier(Node node) { none() } - /** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */ - deprecated predicate isBarrierEdge(Node node1, Node node2) { none() } - /** Holds if data flow into `node` is prohibited. */ predicate isBarrierIn(Node node) { none() } diff --git a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll index 9587ea5f274..f876c04d6c6 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/DataFlowImpl5.qll @@ -66,9 +66,6 @@ abstract class Configuration extends string { */ predicate isBarrier(Node node) { none() } - /** DEPRECATED: override `isBarrierIn` and `isBarrierOut` instead. */ - deprecated predicate isBarrierEdge(Node node1, Node node2) { none() } - /** Holds if data flow into `node` is prohibited. */ predicate isBarrierIn(Node node) { none() } diff --git a/java/ql/src/semmle/code/java/dataflow/internal/tainttracking1/TaintTrackingImpl.qll b/java/ql/src/semmle/code/java/dataflow/internal/tainttracking1/TaintTrackingImpl.qll index e8b828f5b3e..0f0607662e9 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/tainttracking1/TaintTrackingImpl.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/tainttracking1/TaintTrackingImpl.qll @@ -79,13 +79,6 @@ abstract class Configuration extends DataFlow::Configuration { defaultTaintBarrier(node) } - /** DEPRECATED: override `isSanitizerIn` and `isSanitizerOut` instead. */ - deprecated predicate isSanitizerEdge(DataFlow::Node node1, DataFlow::Node node2) { none() } - - deprecated final override predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) { - isSanitizerEdge(node1, node2) - } - /** Holds if data flow into `node` is prohibited. */ predicate isSanitizerIn(DataFlow::Node node) { none() } diff --git a/java/ql/src/semmle/code/java/dataflow/internal/tainttracking2/TaintTrackingImpl.qll b/java/ql/src/semmle/code/java/dataflow/internal/tainttracking2/TaintTrackingImpl.qll index e8b828f5b3e..0f0607662e9 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/tainttracking2/TaintTrackingImpl.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/tainttracking2/TaintTrackingImpl.qll @@ -79,13 +79,6 @@ abstract class Configuration extends DataFlow::Configuration { defaultTaintBarrier(node) } - /** DEPRECATED: override `isSanitizerIn` and `isSanitizerOut` instead. */ - deprecated predicate isSanitizerEdge(DataFlow::Node node1, DataFlow::Node node2) { none() } - - deprecated final override predicate isBarrierEdge(DataFlow::Node node1, DataFlow::Node node2) { - isSanitizerEdge(node1, node2) - } - /** Holds if data flow into `node` is prohibited. */ predicate isSanitizerIn(DataFlow::Node node) { none() } diff --git a/java/ql/src/semmle/code/xml/XML.qll b/java/ql/src/semmle/code/xml/XML.qll index dc7836aaabe..713903b63e6 100755 --- a/java/ql/src/semmle/code/xml/XML.qll +++ b/java/ql/src/semmle/code/xml/XML.qll @@ -116,7 +116,7 @@ class XMLFile extends XMLParent, File { XMLFile() { xmlEncoding(this, _) } /** Gets a printable representation of this XML file. */ - override string toString() { result = XMLParent.super.toString() } + override string toString() { result = getName() } /** Gets the name of this XML file. */ override string getName() { result = File.super.getAbsolutePath() } @@ -236,7 +236,7 @@ class XMLElement extends @xmlelement, XMLParent, XMLLocatable { string getAttributeValue(string name) { result = this.getAttribute(name).getValue() } /** Gets a printable representation of this XML element. */ - override string toString() { result = XMLParent.super.toString() } + override string toString() { result = getName() } } /** diff --git a/java/ql/test/experimental/query-tests/security/CWE-016/SpringBootActuators.java b/java/ql/test/experimental/query-tests/security/CWE-016/SpringBootActuators.java index b554a7bac7e..da59919fbe6 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-016/SpringBootActuators.java +++ b/java/ql/test/experimental/query-tests/security/CWE-016/SpringBootActuators.java @@ -37,4 +37,68 @@ public class SpringBootActuators { protected void configureOk2(HttpSecurity http) throws Exception { http.requestMatchers().requestMatchers(EndpointRequest.toAnyEndpoint()); } + + protected void configureOk3(HttpSecurity http) throws Exception { + http.authorizeRequests().anyRequest().permitAll(); + } + + protected void configureOk4(HttpSecurity http) throws Exception { + http.authorizeRequests(authz -> authz.anyRequest().permitAll()); + } + + protected void configureOkSafeEndpoints1(HttpSecurity http) throws Exception { + http.requestMatcher(EndpointRequest.to("health", "info")).authorizeRequests(requests -> requests.anyRequest().permitAll()); + } + + protected void configureOkSafeEndpoints2(HttpSecurity http) throws Exception { + http.requestMatcher(EndpointRequest.to("health")).authorizeRequests().requestMatchers(EndpointRequest.to("health")).permitAll(); + } + + protected void configureOkSafeEndpoints3(HttpSecurity http) throws Exception { + http.requestMatchers(matcher -> EndpointRequest.to("health", "info")).authorizeRequests().requestMatchers(EndpointRequest.to("health", "info")).permitAll(); + } + + protected void configureOkSafeEndpoints4(HttpSecurity http) throws Exception { + http.requestMatcher(EndpointRequest.to("health", "info")).authorizeRequests().anyRequest().permitAll(); + } + + protected void configureOkSafeEndpoints5(HttpSecurity http) throws Exception { + http.authorizeRequests().requestMatchers(EndpointRequest.to("health", "info")).permitAll(); + } + + protected void configureOkSafeEndpoints6(HttpSecurity http) throws Exception { + http.authorizeRequests(requests -> requests.requestMatchers(EndpointRequest.to("health", "info")).permitAll()); + } + + protected void configureOkSafeEndpoints7(HttpSecurity http) throws Exception { + http.requestMatchers(matcher -> EndpointRequest.to("health", "info")).authorizeRequests().anyRequest().permitAll(); + } + + protected void configureOkNoPermitAll1(HttpSecurity http) throws Exception { + http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests(requests -> requests.anyRequest()); + } + + protected void configureOkNoPermitAll2(HttpSecurity http) throws Exception { + http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests().requestMatchers(EndpointRequest.toAnyEndpoint()); + } + + protected void configureOkNoPermitAll3(HttpSecurity http) throws Exception { + http.requestMatchers(matcher -> EndpointRequest.toAnyEndpoint()).authorizeRequests().requestMatchers(EndpointRequest.toAnyEndpoint()); + } + + protected void configureOkNoPermitAll4(HttpSecurity http) throws Exception { + http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests().anyRequest(); + } + + protected void configureOkNoPermitAll5(HttpSecurity http) throws Exception { + http.authorizeRequests().requestMatchers(EndpointRequest.toAnyEndpoint()); + } + + protected void configureOkNoPermitAll6(HttpSecurity http) throws Exception { + http.authorizeRequests(requests -> requests.requestMatchers(EndpointRequest.toAnyEndpoint())); + } + + protected void configureOkNoPermitAll7(HttpSecurity http) throws Exception { + http.requestMatchers(matcher -> EndpointRequest.toAnyEndpoint()).authorizeRequests().anyRequest(); + } } diff --git a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java b/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java index 5b94a086e8f..e7dd0fd7673 100644 --- a/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java +++ b/java/ql/test/experimental/stubs/springframework-5.2.3/org/springframework/boot/actuate/autoconfigure/security/servlet/EndpointRequest.java @@ -7,6 +7,10 @@ public final class EndpointRequest { public static EndpointRequestMatcher toAnyEndpoint() { return null; } + + public static EndpointRequestMatcher to(String... endpoints) { + return null; + } public static final class EndpointRequestMatcher extends AbstractRequestMatcher {} diff --git a/java/ql/test/query-tests/Nullness/C.java b/java/ql/test/query-tests/Nullness/C.java index 48d7799c9a1..c9fe368a394 100644 --- a/java/ql/test/query-tests/Nullness/C.java +++ b/java/ql/test/query-tests/Nullness/C.java @@ -220,4 +220,28 @@ public class C { return; } } + + private Object foo16; + + private Object getFoo16() { + return this.foo16; + } + + public static void ex16(C c) { + int[] xs = c.getFoo16() != null ? new int[5] : null; + if (c.getFoo16() != null) { + xs[0]++; // NPE - false positive + } + } + + public static final int MAXLEN = 1024; + + public void ex17() { + int[] xs = null; + // loop executes at least once + for (int i = 32; i <= MAXLEN; i *= 2) { + xs = new int[5]; + } + xs[0]++; // OK + } } diff --git a/java/ql/test/query-tests/Nullness/NullMaybe.expected b/java/ql/test/query-tests/Nullness/NullMaybe.expected index 2ddb51dfe4c..8f72be0619f 100644 --- a/java/ql/test/query-tests/Nullness/NullMaybe.expected +++ b/java/ql/test/query-tests/Nullness/NullMaybe.expected @@ -31,5 +31,6 @@ | C.java:188:9:188:11 | obj | Variable $@ may be null here because of $@ assignment. | C.java:181:5:181:22 | Object obj | obj | C.java:181:12:181:21 | obj | this | | C.java:207:9:207:11 | obj | Variable $@ may be null here because of $@ assignment. | C.java:201:5:201:22 | Object obj | obj | C.java:201:12:201:21 | obj | this | | C.java:219:9:219:10 | o1 | Variable $@ may be null here as suggested by $@ null guard. | C.java:212:20:212:28 | o1 | o1 | C.java:213:9:213:18 | ... == ... | this | +| C.java:233:7:233:8 | xs | Variable $@ may be null here because of $@ assignment. | C.java:231:5:231:56 | int[] xs | xs | C.java:231:11:231:55 | xs | this | | F.java:11:5:11:7 | obj | Variable $@ may be null here as suggested by $@ null guard. | F.java:8:18:8:27 | obj | obj | F.java:9:9:9:19 | ... == ... | this | | F.java:17:5:17:7 | obj | Variable $@ may be null here as suggested by $@ null guard. | F.java:14:18:14:27 | obj | obj | F.java:15:9:15:19 | ... == ... | this | diff --git a/java/ql/test/query-tests/RangeAnalysis/A.java b/java/ql/test/query-tests/RangeAnalysis/A.java index d219b85bec3..f2cb4918387 100644 --- a/java/ql/test/query-tests/RangeAnalysis/A.java +++ b/java/ql/test/query-tests/RangeAnalysis/A.java @@ -175,4 +175,23 @@ public class A { } } } + + void m14(int[] xs) { + for (int i = 0; i < xs.length + 1; i++) { + if (i == 0 && xs.length > 0) { + xs[i]++; // OK - FP + } + } + } + + void m15(int[] xs) { + for (int i = 0; i < xs.length; i++) { + int x = ++i; + int y = ++i; + if (y < xs.length) { + xs[x]++; // OK - FP + xs[y]++; // OK + } + } + } } diff --git a/java/ql/test/query-tests/RangeAnalysis/ArrayIndexOutOfBounds.expected b/java/ql/test/query-tests/RangeAnalysis/ArrayIndexOutOfBounds.expected index 0c2aafdc4b1..378e9ad3336 100644 --- a/java/ql/test/query-tests/RangeAnalysis/ArrayIndexOutOfBounds.expected +++ b/java/ql/test/query-tests/RangeAnalysis/ArrayIndexOutOfBounds.expected @@ -10,3 +10,5 @@ | A.java:111:14:111:21 | ...[...] | This array access might be out of bounds, as the index might be equal to the array length + 1. | | A.java:122:16:122:23 | ...[...] | This array access might be out of bounds, as the index might be equal to the array length + 3. | | A.java:134:16:134:23 | ...[...] | This array access might be out of bounds, as the index might be equal to the array length. | +| A.java:182:9:182:13 | ...[...] | This array access might be out of bounds, as the index might be equal to the array length. | +| A.java:192:9:192:13 | ...[...] | This array access might be out of bounds, as the index might be equal to the array length. | diff --git a/javascript/extractor/src/com/semmle/js/parser/TypeScriptParser.java b/javascript/extractor/src/com/semmle/js/parser/TypeScriptParser.java index d7ead7adecc..0d505ecb578 100644 --- a/javascript/extractor/src/com/semmle/js/parser/TypeScriptParser.java +++ b/javascript/extractor/src/com/semmle/js/parser/TypeScriptParser.java @@ -86,6 +86,12 @@ public class TypeScriptParser { */ public static final String TYPESCRIPT_TIMEOUT_VAR = "SEMMLE_TYPESCRIPT_TIMEOUT"; + /** + * An environment variable that can be set to specify a number of retries when verifying + * the TypeScript installation. Default is 3. + */ + public static final String TYPESCRIPT_RETRIES_VAR = "SEMMLE_TYPESCRIPT_RETRIES"; + /** * An environment variable (without the SEMMLE_ or LGTM_ prefix), that can be * set to indicate the maximum heap space usable by the Node.js process, in addition to its @@ -179,9 +185,6 @@ public class TypeScriptParser { public String verifyNodeInstallation() { if (nodeJsVersionString != null) return nodeJsVersionString; - ByteArrayOutputStream out = new ByteArrayOutputStream(); - ByteArrayOutputStream err = new ByteArrayOutputStream(); - // Determine where to find the Node.js runtime. String explicitNodeJsRuntime = Env.systemEnv().get(TYPESCRIPT_NODE_RUNTIME_VAR); if (explicitNodeJsRuntime != null) { @@ -198,12 +201,41 @@ public class TypeScriptParser { nodeJsRuntimeExtraArgs = Arrays.asList(extraArgs.split("\\s+")); } + // Run 'node --version' with a timeout, and retry a few times if it times out. + // If the Java process is suspended we may get a spurious timeout, and we want to + // support long suspensions in cloud environments. Instead of setting a huge timeout, + // retrying guarantees we can survive arbitrary suspensions as long as they don't happen + // too many times in rapid succession. + int timeout = Env.systemEnv().getInt(TYPESCRIPT_TIMEOUT_VAR, 10000); + int numRetries = Env.systemEnv().getInt(TYPESCRIPT_RETRIES_VAR, 3); + for (int i = 0; i < numRetries - 1; ++i) { + try { + return startNodeAndGetVersion(timeout); + } catch (InterruptedError e) { + Exceptions.ignore(e, "We will retry the call that caused this exception."); + System.err.println("Starting Node.js seems to take a long time. Retrying."); + } + } + try { + return startNodeAndGetVersion(timeout); + } catch (InterruptedError e) { + Exceptions.ignore(e, "Exception details are not important."); + throw new CatastrophicError( + "Could not start Node.js (timed out after " + (timeout / 1000) + "s and " + numRetries + " attempts"); + } + } + + /** + * Checks that Node.js is installed and can be run and returns its version string. + */ + private String startNodeAndGetVersion(int timeout) throws InterruptedError { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ByteArrayOutputStream err = new ByteArrayOutputStream(); Builder b = new Builder( getNodeJsRuntimeInvocation("--version"), out, err, getParserWrapper().getParentFile()); b.expectFailure(); // We want to do our own logging in case of an error. - int timeout = Env.systemEnv().getInt(TYPESCRIPT_TIMEOUT_VAR, 10000); try { int r = b.execute(timeout); String stdout = new String(out.toByteArray()); @@ -213,10 +245,6 @@ public class TypeScriptParser { "Could not start Node.js. It is required for TypeScript extraction.\n" + stderr); } return nodeJsVersionString = stdout; - } catch (InterruptedError e) { - Exceptions.ignore(e, "Exception details are not important."); - throw new CatastrophicError( - "Could not start Node.js (timed out after " + (timeout / 1000) + "s)."); } catch (ResourceError e) { // In case 'node' is not found, the process builder converts the IOException // into a ResourceError. diff --git a/javascript/ql/src/external/CodeDuplication.qll b/javascript/ql/src/external/CodeDuplication.qll index d0f9d97776a..bd9a0481a8a 100644 --- a/javascript/ql/src/external/CodeDuplication.qll +++ b/javascript/ql/src/external/CodeDuplication.qll @@ -261,6 +261,11 @@ predicate similarContainers(StmtContainer sc, StmtContainer other, float percent ) } +/** + * INTERNAL: do not use. + * + * Holds if `line` in `f` is similar to a line somewhere else. + */ predicate similarLines(File f, int line) { exists(SimilarBlock b | b.sourceFile() = f and line in [b.sourceStartLine() .. b.sourceEndLine()]) } @@ -275,6 +280,7 @@ private predicate similarLinesPerEquivalenceClass(int equivClass, int lines, Fil ) } +/** Holds if `coveredLines` lines of `f` are similar to lines in `otherFile`. */ pragma[noopt] private predicate similarLinesCovered(File f, int coveredLines, File otherFile) { exists(int numLines | numLines = f.getNumberOfLines() | @@ -296,6 +302,11 @@ private predicate similarLinesCovered(File f, int coveredLines, File otherFile) ) } +/** + * INTERNAL: do not use. + * + * Holds if `line` in `f` is duplicated by a line somewhere else. + */ predicate duplicateLines(File f, int line) { exists(DuplicateBlock b | b.sourceFile() = f and line in [b.sourceStartLine() .. b.sourceEndLine()] @@ -312,6 +323,7 @@ private predicate duplicateLinesPerEquivalenceClass(int equivClass, int lines, F ) } +/** Holds if `coveredLines` lines of `f` are duplicates of lines in `otherFile`. */ pragma[noopt] private predicate duplicateLinesCovered(File f, int coveredLines, File otherFile) { exists(int numLines | numLines = f.getNumberOfLines() | @@ -333,6 +345,7 @@ private predicate duplicateLinesCovered(File f, int coveredLines, File otherFile ) } +/** Holds if most of `f` (`percent`%) is similar to `other`. */ predicate similarFiles(File f, File other, int percent) { exists(int covered, int total | similarLinesCovered(f, covered, other) and @@ -343,6 +356,7 @@ predicate similarFiles(File f, File other, int percent) { not duplicateFiles(f, other, _) } +/** Holds if most of `f` (`percent`%) is duplicated by `other`. */ predicate duplicateFiles(File f, File other, int percent) { exists(int covered, int total | duplicateLinesCovered(f, covered, other) and diff --git a/javascript/ql/src/semmle/javascript/HtmlSanitizers.qll b/javascript/ql/src/semmle/javascript/HtmlSanitizers.qll index 47f88f0390c..870cde4bbce 100644 --- a/javascript/ql/src/semmle/javascript/HtmlSanitizers.qll +++ b/javascript/ql/src/semmle/javascript/HtmlSanitizers.qll @@ -48,6 +48,11 @@ private class DefaultHtmlSanitizerCall extends HtmlSanitizerCall { or callee = LodashUnderscore::member("escape") or + exists(DataFlow::PropRead read | read = callee | + read.getPropertyName() = "sanitize" and + read.getBase().asExpr().(VarAccess).getName() = "DOMPurify" + ) + or exists(string name | name = "encode" or name = "encodeNonUTF" | callee = DataFlow::moduleMember("html-entities", _).getAnInstantiation().getAPropertyRead(name) or diff --git a/javascript/ql/src/semmle/javascript/JSX.qll b/javascript/ql/src/semmle/javascript/JSX.qll index 7684ef2b314..a8c1cfa86a2 100644 --- a/javascript/ql/src/semmle/javascript/JSX.qll +++ b/javascript/ql/src/semmle/javascript/JSX.qll @@ -196,7 +196,7 @@ class JSXName extends Expr { ) or exists(JSXQualifiedName qual | qual = this | - result = qual.getNamespace() + ":" + qual.getName() + result = qual.getNamespace().getName() + ":" + qual.getName().getName() ) } } diff --git a/javascript/ql/src/semmle/javascript/NodeJS.qll b/javascript/ql/src/semmle/javascript/NodeJS.qll index 8376debf8b0..6da4a625537 100644 --- a/javascript/ql/src/semmle/javascript/NodeJS.qll +++ b/javascript/ql/src/semmle/javascript/NodeJS.qll @@ -152,6 +152,18 @@ private class RequireVariable extends Variable { */ private predicate moduleInFile(Module m, File f) { m.getFile() = f } +/** + * Holds if `nd` may refer to `require`, either directly or modulo local data flow. + */ +cached +private predicate isRequire(DataFlow::Node nd) { + nd.asExpr() = any(RequireVariable req).getAnAccess() and + // `mjs` files explicitly disallow `require` + not nd.getFile().getExtension() = "mjs" + or + isRequire(nd.getAPredecessor()) +} + /** * A `require` import. * @@ -162,12 +174,7 @@ private predicate moduleInFile(Module m, File f) { m.getFile() = f } * ``` */ class Require extends CallExpr, Import { - cached - Require() { - any(RequireVariable req).getAnAccess() = getCallee() and - // `mjs` files explicitly disallow `require` - not getFile().getExtension() = "mjs" - } + Require() { isRequire(getCallee().flow()) } override PathExpr getImportedPath() { result = getArgument(0) } @@ -257,8 +264,8 @@ private class RequirePath extends PathExprCandidate { RequirePath() { this = any(Require req).getArgument(0) or - exists(RequireVariable req, MethodCallExpr reqres | - reqres.getReceiver() = req.getAnAccess() and + exists(MethodCallExpr reqres | + isRequire(reqres.getReceiver().flow()) and reqres.getMethodName() = "resolve" and this = reqres.getArgument(0) ) diff --git a/javascript/ql/src/semmle/javascript/XML.qll b/javascript/ql/src/semmle/javascript/XML.qll index dc7836aaabe..713903b63e6 100755 --- a/javascript/ql/src/semmle/javascript/XML.qll +++ b/javascript/ql/src/semmle/javascript/XML.qll @@ -116,7 +116,7 @@ class XMLFile extends XMLParent, File { XMLFile() { xmlEncoding(this, _) } /** Gets a printable representation of this XML file. */ - override string toString() { result = XMLParent.super.toString() } + override string toString() { result = getName() } /** Gets the name of this XML file. */ override string getName() { result = File.super.getAbsolutePath() } @@ -236,7 +236,7 @@ class XMLElement extends @xmlelement, XMLParent, XMLLocatable { string getAttributeValue(string name) { result = this.getAttribute(name).getValue() } /** Gets a printable representation of this XML element. */ - override string toString() { result = XMLParent.super.toString() } + override string toString() { result = getName() } } /** diff --git a/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll b/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll index 5de9ef312d6..7a74c0fa6d8 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll @@ -827,6 +827,28 @@ module TaintTracking { override predicate appliesTo(Configuration cfg) { any() } } + /** + * A test of form `x.length === "0"`, preventing `x` from being tainted. + */ + class IsEmptyGuard extends AdditionalSanitizerGuardNode, DataFlow::ValueNode { + override EqualityTest astNode; + boolean polarity; + Expr operand; + + IsEmptyGuard() { + astNode.getPolarity() = polarity and + astNode.getAnOperand().(ConstantExpr).getIntValue() = 0 and + exists(DataFlow::PropRead read | read.asExpr() = astNode.getAnOperand() | + read.getBase().asExpr() = operand and + read.getPropertyName() = "length" + ) + } + + override predicate sanitizes(boolean outcome, Expr e) { polarity = outcome and e = operand } + + override predicate appliesTo(Configuration cfg) { any() } + } + /** DEPRECATED. This class has been renamed to `InclusionSanitizer`. */ deprecated class StringInclusionSanitizer = InclusionSanitizer; diff --git a/javascript/ql/src/semmle/javascript/dataflow/internal/FlowSteps.qll b/javascript/ql/src/semmle/javascript/dataflow/internal/FlowSteps.qll index 948aabb6dc9..e2353b003c7 100644 --- a/javascript/ql/src/semmle/javascript/dataflow/internal/FlowSteps.qll +++ b/javascript/ql/src/semmle/javascript/dataflow/internal/FlowSteps.qll @@ -151,11 +151,14 @@ private module CachedSteps { ) { calls(invk, f) and ( - exists(int i, Parameter p | - f.getParameter(i) = p and - not p.isRestParameter() and - arg = invk.getArgument(i) and - parm = DataFlow::parameterNode(p) + exists(int i | arg = invk.getArgument(i) | + exists(Parameter p | + f.getParameter(i) = p and + not p.isRestParameter() and + parm = DataFlow::parameterNode(p) + ) + or + parm = reflectiveParameterAccess(f, i) ) or arg = invk.(DataFlow::CallNode).getReceiver() and @@ -185,6 +188,22 @@ private module CachedSteps { ) } + /** + * Gets a data-flow node inside `f` that refers to the `arguments` object of `f`. + */ + private DataFlow::Node argumentsAccess(Function f) { + result.getContainer().getEnclosingContainer*() = f and + result.analyze().getAValue().(AbstractArguments).getFunction() = f + } + + /** + * Gets a data-flow node that refers to the `i`th parameter of `f` through its `arguments` + * object. + */ + private DataFlow::SourceNode reflectiveParameterAccess(Function f, int i) { + result.(DataFlow::PropRead).accesses(argumentsAccess(f), any(string p | i = p.toInt())) + } + /** * Holds if there is a flow step from `pred` to `succ` through parameter passing * to a function call. diff --git a/javascript/ql/src/semmle/javascript/frameworks/Fastify.qll b/javascript/ql/src/semmle/javascript/frameworks/Fastify.qll new file mode 100644 index 00000000000..621017d5b4f --- /dev/null +++ b/javascript/ql/src/semmle/javascript/frameworks/Fastify.qll @@ -0,0 +1,291 @@ +/** + * Provides classes for working with [Fastify](https://www.fastify.io/) applications. + */ + +import javascript +import semmle.javascript.frameworks.HTTP + +/** + * Provides classes for working with [Fastify](https://www.fastify.io/) applications. + */ +module Fastify { + /** + * An expression that creates a new Fastify server. + */ + abstract class ServerDefinition extends HTTP::Servers::StandardServerDefinition { } + + /** + * A standard way to create a Fastify server. + */ + class StandardServerDefinition extends ServerDefinition { + StandardServerDefinition() { + this = DataFlow::moduleImport("fastify").getAnInvocation().asExpr() + } + } + + /** + * A function used as a Fastify route handler. + * + * By default, only handlers installed by a Fastify route setup are recognized, + * but support for other kinds of route handlers can be added by implementing + * additional subclasses of this class. + */ + abstract class RouteHandler extends HTTP::Servers::StandardRouteHandler, DataFlow::ValueNode { + /** + * Gets the parameter of the route handler that contains the request object. + */ + abstract DataFlow::ParameterNode getRequestParameter(); + + /** + * Gets the parameter of the route handler that contains the reply object. + */ + abstract DataFlow::ParameterNode getReplyParameter(); + } + + /** + * A Fastify route handler installed by a route setup. + */ + class StandardRouteHandler extends RouteHandler, DataFlow::FunctionNode { + StandardRouteHandler() { this = any(RouteSetup setup).getARouteHandler() } + + override DataFlow::ParameterNode getRequestParameter() { result = this.getParameter(0) } + + override DataFlow::ParameterNode getReplyParameter() { result = this.getParameter(1) } + } + + /** + * A Fastify reply source, that is, the `reply` parameter of a + * route handler. + */ + private class ReplySource extends HTTP::Servers::ResponseSource { + RouteHandler rh; + + ReplySource() { this = rh.getReplyParameter() } + + /** + * Gets the route handler that provides this response. + */ + override RouteHandler getRouteHandler() { result = rh } + } + + /** + * A Fastify request source, that is, the request parameter of a + * route handler. + */ + private class RequestSource extends HTTP::Servers::RequestSource { + RouteHandler rh; + + RequestSource() { this = rh.getRequestParameter() } + + /** + * Gets the route handler that handles this request. + */ + override RouteHandler getRouteHandler() { result = rh } + } + + /** + * A call to a Fastify method that sets up a route. + */ + class RouteSetup extends MethodCallExpr, HTTP::Servers::StandardRouteSetup { + ServerDefinition server; + string methodName; + + RouteSetup() { + this.getMethodName() = methodName and + methodName = ["route", "get", "head", "post", "put", "delete", "options", "patch"] and + server.flowsTo(this.getReceiver()) + } + + override DataFlow::SourceNode getARouteHandler() { + result = getARouteHandler(DataFlow::TypeBackTracker::end()) + } + + private DataFlow::SourceNode getARouteHandler(DataFlow::TypeBackTracker t) { + t.start() and + result = this.getARouteHandlerExpr().getALocalSource() + or + exists(DataFlow::TypeBackTracker t2 | result = this.getARouteHandler(t2).backtrack(t2, t)) + } + + override Expr getServer() { result = server } + + /** Gets an argument that represents a route handler being registered. */ + private DataFlow::Node getARouteHandlerExpr() { + if methodName = "route" + then + result = + this + .flow() + .(DataFlow::MethodCallNode) + .getOptionArgument(0, + ["onRequest", "preParsing", "preValidation", "preHandler", "preSerialization", + "onSend", "onResponse", "handler"]) + else result = getLastArgument().flow() + } + } + + /** + * An access to a user-controlled Fastify request input. + */ + private class RequestInputAccess extends HTTP::RequestInputAccess { + RouteHandler rh; + string kind; + + RequestInputAccess() { + exists(string name | this = rh.getARequestSource().ref().getAPropertyRead(name) | + kind = "parameter" and + name = ["params", "query"] + or + kind = "body" and + name = "body" + ) + } + + override RouteHandler getRouteHandler() { result = rh } + + override string getKind() { result = kind } + + override predicate isUserControlledObject() { + kind = "body" and + ( + usesFastifyPlugin(rh, + DataFlow::moduleImport(["fastify-xml-body-parser", "fastify-formbody"])) + or + usesMiddleware(rh, + any(ExpressLibraries::BodyParser bodyParser | bodyParser.producesUserControlledObjects())) + ) + or + kind = "parameter" and + usesFastifyPlugin(rh, DataFlow::moduleImport("fastify-qs")) + } + } + + /** + * Holds if `rh` uses `plugin`. + */ + private predicate usesFastifyPlugin(RouteHandler rh, DataFlow::SourceNode plugin) { + exists(RouteSetup setup | + plugin + .flowsTo(setup + .getServer() + .flow() + .(DataFlow::SourceNode) + .getAMethodCall("register") + .getArgument(0)) and // only matches the plugins that apply to all routes + rh = setup.getARouteHandler() + ) + } + + /** + * Holds if `rh` uses `plugin`. + */ + private predicate usesMiddleware(RouteHandler rh, DataFlow::SourceNode middleware) { + exists(RouteSetup setup | + middleware + .flowsTo(setup + .getServer() + .flow() + .(DataFlow::SourceNode) + .getAMethodCall("use") + .getArgument(0)) and // only matches the middlewares that apply to all routes + rh = setup.getARouteHandler() + ) + } + + /** + * An access to a header on a Fastify request. + */ + private class RequestHeaderAccess extends HTTP::RequestHeaderAccess { + RouteHandler rh; + + RequestHeaderAccess() { + this = rh.getARequestSource().ref().getAPropertyRead("headers").getAPropertyRead() + } + + override string getAHeaderName() { + result = this.(DataFlow::PropRead).getPropertyName().toLowerCase() + } + + override RouteHandler getRouteHandler() { result = rh } + + override string getKind() { result = "header" } + } + + /** + * An argument passed to the `send` or `end` method of an HTTP response object. + */ + private class ResponseSendArgument extends HTTP::ResponseSendArgument { + RouteHandler rh; + + ResponseSendArgument() { + this = rh.getAResponseSource().ref().getAMethodCall("send").getArgument(0).asExpr() + or + this = rh.(DataFlow::FunctionNode).getAReturn().asExpr() + } + + override RouteHandler getRouteHandler() { result = rh } + } + + /** + * An invocation of the `redirect` method of an HTTP response object. + */ + private class RedirectInvocation extends HTTP::RedirectInvocation, MethodCallExpr { + RouteHandler rh; + + RedirectInvocation() { + this = rh.getAResponseSource().ref().getAMethodCall("redirect").asExpr() + } + + override Expr getUrlArgument() { result = this.getLastArgument() } + + override RouteHandler getRouteHandler() { result = rh } + } + + /** + * An invocation that sets a single header of the HTTP response. + */ + private class SetOneHeader extends HTTP::Servers::StandardHeaderDefinition, + DataFlow::MethodCallNode { + RouteHandler rh; + + SetOneHeader() { + this = rh.getAResponseSource().ref().getAMethodCall("header") and + this.getNumArgument() = 2 + } + + override RouteHandler getRouteHandler() { result = rh } + } + + /** + * An invocation that sets any number of headers of the HTTP response. + */ + class SetMultipleHeaders extends HTTP::ExplicitHeaderDefinition, DataFlow::MethodCallNode { + RouteHandler rh; + + SetMultipleHeaders() { + this = rh.getAResponseSource().ref().getAMethodCall("headers") and + this.getNumArgument() = 1 + } + + /** + * Gets a reference to the multiple headers object that is to be set. + */ + private DataFlow::SourceNode getAHeaderSource() { result.flowsTo(this.getArgument(0)) } + + override predicate definesExplicitly(string headerName, Expr headerValue) { + exists(string header | + getAHeaderSource().hasPropertyWrite(header, headerValue.flow()) and + headerName = header.toLowerCase() + ) + } + + override RouteHandler getRouteHandler() { result = rh } + + override Expr getNameExpr() { + exists(DataFlow::PropWrite write | + this.getAHeaderSource().flowsTo(write.getBase()) and + result = write.getPropertyNameExpr() + ) + } + } +} diff --git a/javascript/ql/src/semmle/javascript/frameworks/HttpFrameworks.qll b/javascript/ql/src/semmle/javascript/frameworks/HttpFrameworks.qll index 810246e429e..c6798fb22a5 100644 --- a/javascript/ql/src/semmle/javascript/frameworks/HttpFrameworks.qll +++ b/javascript/ql/src/semmle/javascript/frameworks/HttpFrameworks.qll @@ -4,3 +4,4 @@ import semmle.javascript.frameworks.Koa import semmle.javascript.frameworks.NodeJSLib import semmle.javascript.frameworks.Restify import semmle.javascript.frameworks.Connect +import semmle.javascript.frameworks.Fastify diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/DomBasedXss.qll b/javascript/ql/src/semmle/javascript/security/dataflow/DomBasedXss.qll index a1b2a4bf6d9..65ab201a720 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/DomBasedXss.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/DomBasedXss.qll @@ -24,6 +24,10 @@ module DomBasedXss { node instanceof Sanitizer } + override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode guard) { + guard instanceof SanitizerGuard + } + override predicate isAdditionalLoadStoreStep( DataFlow::Node pred, DataFlow::Node succ, string predProp, string succProp ) { @@ -47,6 +51,10 @@ module DomBasedXss { prop = urlSuffixPseudoProperty() ) } + + override predicate isSanitizerEdge(DataFlow::Node pred, DataFlow::Node succ) { + DomBasedXss::isOptionallySanitizedEdge(pred, succ) + } } private string urlSuffixPseudoProperty() { result = "$UrlSuffix$" } diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/IncompleteHtmlAttributeSanitizationCustomizations.qll b/javascript/ql/src/semmle/javascript/security/dataflow/IncompleteHtmlAttributeSanitizationCustomizations.qll index ceb63d013f3..cd1d9c1cc6c 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/IncompleteHtmlAttributeSanitizationCustomizations.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/IncompleteHtmlAttributeSanitizationCustomizations.qll @@ -51,8 +51,11 @@ module IncompleteHtmlAttributeSanitization { string lhs; HtmlAttributeConcatenation() { - lhs = this.getPreviousLeaf().getStringValue().regexpCapture("(.*)=\"[^\"]*", 1) and - this.getNextLeaf().getStringValue().regexpMatch(".*\".*") + lhs = this.getPreviousLeaf().getStringValue().regexpCapture("(?s)(.*)=\"[^\"]*", 1) and + ( + this.getNextLeaf().getStringValue().regexpMatch(".*\".*") or + this instanceof StringOps::HtmlConcatenationLeaf + ) } /** diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/ReflectedXss.qll b/javascript/ql/src/semmle/javascript/security/dataflow/ReflectedXss.qll index ece299d7fa0..b7034996a63 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/ReflectedXss.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/ReflectedXss.qll @@ -22,5 +22,9 @@ module ReflectedXss { super.isSanitizer(node) or node instanceof Sanitizer } + + override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode guard) { + guard instanceof SanitizerGuard + } } } diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/StoredXss.qll b/javascript/ql/src/semmle/javascript/security/dataflow/StoredXss.qll index 7741d1e8778..9fbf0933042 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/StoredXss.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/StoredXss.qll @@ -22,6 +22,10 @@ module StoredXss { super.isSanitizer(node) or node instanceof Sanitizer } + + override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode guard) { + guard instanceof SanitizerGuard + } } /** A file name, considered as a flow source for stored XSS. */ diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/Xss.qll b/javascript/ql/src/semmle/javascript/security/dataflow/Xss.qll index 55be5464a6e..6a18355e12e 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/Xss.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/Xss.qll @@ -23,6 +23,9 @@ module Shared { /** A sanitizer for XSS vulnerabilities. */ abstract class Sanitizer extends DataFlow::Node { } + /** A sanitizer guard for XSS vulnerabilities. */ + abstract class SanitizerGuard extends TaintTracking::SanitizerGuardNode { } + /** * A regexp replacement involving an HTML meta-character, viewed as a sanitizer for * XSS vulnerabilities. @@ -51,6 +54,25 @@ module Shared { ) } } + + private import semmle.javascript.security.dataflow.IncompleteHtmlAttributeSanitizationCustomizations::IncompleteHtmlAttributeSanitization as IncompleteHTML + + /** + * A guard that checks if a string can contain quotes, which is a guard for strings that are inside a HTML attribute. + */ + class QuoteGuard extends SanitizerGuard, StringOps::Includes { + QuoteGuard() { + this.getSubstring().mayHaveStringValue("\"") and + this + .getBaseString() + .getALocalSource() + .flowsTo(any(IncompleteHTML::HtmlAttributeConcatenation attributeConcat)) + } + + override predicate sanitizes(boolean outcome, Expr e) { + e = this.getBaseString().getEnclosingExpr() and outcome = this.getPolarity().booleanNot() + } + } } /** Provides classes and predicates for the DOM-based XSS query. */ @@ -64,6 +86,9 @@ module DomBasedXss { /** A sanitizer for DOM-based XSS vulnerabilities. */ abstract class Sanitizer extends Shared::Sanitizer { } + /** A sanitizer guard for DOM-based XSS vulnerabilities. */ + abstract class SanitizerGuard extends Shared::SanitizerGuard { } + /** * An expression whose value is interpreted as HTML * and may be inserted into the DOM through a library. @@ -302,6 +327,38 @@ module DomBasedXss { private class MetacharEscapeSanitizer extends Sanitizer, Shared::MetacharEscapeSanitizer { } private class UriEncodingSanitizer extends Sanitizer, Shared::UriEncodingSanitizer { } + + private class QuoteGuard extends SanitizerGuard, Shared::QuoteGuard { } + + /** + * Holds if there exists two dataflow edges to `succ`, where one edges is sanitized, and the other edge starts with `pred`. + */ + predicate isOptionallySanitizedEdge(DataFlow::Node pred, DataFlow::Node succ) { + exists(HtmlSanitizerCall sanitizer | + // sanitized = sanitize ? sanitizer(source) : source; + exists(ConditionalExpr branch, Variable var, VarAccess access | + branch = succ.asExpr() and access = var.getAnAccess() + | + branch.getABranch() = access and + pred.getEnclosingExpr() = access and + sanitizer = branch.getABranch().flow() and + sanitizer.getAnArgument().getEnclosingExpr() = var.getAnAccess() + ) + or + // sanitized = source; if (sanitize) {sanitized = sanitizer(source)}; + exists(SsaPhiNode phi, SsaExplicitDefinition a, SsaDefinition b | + a = phi.getAnInput().getDefinition() and + b = phi.getAnInput().getDefinition() and + count(phi.getAnInput()) = 2 and + not a = b and + sanitizer = DataFlow::valueNode(a.getDef().getSource()) and + sanitizer.getAnArgument().asExpr().(VarAccess).getVariable() = b.getSourceVariable() + | + pred = DataFlow::ssaDefinitionNode(b) and + succ = DataFlow::ssaDefinitionNode(phi) + ) + ) + } } /** Provides classes and predicates for the reflected XSS query. */ @@ -315,6 +372,9 @@ module ReflectedXss { /** A sanitizer for reflected XSS vulnerabilities. */ abstract class Sanitizer extends Shared::Sanitizer { } + /** A sanitizer guard for reflected XSS vulnerabilities. */ + abstract class SanitizerGuard extends Shared::SanitizerGuard { } + /** * An expression that is sent as part of an HTTP response, considered as an XSS sink. * @@ -401,6 +461,8 @@ module ReflectedXss { private class MetacharEscapeSanitizer extends Sanitizer, Shared::MetacharEscapeSanitizer { } private class UriEncodingSanitizer extends Sanitizer, Shared::UriEncodingSanitizer { } + + private class QuoteGuard extends SanitizerGuard, Shared::QuoteGuard { } } /** Provides classes and predicates for the stored XSS query. */ @@ -414,6 +476,9 @@ module StoredXss { /** A sanitizer for stored XSS vulnerabilities. */ abstract class Sanitizer extends Shared::Sanitizer { } + /** A sanitizer guard for stored XSS vulnerabilities. */ + abstract class SanitizerGuard extends Shared::SanitizerGuard { } + /** An arbitrary XSS sink, considered as a flow sink for stored XSS. */ private class AnySink extends Sink { AnySink() { this instanceof Shared::Sink } @@ -429,6 +494,8 @@ module StoredXss { private class MetacharEscapeSanitizer extends Sanitizer, Shared::MetacharEscapeSanitizer { } private class UriEncodingSanitizer extends Sanitizer, Shared::UriEncodingSanitizer { } + + private class QuoteGuard extends SanitizerGuard, Shared::QuoteGuard { } } /** Provides classes and predicates for the XSS through DOM query. */ diff --git a/javascript/ql/src/semmle/javascript/security/dataflow/XssThroughDom.qll b/javascript/ql/src/semmle/javascript/security/dataflow/XssThroughDom.qll index 716002b7db6..3291b4f52a8 100644 --- a/javascript/ql/src/semmle/javascript/security/dataflow/XssThroughDom.qll +++ b/javascript/ql/src/semmle/javascript/security/dataflow/XssThroughDom.qll @@ -31,7 +31,12 @@ module XssThroughDom { override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode guard) { guard instanceof TypeTestGuard or - guard instanceof UnsafeJQuery::PropertyPresenceSanitizer + guard instanceof UnsafeJQuery::PropertyPresenceSanitizer or + guard instanceof DomBasedXss::SanitizerGuard + } + + override predicate isSanitizerEdge(DataFlow::Node pred, DataFlow::Node succ) { + DomBasedXss::isOptionallySanitizedEdge(pred, succ) } } diff --git a/javascript/ql/test/library-tests/DataFlow/argumentPassing.expected b/javascript/ql/test/library-tests/DataFlow/argumentPassing.expected new file mode 100644 index 00000000000..37fad6b0f65 --- /dev/null +++ b/javascript/ql/test/library-tests/DataFlow/argumentPassing.expected @@ -0,0 +1,10 @@ +| arguments.js:11:5:11:14 | f(1, 2, 3) | arguments.js:11:7:11:7 | 1 | arguments.js:2:5:10:5 | functio ... ;\\n } | arguments.js:2:16:2:16 | x | +| arguments.js:11:5:11:14 | f(1, 2, 3) | arguments.js:11:7:11:7 | 1 | arguments.js:2:5:10:5 | functio ... ;\\n } | arguments.js:4:28:4:39 | arguments[0] | +| arguments.js:11:5:11:14 | f(1, 2, 3) | arguments.js:11:10:11:10 | 2 | arguments.js:2:5:10:5 | functio ... ;\\n } | arguments.js:5:25:5:36 | arguments[1] | +| arguments.js:11:5:11:14 | f(1, 2, 3) | arguments.js:11:13:11:13 | 3 | arguments.js:2:5:10:5 | functio ... ;\\n } | arguments.js:7:24:7:30 | args[2] | +| sources.js:3:1:5:6 | (functi ... \\n})(23) | sources.js:5:4:5:5 | 23 | sources.js:3:2:5:1 | functio ... x+19;\\n} | sources.js:3:11:3:11 | x | +| tst.js:16:1:20:9 | (functi ... ("arg") | tst.js:20:4:20:8 | "arg" | tst.js:16:2:20:1 | functio ... n "";\\n} | tst.js:16:13:16:13 | a | +| tst.js:35:1:35:7 | g(true) | tst.js:35:3:35:6 | true | tst.js:32:1:34:1 | functio ... ables\\n} | tst.js:32:12:32:12 | b | +| tst.js:44:1:44:5 | o.m() | tst.js:44:1:44:1 | o | tst.js:39:4:41:3 | () {\\n this;\\n } | tst.js:39:4:39:3 | this | +| tst.js:87:1:96:2 | (functi ... r: 0\\n}) | tst.js:92:4:96:1 | {\\n p: ... r: 0\\n} | tst.js:87:2:92:1 | functio ... + z;\\n} | tst.js:87:11:87:24 | { p: x, ...o } | +| tst.js:98:1:103:17 | (functi ... 3, 0 ]) | tst.js:103:4:103:16 | [ 19, 23, 0 ] | tst.js:98:2:103:1 | functio ... + z;\\n} | tst.js:98:11:98:24 | [ x, ...rest ] | diff --git a/javascript/ql/test/library-tests/DataFlow/argumentPassing.ql b/javascript/ql/test/library-tests/DataFlow/argumentPassing.ql new file mode 100644 index 00000000000..56bb46f695a --- /dev/null +++ b/javascript/ql/test/library-tests/DataFlow/argumentPassing.ql @@ -0,0 +1,6 @@ +import javascript +import semmle.javascript.dataflow.internal.FlowSteps as FlowSteps + +from DataFlow::Node invk, DataFlow::Node arg, Function f, DataFlow::SourceNode parm +where FlowSteps::argumentPassing(invk, arg, f, parm) +select invk, arg, f, parm diff --git a/javascript/ql/test/library-tests/DataFlow/arguments.js b/javascript/ql/test/library-tests/DataFlow/arguments.js new file mode 100644 index 00000000000..e0ca10ffc59 --- /dev/null +++ b/javascript/ql/test/library-tests/DataFlow/arguments.js @@ -0,0 +1,12 @@ +(function() { + function f(x) { + let firstArg = x; + let alsoFirstArg = arguments[0]; + let secondArg = arguments[1]; + let args = arguments; + let thirdArg = args[2]; + arguments = {}; + let notFirstArg = arguments[0]; + } + f(1, 2, 3); +})(); diff --git a/javascript/ql/test/library-tests/DataFlow/enclosingExpr.expected b/javascript/ql/test/library-tests/DataFlow/enclosingExpr.expected index 80b838abfc6..e5705fa6f83 100644 --- a/javascript/ql/test/library-tests/DataFlow/enclosingExpr.expected +++ b/javascript/ql/test/library-tests/DataFlow/enclosingExpr.expected @@ -1,3 +1,42 @@ +| arguments.js:1:1:12:2 | (functi ... 3);\\n}) | arguments.js:1:1:12:2 | (functi ... 3);\\n}) | +| arguments.js:1:1:12:4 | (functi ... );\\n})() | arguments.js:1:1:12:4 | (functi ... );\\n})() | +| arguments.js:1:2:12:1 | functio ... , 3);\\n} | arguments.js:1:2:12:1 | functio ... , 3);\\n} | +| arguments.js:2:14:2:14 | f | arguments.js:2:14:2:14 | f | +| arguments.js:2:16:2:16 | x | arguments.js:2:16:2:16 | x | +| arguments.js:3:13:3:20 | firstArg | arguments.js:3:13:3:20 | firstArg | +| arguments.js:3:13:3:24 | firstArg = x | arguments.js:3:13:3:24 | firstArg = x | +| arguments.js:3:24:3:24 | x | arguments.js:3:24:3:24 | x | +| arguments.js:4:13:4:24 | alsoFirstArg | arguments.js:4:13:4:24 | alsoFirstArg | +| arguments.js:4:13:4:39 | alsoFir ... ents[0] | arguments.js:4:13:4:39 | alsoFir ... ents[0] | +| arguments.js:4:28:4:36 | arguments | arguments.js:4:28:4:36 | arguments | +| arguments.js:4:28:4:39 | arguments[0] | arguments.js:4:28:4:39 | arguments[0] | +| arguments.js:4:38:4:38 | 0 | arguments.js:4:38:4:38 | 0 | +| arguments.js:5:13:5:21 | secondArg | arguments.js:5:13:5:21 | secondArg | +| arguments.js:5:13:5:36 | secondA ... ents[1] | arguments.js:5:13:5:36 | secondA ... ents[1] | +| arguments.js:5:25:5:33 | arguments | arguments.js:5:25:5:33 | arguments | +| arguments.js:5:25:5:36 | arguments[1] | arguments.js:5:25:5:36 | arguments[1] | +| arguments.js:5:35:5:35 | 1 | arguments.js:5:35:5:35 | 1 | +| arguments.js:6:13:6:16 | args | arguments.js:6:13:6:16 | args | +| arguments.js:6:13:6:28 | args = arguments | arguments.js:6:13:6:28 | args = arguments | +| arguments.js:6:20:6:28 | arguments | arguments.js:6:20:6:28 | arguments | +| arguments.js:7:13:7:20 | thirdArg | arguments.js:7:13:7:20 | thirdArg | +| arguments.js:7:13:7:30 | thirdArg = args[2] | arguments.js:7:13:7:30 | thirdArg = args[2] | +| arguments.js:7:24:7:27 | args | arguments.js:7:24:7:27 | args | +| arguments.js:7:24:7:30 | args[2] | arguments.js:7:24:7:30 | args[2] | +| arguments.js:7:29:7:29 | 2 | arguments.js:7:29:7:29 | 2 | +| arguments.js:8:9:8:17 | arguments | arguments.js:8:9:8:17 | arguments | +| arguments.js:8:9:8:22 | arguments = {} | arguments.js:8:9:8:22 | arguments = {} | +| arguments.js:8:21:8:22 | {} | arguments.js:8:21:8:22 | {} | +| arguments.js:9:13:9:23 | notFirstArg | arguments.js:9:13:9:23 | notFirstArg | +| arguments.js:9:13:9:38 | notFirs ... ents[0] | arguments.js:9:13:9:38 | notFirs ... ents[0] | +| arguments.js:9:27:9:35 | arguments | arguments.js:9:27:9:35 | arguments | +| arguments.js:9:27:9:38 | arguments[0] | arguments.js:9:27:9:38 | arguments[0] | +| arguments.js:9:37:9:37 | 0 | arguments.js:9:37:9:37 | 0 | +| arguments.js:11:5:11:5 | f | arguments.js:11:5:11:5 | f | +| arguments.js:11:5:11:14 | f(1, 2, 3) | arguments.js:11:5:11:14 | f(1, 2, 3) | +| arguments.js:11:7:11:7 | 1 | arguments.js:11:7:11:7 | 1 | +| arguments.js:11:10:11:10 | 2 | arguments.js:11:10:11:10 | 2 | +| arguments.js:11:13:11:13 | 3 | arguments.js:11:13:11:13 | 3 | | eval.js:1:10:1:10 | k | eval.js:1:10:1:10 | k | | eval.js:2:7:2:7 | x | eval.js:2:7:2:7 | x | | eval.js:2:7:2:12 | x = 42 | eval.js:2:7:2:12 | x = 42 | diff --git a/javascript/ql/test/library-tests/DataFlow/flowStep.expected b/javascript/ql/test/library-tests/DataFlow/flowStep.expected index 49f208f89c1..cfb92ff6339 100644 --- a/javascript/ql/test/library-tests/DataFlow/flowStep.expected +++ b/javascript/ql/test/library-tests/DataFlow/flowStep.expected @@ -1,3 +1,16 @@ +| arguments.js:1:2:12:1 | functio ... , 3);\\n} | arguments.js:1:1:12:2 | (functi ... 3);\\n}) | +| arguments.js:2:5:2:5 | arguments | arguments.js:4:28:4:36 | arguments | +| arguments.js:2:5:2:5 | arguments | arguments.js:5:25:5:33 | arguments | +| arguments.js:2:5:2:5 | arguments | arguments.js:6:20:6:28 | arguments | +| arguments.js:2:5:10:5 | functio ... ;\\n } | arguments.js:2:14:2:14 | f | +| arguments.js:2:14:2:14 | f | arguments.js:11:5:11:5 | f | +| arguments.js:2:16:2:16 | x | arguments.js:2:16:2:16 | x | +| arguments.js:2:16:2:16 | x | arguments.js:3:24:3:24 | x | +| arguments.js:6:13:6:28 | args | arguments.js:7:24:7:27 | args | +| arguments.js:6:20:6:28 | arguments | arguments.js:6:13:6:28 | args | +| arguments.js:8:9:8:22 | arguments | arguments.js:9:27:9:35 | arguments | +| arguments.js:8:21:8:22 | {} | arguments.js:8:9:8:22 | arguments | +| arguments.js:8:21:8:22 | {} | arguments.js:8:9:8:22 | arguments = {} | | eval.js:2:7:2:12 | x | eval.js:4:3:4:3 | x | | eval.js:2:11:2:12 | 42 | eval.js:2:7:2:12 | x | | sources.js:1:6:1:6 | x | sources.js:1:6:1:6 | x | diff --git a/javascript/ql/test/library-tests/DataFlow/getIntValue.expected b/javascript/ql/test/library-tests/DataFlow/getIntValue.expected index fdeffe0b52e..e59b4fdd7c7 100644 --- a/javascript/ql/test/library-tests/DataFlow/getIntValue.expected +++ b/javascript/ql/test/library-tests/DataFlow/getIntValue.expected @@ -1,3 +1,10 @@ +| arguments.js:4:38:4:38 | 0 | 0 | +| arguments.js:5:35:5:35 | 1 | 1 | +| arguments.js:7:29:7:29 | 2 | 2 | +| arguments.js:9:37:9:37 | 0 | 0 | +| arguments.js:11:7:11:7 | 1 | 1 | +| arguments.js:11:10:11:10 | 2 | 2 | +| arguments.js:11:13:11:13 | 3 | 3 | | eval.js:2:11:2:12 | 42 | 42 | | sources.js:4:12:4:13 | 19 | 19 | | sources.js:5:4:5:5 | 23 | 23 | diff --git a/javascript/ql/test/library-tests/DataFlow/incomplete.expected b/javascript/ql/test/library-tests/DataFlow/incomplete.expected index 942748440e5..ae3b700e1a6 100644 --- a/javascript/ql/test/library-tests/DataFlow/incomplete.expected +++ b/javascript/ql/test/library-tests/DataFlow/incomplete.expected @@ -1,3 +1,13 @@ +| arguments.js:1:1:12:4 | exceptional return of (functi ... );\\n})() | call | +| arguments.js:1:2:12:1 | exceptional return of anonymous function | call | +| arguments.js:2:5:10:5 | exceptional return of function f | call | +| arguments.js:2:16:2:16 | x | call | +| arguments.js:4:28:4:39 | arguments[0] | heap | +| arguments.js:5:25:5:36 | arguments[1] | heap | +| arguments.js:7:24:7:30 | args[2] | heap | +| arguments.js:9:27:9:38 | arguments[0] | heap | +| arguments.js:11:5:11:14 | exceptional return of f(1, 2, 3) | call | +| arguments.js:11:5:11:14 | f(1, 2, 3) | call | | eval.js:1:1:5:1 | exceptional return of function k | call | | eval.js:2:7:2:12 | x | eval | | eval.js:3:3:3:6 | eval | global | diff --git a/javascript/ql/test/library-tests/DataFlow/parameters.expected b/javascript/ql/test/library-tests/DataFlow/parameters.expected index 34cc8e6bb66..c6252e77f12 100644 --- a/javascript/ql/test/library-tests/DataFlow/parameters.expected +++ b/javascript/ql/test/library-tests/DataFlow/parameters.expected @@ -1,3 +1,4 @@ +| arguments.js:2:16:2:16 | x | | sources.js:1:6:1:6 | x | | sources.js:3:11:3:11 | x | | sources.js:9:14:9:18 | array | diff --git a/javascript/ql/test/library-tests/DataFlow/sources.expected b/javascript/ql/test/library-tests/DataFlow/sources.expected index ff60d49cf58..faa640a5d84 100644 --- a/javascript/ql/test/library-tests/DataFlow/sources.expected +++ b/javascript/ql/test/library-tests/DataFlow/sources.expected @@ -1,3 +1,16 @@ +| arguments.js:1:1:1:0 | this | +| arguments.js:1:1:12:4 | (functi ... );\\n})() | +| arguments.js:1:2:1:1 | this | +| arguments.js:1:2:12:1 | functio ... , 3);\\n} | +| arguments.js:2:5:2:4 | this | +| arguments.js:2:5:10:5 | functio ... ;\\n } | +| arguments.js:2:16:2:16 | x | +| arguments.js:4:28:4:39 | arguments[0] | +| arguments.js:5:25:5:36 | arguments[1] | +| arguments.js:7:24:7:30 | args[2] | +| arguments.js:8:21:8:22 | {} | +| arguments.js:9:27:9:38 | arguments[0] | +| arguments.js:11:5:11:14 | f(1, 2, 3) | | eval.js:1:1:1:0 | this | | eval.js:1:1:1:0 | this | | eval.js:1:1:5:1 | functio ... eval`\\n} | diff --git a/javascript/ql/test/library-tests/NodeJS/Require.expected b/javascript/ql/test/library-tests/NodeJS/Require.expected index 7e9a50685ab..cdbead07939 100644 --- a/javascript/ql/test/library-tests/NodeJS/Require.expected +++ b/javascript/ql/test/library-tests/NodeJS/Require.expected @@ -11,6 +11,8 @@ | d.js:7:1:7:14 | require('foo') | | e.js:5:1:5:18 | require("process") | | f.js:2:1:2:7 | r("fs") | +| g.js:1:1:1:96 | (proces ... https") | +| g.js:1:43:1:61 | require("electron") | | index.js:1:12:1:26 | require('path') | | index.js:2:1:2:41 | require ... b.js")) | | mjs-files/require-from-js.js:1:12:1:36 | require ... on-me') | diff --git a/javascript/ql/test/library-tests/NodeJS/g.js b/javascript/ql/test/library-tests/NodeJS/g.js new file mode 100644 index 00000000000..9d04081336e --- /dev/null +++ b/javascript/ql/test/library-tests/NodeJS/g.js @@ -0,0 +1 @@ +(process && "renderer" === process.type ? require("electron").remote.require : require)("https"); \ No newline at end of file diff --git a/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected b/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected index 9c12e879372..261631d61ab 100644 --- a/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected +++ b/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected @@ -31,6 +31,7 @@ typeInferenceMismatch | callbacks.js:44:17:44:24 | source() | callbacks.js:41:10:41:10 | x | | callbacks.js:50:18:50:25 | source() | callbacks.js:30:29:30:29 | y | | callbacks.js:51:18:51:25 | source() | callbacks.js:30:29:30:29 | y | +| capture-flow.js:9:11:9:18 | source() | capture-flow.js:14:10:14:16 | outer() | | captured-sanitizer.js:25:3:25:10 | source() | captured-sanitizer.js:15:10:15:10 | x | | closure.js:6:15:6:22 | source() | closure.js:8:8:8:31 | string. ... (taint) | | closure.js:6:15:6:22 | source() | closure.js:9:8:9:25 | string.trim(taint) | diff --git a/javascript/ql/test/library-tests/TaintTracking/DataFlowTracking.expected b/javascript/ql/test/library-tests/TaintTracking/DataFlowTracking.expected index 471c5991025..5434cc908ba 100644 --- a/javascript/ql/test/library-tests/TaintTracking/DataFlowTracking.expected +++ b/javascript/ql/test/library-tests/TaintTracking/DataFlowTracking.expected @@ -22,6 +22,7 @@ | callbacks.js:44:17:44:24 | source() | callbacks.js:41:10:41:10 | x | | callbacks.js:50:18:50:25 | source() | callbacks.js:30:29:30:29 | y | | callbacks.js:51:18:51:25 | source() | callbacks.js:30:29:30:29 | y | +| capture-flow.js:9:11:9:18 | source() | capture-flow.js:14:10:14:16 | outer() | | captured-sanitizer.js:25:3:25:10 | source() | captured-sanitizer.js:15:10:15:10 | x | | constructor-calls.js:4:18:4:25 | source() | constructor-calls.js:18:8:18:14 | c.taint | | constructor-calls.js:4:18:4:25 | source() | constructor-calls.js:22:8:22:19 | c_safe.taint | diff --git a/javascript/ql/test/library-tests/TaintTracking/capture-flow.js b/javascript/ql/test/library-tests/TaintTracking/capture-flow.js new file mode 100644 index 00000000000..f9b7d30d4cf --- /dev/null +++ b/javascript/ql/test/library-tests/TaintTracking/capture-flow.js @@ -0,0 +1,19 @@ +import 'dummy'; + +function outerMost() { + function outer() { + var captured; + function f(x) { + captured = x; + } + f(source()); + + return captured; + } + + sink(outer()); // NOT OK + + return outer(); +} + +sink(outerMost()); // NOT OK - but missed diff --git a/javascript/ql/test/library-tests/frameworks/fastify/HeaderAccess.qll b/javascript/ql/test/library-tests/frameworks/fastify/HeaderAccess.qll new file mode 100644 index 00000000000..22d1d6aee53 --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/fastify/HeaderAccess.qll @@ -0,0 +1,5 @@ +import javascript + +query predicate test_HeaderAccess(HTTP::RequestHeaderAccess access, string res) { + res = access.getAHeaderName() +} diff --git a/javascript/ql/test/library-tests/frameworks/fastify/HeaderDefinition.qll b/javascript/ql/test/library-tests/frameworks/fastify/HeaderDefinition.qll new file mode 100644 index 00000000000..bf3ef7fb7fc --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/fastify/HeaderDefinition.qll @@ -0,0 +1,5 @@ +import javascript + +query predicate test_HeaderDefinition(HTTP::HeaderDefinition hd, Fastify::RouteHandler rh) { + rh = hd.getRouteHandler() +} diff --git a/javascript/ql/test/library-tests/frameworks/fastify/HeaderDefinition_defines.qll b/javascript/ql/test/library-tests/frameworks/fastify/HeaderDefinition_defines.qll new file mode 100644 index 00000000000..43413a4dc4b --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/fastify/HeaderDefinition_defines.qll @@ -0,0 +1,5 @@ +import javascript + +query predicate test_HeaderDefinition_defines(HTTP::HeaderDefinition hd, string name, string value) { + hd.defines(name, value) and hd.getRouteHandler() instanceof Fastify::RouteHandler +} diff --git a/javascript/ql/test/library-tests/frameworks/fastify/HeaderDefinition_getAHeaderName.qll b/javascript/ql/test/library-tests/frameworks/fastify/HeaderDefinition_getAHeaderName.qll new file mode 100644 index 00000000000..62e1d62c06f --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/fastify/HeaderDefinition_getAHeaderName.qll @@ -0,0 +1,5 @@ +import javascript + +query predicate test_HeaderDefinition_getAHeaderName(HTTP::HeaderDefinition hd, string res) { + hd.getRouteHandler() instanceof Fastify::RouteHandler and res = hd.getAHeaderName() +} diff --git a/javascript/ql/test/library-tests/frameworks/fastify/RedirectInvocation.qll b/javascript/ql/test/library-tests/frameworks/fastify/RedirectInvocation.qll new file mode 100644 index 00000000000..fffbf35a79e --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/fastify/RedirectInvocation.qll @@ -0,0 +1,5 @@ +import javascript + +query predicate test_RedirectInvocation(HTTP::RedirectInvocation invk, Fastify::RouteHandler rh) { + invk.getRouteHandler() = rh +} diff --git a/javascript/ql/test/library-tests/frameworks/fastify/RequestInputAccess.qll b/javascript/ql/test/library-tests/frameworks/fastify/RequestInputAccess.qll new file mode 100644 index 00000000000..d8cdc2a4ccb --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/fastify/RequestInputAccess.qll @@ -0,0 +1,11 @@ +import javascript + +query predicate test_RequestInputAccess( + HTTP::RequestInputAccess ria, string res, Fastify::RouteHandler rh, boolean isUserControlledObject +) { + ria.getRouteHandler() = rh and + res = ria.getKind() and + if ria.isUserControlledObject() + then isUserControlledObject = true + else isUserControlledObject = false +} diff --git a/javascript/ql/test/library-tests/frameworks/fastify/ResponseSendArgument.qll b/javascript/ql/test/library-tests/frameworks/fastify/ResponseSendArgument.qll new file mode 100644 index 00000000000..8f221b18bd7 --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/fastify/ResponseSendArgument.qll @@ -0,0 +1,5 @@ +import javascript + +query predicate test_ResponseSendArgument(HTTP::ResponseSendArgument arg, Fastify::RouteHandler rh) { + arg.getRouteHandler() = rh +} diff --git a/javascript/ql/test/library-tests/frameworks/fastify/RouteHandler.qll b/javascript/ql/test/library-tests/frameworks/fastify/RouteHandler.qll new file mode 100644 index 00000000000..89598e2169e --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/fastify/RouteHandler.qll @@ -0,0 +1,3 @@ +import javascript + +query predicate test_RouteHandler(Fastify::RouteHandler rh, Expr res) { res = rh.getServer() } diff --git a/javascript/ql/test/library-tests/frameworks/fastify/RouteHandler_getARequestExpr.qll b/javascript/ql/test/library-tests/frameworks/fastify/RouteHandler_getARequestExpr.qll new file mode 100644 index 00000000000..dbc8d7cb896 --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/fastify/RouteHandler_getARequestExpr.qll @@ -0,0 +1,5 @@ +import semmle.javascript.frameworks.Express + +query predicate test_RouteHandler_getARequestExpr(Fastify::RouteHandler rh, HTTP::RequestExpr res) { + res = rh.getARequestExpr() +} diff --git a/javascript/ql/test/library-tests/frameworks/fastify/RouteHandler_getAResponseHeader.qll b/javascript/ql/test/library-tests/frameworks/fastify/RouteHandler_getAResponseHeader.qll new file mode 100644 index 00000000000..a5394519940 --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/fastify/RouteHandler_getAResponseHeader.qll @@ -0,0 +1,7 @@ +import semmle.javascript.frameworks.Express + +query predicate test_RouteHandler_getAResponseHeader( + Fastify::RouteHandler rh, string name, HTTP::HeaderDefinition res +) { + res = rh.getAResponseHeader(name) +} diff --git a/javascript/ql/test/library-tests/frameworks/fastify/RouteSetup.qll b/javascript/ql/test/library-tests/frameworks/fastify/RouteSetup.qll new file mode 100644 index 00000000000..e75078c94f5 --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/fastify/RouteSetup.qll @@ -0,0 +1,3 @@ +import javascript + +query predicate test_RouteSetup(Fastify::RouteSetup rs) { any() } diff --git a/javascript/ql/test/library-tests/frameworks/fastify/RouteSetup_getARouteHandler.qll b/javascript/ql/test/library-tests/frameworks/fastify/RouteSetup_getARouteHandler.qll new file mode 100644 index 00000000000..51e333d54fa --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/fastify/RouteSetup_getARouteHandler.qll @@ -0,0 +1,5 @@ +import javascript + +query predicate test_RouteSetup_getARouteHandler(Fastify::RouteSetup r, DataFlow::SourceNode res) { + res = r.getARouteHandler() +} diff --git a/javascript/ql/test/library-tests/frameworks/fastify/RouteSetup_getServer.qll b/javascript/ql/test/library-tests/frameworks/fastify/RouteSetup_getServer.qll new file mode 100644 index 00000000000..8339498d094 --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/fastify/RouteSetup_getServer.qll @@ -0,0 +1,3 @@ +import javascript + +query predicate test_RouteSetup_getServer(Fastify::RouteSetup rs, Expr res) { res = rs.getServer() } diff --git a/javascript/ql/test/library-tests/frameworks/fastify/ServerDefinition.qll b/javascript/ql/test/library-tests/frameworks/fastify/ServerDefinition.qll new file mode 100644 index 00000000000..97fb53f0f8e --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/fastify/ServerDefinition.qll @@ -0,0 +1,3 @@ +import javascript + +query predicate test_ServerDefinition(Fastify::ServerDefinition s) { any() } diff --git a/javascript/ql/test/library-tests/frameworks/fastify/src/fastify.js b/javascript/ql/test/library-tests/frameworks/fastify/src/fastify.js new file mode 100644 index 00000000000..8c5264b50e8 --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/fastify/src/fastify.js @@ -0,0 +1,92 @@ +var fastify = require("fastify")(); + +fastify.get( + "/", + /* handler */ async (request, reply) => { + return { hello: "world" }; // response + } +); + +fastify.route({ + method: "GET", + url: "/", + onRequest: /* handler */ (request, reply, done) => {}, + preParsing: /* handler */ (request, reply, done) => {}, + preValidation: /* handler */ (request, reply, done) => {}, + preHandler: /* handler */ (request, reply, done) => {}, + preSerialization: /* handler */ (request, reply, payload, done) => {}, + onSend: /* handler */ (request, reply, payload, done) => {}, + onResponse: /* handler */ (request, reply, done) => {}, + handler: /* handler */ (request, reply) => {} +}); + +fastify.get( + "/", + opts, + /* handler */ (request, reply) => { + reply.send({ hello: "world" }); // response + } +); + +fastify.post( + "/:params", + options, + /* handler */ function(request, reply) { + // request properties + request.query.name; // the parsed querystring + request.body; // the body + request.params.name; // the params matching the URL + request.headers.name; // the headers + + // reply properties + reply.header("name", "value"); // Sets a response header. + reply.headers({ name: "value" }); // Sets all the keys of the object as a response headers. + reply.redirect(code, url); // Redirect to the specified url, the status code is optional (default to 302). + reply.send(payload); // Sends the payload to the user, could be a plain text, a buffer, JSON, stream + } +); +fastify.listen(3000); + +var fastifyWithObjects1 = require("fastify")(); +fastifyWithObjects1.register(require("fastify-xml-body-parser")); +fastifyWithObjects1.post( + "/:params", + /* handler */ function(request, reply) { + request.query; + request.body; + request.params; + } +); + +var fastifyWithObjects2 = require("fastify")(); +fastifyWithObjects2.register(require("fastify-formbody")); +fastifyWithObjects2.post( + "/:params", + /* handler */ function(request, reply) { + request.query; + request.body; + request.params; + } +); + +var fastifyWithObjects3 = require("fastify")(); +fastifyWithObjects3.register(require("fastify-qs")); +fastifyWithObjects3.post( + "/:params", + /* handler */ function(request, reply) { + request.query; + request.body; + request.params; + } +); + +var fastifyWithObjects4 = require("fastify")(); +fastifyWithObjects4.use(require("body-parser").urlencoded({ extended: true })); +fastifyWithObjects4.post( + "/:params", + /* handler */ function(request, reply) { + request.query; + request.body; + request.params; + } +); diff --git a/javascript/ql/test/library-tests/frameworks/fastify/tests.expected b/javascript/ql/test/library-tests/frameworks/fastify/tests.expected new file mode 100644 index 00000000000..1371bf551c8 --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/fastify/tests.expected @@ -0,0 +1,125 @@ +test_RouteSetup +| src/fastify.js:3:1:8:1 | fastify ... e\\n }\\n) | +| src/fastify.js:10:1:21:2 | fastify ... > {}\\n}) | +| src/fastify.js:23:1:29:1 | fastify ... e\\n }\\n) | +| src/fastify.js:31:1:47:1 | fastify ... m\\n }\\n) | +| src/fastify.js:52:1:59:1 | fastify ... ;\\n }\\n) | +| src/fastify.js:63:1:70:1 | fastify ... ;\\n }\\n) | +| src/fastify.js:74:1:81:1 | fastify ... ;\\n }\\n) | +| src/fastify.js:85:1:92:1 | fastify ... ;\\n }\\n) | +test_RequestInputAccess +| src/fastify.js:36:5:36:17 | request.query | parameter | src/fastify.js:34:17:46:3 | functio ... eam\\n } | false | +| src/fastify.js:37:5:37:16 | request.body | body | src/fastify.js:34:17:46:3 | functio ... eam\\n } | false | +| src/fastify.js:38:5:38:18 | request.params | parameter | src/fastify.js:34:17:46:3 | functio ... eam\\n } | false | +| src/fastify.js:39:5:39:24 | request.headers.name | header | src/fastify.js:34:17:46:3 | functio ... eam\\n } | false | +| src/fastify.js:55:5:55:17 | request.query | parameter | src/fastify.js:54:17:58:3 | functio ... ms;\\n } | false | +| src/fastify.js:56:5:56:16 | request.body | body | src/fastify.js:54:17:58:3 | functio ... ms;\\n } | true | +| src/fastify.js:57:5:57:18 | request.params | parameter | src/fastify.js:54:17:58:3 | functio ... ms;\\n } | false | +| src/fastify.js:66:5:66:17 | request.query | parameter | src/fastify.js:65:17:69:3 | functio ... ms;\\n } | false | +| src/fastify.js:67:5:67:16 | request.body | body | src/fastify.js:65:17:69:3 | functio ... ms;\\n } | true | +| src/fastify.js:68:5:68:18 | request.params | parameter | src/fastify.js:65:17:69:3 | functio ... ms;\\n } | false | +| src/fastify.js:77:5:77:17 | request.query | parameter | src/fastify.js:76:17:80:3 | functio ... ms;\\n } | true | +| src/fastify.js:78:5:78:16 | request.body | body | src/fastify.js:76:17:80:3 | functio ... ms;\\n } | false | +| src/fastify.js:79:5:79:18 | request.params | parameter | src/fastify.js:76:17:80:3 | functio ... ms;\\n } | true | +| src/fastify.js:88:5:88:17 | request.query | parameter | src/fastify.js:87:17:91:3 | functio ... ms;\\n } | false | +| src/fastify.js:89:5:89:16 | request.body | body | src/fastify.js:87:17:91:3 | functio ... ms;\\n } | true | +| src/fastify.js:90:5:90:18 | request.params | parameter | src/fastify.js:87:17:91:3 | functio ... ms;\\n } | false | +test_RouteHandler_getAResponseHeader +| src/fastify.js:34:17:46:3 | functio ... eam\\n } | name | src/fastify.js:42:5:42:33 | reply.h ... value") | +| src/fastify.js:34:17:46:3 | functio ... eam\\n } | name | src/fastify.js:43:5:43:36 | reply.h ... lue" }) | +test_HeaderDefinition_defines +| src/fastify.js:42:5:42:33 | reply.h ... value") | name | value | +| src/fastify.js:43:5:43:36 | reply.h ... lue" }) | name | value | +test_HeaderDefinition +| src/fastify.js:42:5:42:33 | reply.h ... value") | src/fastify.js:34:17:46:3 | functio ... eam\\n } | +| src/fastify.js:43:5:43:36 | reply.h ... lue" }) | src/fastify.js:34:17:46:3 | functio ... eam\\n } | +test_RouteSetup_getServer +| src/fastify.js:3:1:8:1 | fastify ... e\\n }\\n) | src/fastify.js:1:15:1:34 | require("fastify")() | +| src/fastify.js:10:1:21:2 | fastify ... > {}\\n}) | src/fastify.js:1:15:1:34 | require("fastify")() | +| src/fastify.js:23:1:29:1 | fastify ... e\\n }\\n) | src/fastify.js:1:15:1:34 | require("fastify")() | +| src/fastify.js:31:1:47:1 | fastify ... m\\n }\\n) | src/fastify.js:1:15:1:34 | require("fastify")() | +| src/fastify.js:52:1:59:1 | fastify ... ;\\n }\\n) | src/fastify.js:50:27:50:46 | require("fastify")() | +| src/fastify.js:63:1:70:1 | fastify ... ;\\n }\\n) | src/fastify.js:61:27:61:46 | require("fastify")() | +| src/fastify.js:74:1:81:1 | fastify ... ;\\n }\\n) | src/fastify.js:72:27:72:46 | require("fastify")() | +| src/fastify.js:85:1:92:1 | fastify ... ;\\n }\\n) | src/fastify.js:83:27:83:46 | require("fastify")() | +test_HeaderDefinition_getAHeaderName +| src/fastify.js:42:5:42:33 | reply.h ... value") | name | +| src/fastify.js:43:5:43:36 | reply.h ... lue" }) | name | +test_ServerDefinition +| src/fastify.js:1:15:1:34 | require("fastify")() | +| src/fastify.js:50:27:50:46 | require("fastify")() | +| src/fastify.js:61:27:61:46 | require("fastify")() | +| src/fastify.js:72:27:72:46 | require("fastify")() | +| src/fastify.js:83:27:83:46 | require("fastify")() | +test_HeaderAccess +| src/fastify.js:39:5:39:24 | request.headers.name | name | +test_RouteSetup_getARouteHandler +| src/fastify.js:3:1:8:1 | fastify ... e\\n }\\n) | src/fastify.js:5:17:7:3 | async ( ... nse\\n } | +| src/fastify.js:10:1:21:2 | fastify ... > {}\\n}) | src/fastify.js:13:28:13:55 | (reques ... ) => {} | +| src/fastify.js:10:1:21:2 | fastify ... > {}\\n}) | src/fastify.js:14:29:14:56 | (reques ... ) => {} | +| src/fastify.js:10:1:21:2 | fastify ... > {}\\n}) | src/fastify.js:15:32:15:59 | (reques ... ) => {} | +| src/fastify.js:10:1:21:2 | fastify ... > {}\\n}) | src/fastify.js:16:29:16:56 | (reques ... ) => {} | +| src/fastify.js:10:1:21:2 | fastify ... > {}\\n}) | src/fastify.js:17:35:17:71 | (reques ... ) => {} | +| src/fastify.js:10:1:21:2 | fastify ... > {}\\n}) | src/fastify.js:18:25:18:61 | (reques ... ) => {} | +| src/fastify.js:10:1:21:2 | fastify ... > {}\\n}) | src/fastify.js:19:29:19:56 | (reques ... ) => {} | +| src/fastify.js:10:1:21:2 | fastify ... > {}\\n}) | src/fastify.js:20:26:20:47 | (reques ... ) => {} | +| src/fastify.js:23:1:29:1 | fastify ... e\\n }\\n) | src/fastify.js:26:17:28:3 | (reques ... nse\\n } | +| src/fastify.js:31:1:47:1 | fastify ... m\\n }\\n) | src/fastify.js:34:17:46:3 | functio ... eam\\n } | +| src/fastify.js:52:1:59:1 | fastify ... ;\\n }\\n) | src/fastify.js:54:17:58:3 | functio ... ms;\\n } | +| src/fastify.js:63:1:70:1 | fastify ... ;\\n }\\n) | src/fastify.js:65:17:69:3 | functio ... ms;\\n } | +| src/fastify.js:74:1:81:1 | fastify ... ;\\n }\\n) | src/fastify.js:76:17:80:3 | functio ... ms;\\n } | +| src/fastify.js:85:1:92:1 | fastify ... ;\\n }\\n) | src/fastify.js:87:17:91:3 | functio ... ms;\\n } | +test_RouteHandler +| src/fastify.js:5:17:7:3 | async ( ... nse\\n } | src/fastify.js:1:15:1:34 | require("fastify")() | +| src/fastify.js:13:28:13:55 | (reques ... ) => {} | src/fastify.js:1:15:1:34 | require("fastify")() | +| src/fastify.js:14:29:14:56 | (reques ... ) => {} | src/fastify.js:1:15:1:34 | require("fastify")() | +| src/fastify.js:15:32:15:59 | (reques ... ) => {} | src/fastify.js:1:15:1:34 | require("fastify")() | +| src/fastify.js:16:29:16:56 | (reques ... ) => {} | src/fastify.js:1:15:1:34 | require("fastify")() | +| src/fastify.js:17:35:17:71 | (reques ... ) => {} | src/fastify.js:1:15:1:34 | require("fastify")() | +| src/fastify.js:18:25:18:61 | (reques ... ) => {} | src/fastify.js:1:15:1:34 | require("fastify")() | +| src/fastify.js:19:29:19:56 | (reques ... ) => {} | src/fastify.js:1:15:1:34 | require("fastify")() | +| src/fastify.js:20:26:20:47 | (reques ... ) => {} | src/fastify.js:1:15:1:34 | require("fastify")() | +| src/fastify.js:26:17:28:3 | (reques ... nse\\n } | src/fastify.js:1:15:1:34 | require("fastify")() | +| src/fastify.js:34:17:46:3 | functio ... eam\\n } | src/fastify.js:1:15:1:34 | require("fastify")() | +| src/fastify.js:54:17:58:3 | functio ... ms;\\n } | src/fastify.js:50:27:50:46 | require("fastify")() | +| src/fastify.js:65:17:69:3 | functio ... ms;\\n } | src/fastify.js:61:27:61:46 | require("fastify")() | +| src/fastify.js:76:17:80:3 | functio ... ms;\\n } | src/fastify.js:72:27:72:46 | require("fastify")() | +| src/fastify.js:87:17:91:3 | functio ... ms;\\n } | src/fastify.js:83:27:83:46 | require("fastify")() | +test_RouteHandler_getARequestExpr +| src/fastify.js:5:17:7:3 | async ( ... nse\\n } | src/fastify.js:5:24:5:30 | request | +| src/fastify.js:13:28:13:55 | (reques ... ) => {} | src/fastify.js:13:29:13:35 | request | +| src/fastify.js:14:29:14:56 | (reques ... ) => {} | src/fastify.js:14:30:14:36 | request | +| src/fastify.js:15:32:15:59 | (reques ... ) => {} | src/fastify.js:15:33:15:39 | request | +| src/fastify.js:16:29:16:56 | (reques ... ) => {} | src/fastify.js:16:30:16:36 | request | +| src/fastify.js:17:35:17:71 | (reques ... ) => {} | src/fastify.js:17:36:17:42 | request | +| src/fastify.js:18:25:18:61 | (reques ... ) => {} | src/fastify.js:18:26:18:32 | request | +| src/fastify.js:19:29:19:56 | (reques ... ) => {} | src/fastify.js:19:30:19:36 | request | +| src/fastify.js:20:26:20:47 | (reques ... ) => {} | src/fastify.js:20:27:20:33 | request | +| src/fastify.js:26:17:28:3 | (reques ... nse\\n } | src/fastify.js:26:18:26:24 | request | +| src/fastify.js:34:17:46:3 | functio ... eam\\n } | src/fastify.js:34:26:34:32 | request | +| src/fastify.js:34:17:46:3 | functio ... eam\\n } | src/fastify.js:36:5:36:11 | request | +| src/fastify.js:34:17:46:3 | functio ... eam\\n } | src/fastify.js:37:5:37:11 | request | +| src/fastify.js:34:17:46:3 | functio ... eam\\n } | src/fastify.js:38:5:38:11 | request | +| src/fastify.js:34:17:46:3 | functio ... eam\\n } | src/fastify.js:39:5:39:11 | request | +| src/fastify.js:54:17:58:3 | functio ... ms;\\n } | src/fastify.js:54:26:54:32 | request | +| src/fastify.js:54:17:58:3 | functio ... ms;\\n } | src/fastify.js:55:5:55:11 | request | +| src/fastify.js:54:17:58:3 | functio ... ms;\\n } | src/fastify.js:56:5:56:11 | request | +| src/fastify.js:54:17:58:3 | functio ... ms;\\n } | src/fastify.js:57:5:57:11 | request | +| src/fastify.js:65:17:69:3 | functio ... ms;\\n } | src/fastify.js:65:26:65:32 | request | +| src/fastify.js:65:17:69:3 | functio ... ms;\\n } | src/fastify.js:66:5:66:11 | request | +| src/fastify.js:65:17:69:3 | functio ... ms;\\n } | src/fastify.js:67:5:67:11 | request | +| src/fastify.js:65:17:69:3 | functio ... ms;\\n } | src/fastify.js:68:5:68:11 | request | +| src/fastify.js:76:17:80:3 | functio ... ms;\\n } | src/fastify.js:76:26:76:32 | request | +| src/fastify.js:76:17:80:3 | functio ... ms;\\n } | src/fastify.js:77:5:77:11 | request | +| src/fastify.js:76:17:80:3 | functio ... ms;\\n } | src/fastify.js:78:5:78:11 | request | +| src/fastify.js:76:17:80:3 | functio ... ms;\\n } | src/fastify.js:79:5:79:11 | request | +| src/fastify.js:87:17:91:3 | functio ... ms;\\n } | src/fastify.js:87:26:87:32 | request | +| src/fastify.js:87:17:91:3 | functio ... ms;\\n } | src/fastify.js:88:5:88:11 | request | +| src/fastify.js:87:17:91:3 | functio ... ms;\\n } | src/fastify.js:89:5:89:11 | request | +| src/fastify.js:87:17:91:3 | functio ... ms;\\n } | src/fastify.js:90:5:90:11 | request | +test_ResponseSendArgument +| src/fastify.js:6:12:6:29 | { hello: "world" } | src/fastify.js:5:17:7:3 | async ( ... nse\\n } | +| src/fastify.js:27:16:27:33 | { hello: "world" } | src/fastify.js:26:17:28:3 | (reques ... nse\\n } | +| src/fastify.js:45:16:45:22 | payload | src/fastify.js:34:17:46:3 | functio ... eam\\n } | +test_RedirectInvocation +| src/fastify.js:44:5:44:29 | reply.r ... e, url) | src/fastify.js:34:17:46:3 | functio ... eam\\n } | diff --git a/javascript/ql/test/library-tests/frameworks/fastify/tests.ql b/javascript/ql/test/library-tests/frameworks/fastify/tests.ql new file mode 100644 index 00000000000..8bf2f61f5dd --- /dev/null +++ b/javascript/ql/test/library-tests/frameworks/fastify/tests.ql @@ -0,0 +1,14 @@ +import RouteSetup +import RequestInputAccess +import RouteHandler_getAResponseHeader +import HeaderDefinition_defines +import HeaderDefinition +import RouteSetup_getServer +import HeaderDefinition_getAHeaderName +import ServerDefinition +import HeaderAccess +import RouteSetup_getARouteHandler +import RouteHandler +import RouteHandler_getARequestExpr +import ResponseSendArgument +import RedirectInvocation diff --git a/javascript/ql/test/query-tests/Security/CWE-079/Xss.expected b/javascript/ql/test/query-tests/Security/CWE-079/Xss.expected index 9235b724e9d..64ce3616a2c 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/Xss.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/Xss.expected @@ -36,6 +36,47 @@ nodes | nodemailer.js:13:11:13:69 | `Hi, yo ... sage}.` | | nodemailer.js:13:50:13:66 | req.query.message | | nodemailer.js:13:50:13:66 | req.query.message | +| optionalSanitizer.js:2:7:2:39 | target | +| optionalSanitizer.js:2:16:2:32 | document.location | +| optionalSanitizer.js:2:16:2:32 | document.location | +| optionalSanitizer.js:2:16:2:39 | documen ... .search | +| optionalSanitizer.js:6:18:6:23 | target | +| optionalSanitizer.js:6:18:6:23 | target | +| optionalSanitizer.js:8:7:8:22 | tainted | +| optionalSanitizer.js:8:17:8:22 | target | +| optionalSanitizer.js:9:18:9:24 | tainted | +| optionalSanitizer.js:9:18:9:24 | tainted | +| optionalSanitizer.js:15:9:15:14 | target | +| optionalSanitizer.js:16:18:16:18 | x | +| optionalSanitizer.js:17:20:17:20 | x | +| optionalSanitizer.js:17:20:17:20 | x | +| optionalSanitizer.js:26:7:26:39 | target | +| optionalSanitizer.js:26:16:26:32 | document.location | +| optionalSanitizer.js:26:16:26:32 | document.location | +| optionalSanitizer.js:26:16:26:39 | documen ... .search | +| optionalSanitizer.js:31:7:31:23 | tainted2 | +| optionalSanitizer.js:31:18:31:23 | target | +| optionalSanitizer.js:32:18:32:25 | tainted2 | +| optionalSanitizer.js:32:18:32:25 | tainted2 | +| optionalSanitizer.js:34:5:34:36 | tainted2 | +| optionalSanitizer.js:34:16:34:36 | sanitiz ... inted2) | +| optionalSanitizer.js:34:28:34:35 | tainted2 | +| optionalSanitizer.js:36:18:36:25 | tainted2 | +| optionalSanitizer.js:36:18:36:25 | tainted2 | +| optionalSanitizer.js:38:7:38:23 | tainted3 | +| optionalSanitizer.js:38:18:38:23 | target | +| optionalSanitizer.js:39:18:39:25 | tainted3 | +| optionalSanitizer.js:39:18:39:25 | tainted3 | +| optionalSanitizer.js:41:5:41:36 | tainted3 | +| optionalSanitizer.js:41:16:41:36 | sanitiz ... inted3) | +| optionalSanitizer.js:41:28:41:35 | tainted3 | +| optionalSanitizer.js:43:18:43:25 | tainted3 | +| optionalSanitizer.js:43:18:43:25 | tainted3 | +| optionalSanitizer.js:45:18:45:56 | sanitiz ... target | +| optionalSanitizer.js:45:18:45:56 | sanitiz ... target | +| optionalSanitizer.js:45:29:45:47 | sanitizeBad(target) | +| optionalSanitizer.js:45:41:45:46 | target | +| optionalSanitizer.js:45:51:45:56 | target | | react-native.js:7:7:7:33 | tainted | | react-native.js:7:17:7:33 | req.param("code") | | react-native.js:7:17:7:33 | req.param("code") | @@ -53,6 +94,11 @@ nodes | stored-xss.js:5:20:5:52 | session ... ssion') | | stored-xss.js:8:20:8:48 | localSt ... local') | | stored-xss.js:8:20:8:48 | localSt ... local') | +| stored-xss.js:10:9:10:44 | href | +| stored-xss.js:10:16:10:44 | localSt ... local') | +| stored-xss.js:12:20:12:54 | "" | +| stored-xss.js:12:20:12:54 | "" | +| stored-xss.js:12:35:12:38 | href | | string-manipulations.js:3:16:3:32 | document.location | | string-manipulations.js:3:16:3:32 | document.location | | string-manipulations.js:3:16:3:32 | document.location | @@ -417,6 +463,51 @@ edges | nodemailer.js:13:50:13:66 | req.query.message | nodemailer.js:13:11:13:69 | `Hi, yo ... sage}.` | | nodemailer.js:13:50:13:66 | req.query.message | nodemailer.js:13:11:13:69 | `Hi, yo ... sage}.` | | nodemailer.js:13:50:13:66 | req.query.message | nodemailer.js:13:11:13:69 | `Hi, yo ... sage}.` | +| optionalSanitizer.js:2:7:2:39 | target | optionalSanitizer.js:6:18:6:23 | target | +| optionalSanitizer.js:2:7:2:39 | target | optionalSanitizer.js:6:18:6:23 | target | +| optionalSanitizer.js:2:7:2:39 | target | optionalSanitizer.js:8:17:8:22 | target | +| optionalSanitizer.js:2:7:2:39 | target | optionalSanitizer.js:15:9:15:14 | target | +| optionalSanitizer.js:2:16:2:32 | document.location | optionalSanitizer.js:2:16:2:39 | documen ... .search | +| optionalSanitizer.js:2:16:2:32 | document.location | optionalSanitizer.js:2:16:2:39 | documen ... .search | +| optionalSanitizer.js:2:16:2:39 | documen ... .search | optionalSanitizer.js:2:7:2:39 | target | +| optionalSanitizer.js:8:7:8:22 | tainted | optionalSanitizer.js:9:18:9:24 | tainted | +| optionalSanitizer.js:8:7:8:22 | tainted | optionalSanitizer.js:9:18:9:24 | tainted | +| optionalSanitizer.js:8:17:8:22 | target | optionalSanitizer.js:8:7:8:22 | tainted | +| optionalSanitizer.js:15:9:15:14 | target | optionalSanitizer.js:16:18:16:18 | x | +| optionalSanitizer.js:16:18:16:18 | x | optionalSanitizer.js:17:20:17:20 | x | +| optionalSanitizer.js:16:18:16:18 | x | optionalSanitizer.js:17:20:17:20 | x | +| optionalSanitizer.js:26:7:26:39 | target | optionalSanitizer.js:31:18:31:23 | target | +| optionalSanitizer.js:26:7:26:39 | target | optionalSanitizer.js:38:18:38:23 | target | +| optionalSanitizer.js:26:7:26:39 | target | optionalSanitizer.js:45:41:45:46 | target | +| optionalSanitizer.js:26:7:26:39 | target | optionalSanitizer.js:45:51:45:56 | target | +| optionalSanitizer.js:26:16:26:32 | document.location | optionalSanitizer.js:26:16:26:39 | documen ... .search | +| optionalSanitizer.js:26:16:26:32 | document.location | optionalSanitizer.js:26:16:26:39 | documen ... .search | +| optionalSanitizer.js:26:16:26:39 | documen ... .search | optionalSanitizer.js:26:7:26:39 | target | +| optionalSanitizer.js:31:7:31:23 | tainted2 | optionalSanitizer.js:32:18:32:25 | tainted2 | +| optionalSanitizer.js:31:7:31:23 | tainted2 | optionalSanitizer.js:32:18:32:25 | tainted2 | +| optionalSanitizer.js:31:7:31:23 | tainted2 | optionalSanitizer.js:34:28:34:35 | tainted2 | +| optionalSanitizer.js:31:7:31:23 | tainted2 | optionalSanitizer.js:36:18:36:25 | tainted2 | +| optionalSanitizer.js:31:7:31:23 | tainted2 | optionalSanitizer.js:36:18:36:25 | tainted2 | +| optionalSanitizer.js:31:18:31:23 | target | optionalSanitizer.js:31:7:31:23 | tainted2 | +| optionalSanitizer.js:34:5:34:36 | tainted2 | optionalSanitizer.js:36:18:36:25 | tainted2 | +| optionalSanitizer.js:34:5:34:36 | tainted2 | optionalSanitizer.js:36:18:36:25 | tainted2 | +| optionalSanitizer.js:34:16:34:36 | sanitiz ... inted2) | optionalSanitizer.js:34:5:34:36 | tainted2 | +| optionalSanitizer.js:34:28:34:35 | tainted2 | optionalSanitizer.js:34:16:34:36 | sanitiz ... inted2) | +| optionalSanitizer.js:38:7:38:23 | tainted3 | optionalSanitizer.js:39:18:39:25 | tainted3 | +| optionalSanitizer.js:38:7:38:23 | tainted3 | optionalSanitizer.js:39:18:39:25 | tainted3 | +| optionalSanitizer.js:38:7:38:23 | tainted3 | optionalSanitizer.js:41:28:41:35 | tainted3 | +| optionalSanitizer.js:38:7:38:23 | tainted3 | optionalSanitizer.js:43:18:43:25 | tainted3 | +| optionalSanitizer.js:38:7:38:23 | tainted3 | optionalSanitizer.js:43:18:43:25 | tainted3 | +| optionalSanitizer.js:38:18:38:23 | target | optionalSanitizer.js:38:7:38:23 | tainted3 | +| optionalSanitizer.js:41:5:41:36 | tainted3 | optionalSanitizer.js:43:18:43:25 | tainted3 | +| optionalSanitizer.js:41:5:41:36 | tainted3 | optionalSanitizer.js:43:18:43:25 | tainted3 | +| optionalSanitizer.js:41:16:41:36 | sanitiz ... inted3) | optionalSanitizer.js:41:5:41:36 | tainted3 | +| optionalSanitizer.js:41:28:41:35 | tainted3 | optionalSanitizer.js:41:16:41:36 | sanitiz ... inted3) | +| optionalSanitizer.js:45:29:45:47 | sanitizeBad(target) | optionalSanitizer.js:45:18:45:56 | sanitiz ... target | +| optionalSanitizer.js:45:29:45:47 | sanitizeBad(target) | optionalSanitizer.js:45:18:45:56 | sanitiz ... target | +| optionalSanitizer.js:45:41:45:46 | target | optionalSanitizer.js:45:29:45:47 | sanitizeBad(target) | +| optionalSanitizer.js:45:51:45:56 | target | optionalSanitizer.js:45:18:45:56 | sanitiz ... target | +| optionalSanitizer.js:45:51:45:56 | target | optionalSanitizer.js:45:18:45:56 | sanitiz ... target | | react-native.js:7:7:7:33 | tainted | react-native.js:8:18:8:24 | tainted | | react-native.js:7:7:7:33 | tainted | react-native.js:8:18:8:24 | tainted | | react-native.js:7:7:7:33 | tainted | react-native.js:9:27:9:33 | tainted | @@ -431,6 +522,11 @@ edges | stored-xss.js:3:35:3:51 | document.location | stored-xss.js:3:35:3:58 | documen ... .search | | stored-xss.js:3:35:3:58 | documen ... .search | stored-xss.js:8:20:8:48 | localSt ... local') | | stored-xss.js:3:35:3:58 | documen ... .search | stored-xss.js:8:20:8:48 | localSt ... local') | +| stored-xss.js:3:35:3:58 | documen ... .search | stored-xss.js:10:16:10:44 | localSt ... local') | +| stored-xss.js:10:9:10:44 | href | stored-xss.js:12:35:12:38 | href | +| stored-xss.js:10:16:10:44 | localSt ... local') | stored-xss.js:10:9:10:44 | href | +| stored-xss.js:12:35:12:38 | href | stored-xss.js:12:20:12:54 | "" | +| stored-xss.js:12:35:12:38 | href | stored-xss.js:12:20:12:54 | "" | | string-manipulations.js:3:16:3:32 | document.location | string-manipulations.js:3:16:3:32 | document.location | | string-manipulations.js:4:16:4:32 | document.location | string-manipulations.js:4:16:4:37 | documen ... on.href | | string-manipulations.js:4:16:4:32 | document.location | string-manipulations.js:4:16:4:37 | documen ... on.href | @@ -728,10 +824,19 @@ edges | jquery.js:7:5:7:34 | "
    " | jquery.js:2:17:2:33 | document.location | jquery.js:7:5:7:34 | "
    " | Cross-site scripting vulnerability due to $@. | jquery.js:2:17:2:33 | document.location | user-provided value | | jquery.js:8:18:8:34 | "XSS: " + tainted | jquery.js:2:17:2:33 | document.location | jquery.js:8:18:8:34 | "XSS: " + tainted | Cross-site scripting vulnerability due to $@. | jquery.js:2:17:2:33 | document.location | user-provided value | | nodemailer.js:13:11:13:69 | `Hi, yo ... sage}.` | nodemailer.js:13:50:13:66 | req.query.message | nodemailer.js:13:11:13:69 | `Hi, yo ... sage}.` | HTML injection vulnerability due to $@. | nodemailer.js:13:50:13:66 | req.query.message | user-provided value | +| optionalSanitizer.js:6:18:6:23 | target | optionalSanitizer.js:2:16:2:32 | document.location | optionalSanitizer.js:6:18:6:23 | target | Cross-site scripting vulnerability due to $@. | optionalSanitizer.js:2:16:2:32 | document.location | user-provided value | +| optionalSanitizer.js:9:18:9:24 | tainted | optionalSanitizer.js:2:16:2:32 | document.location | optionalSanitizer.js:9:18:9:24 | tainted | Cross-site scripting vulnerability due to $@. | optionalSanitizer.js:2:16:2:32 | document.location | user-provided value | +| optionalSanitizer.js:17:20:17:20 | x | optionalSanitizer.js:2:16:2:32 | document.location | optionalSanitizer.js:17:20:17:20 | x | Cross-site scripting vulnerability due to $@. | optionalSanitizer.js:2:16:2:32 | document.location | user-provided value | +| optionalSanitizer.js:32:18:32:25 | tainted2 | optionalSanitizer.js:26:16:26:32 | document.location | optionalSanitizer.js:32:18:32:25 | tainted2 | Cross-site scripting vulnerability due to $@. | optionalSanitizer.js:26:16:26:32 | document.location | user-provided value | +| optionalSanitizer.js:36:18:36:25 | tainted2 | optionalSanitizer.js:26:16:26:32 | document.location | optionalSanitizer.js:36:18:36:25 | tainted2 | Cross-site scripting vulnerability due to $@. | optionalSanitizer.js:26:16:26:32 | document.location | user-provided value | +| optionalSanitizer.js:39:18:39:25 | tainted3 | optionalSanitizer.js:26:16:26:32 | document.location | optionalSanitizer.js:39:18:39:25 | tainted3 | Cross-site scripting vulnerability due to $@. | optionalSanitizer.js:26:16:26:32 | document.location | user-provided value | +| optionalSanitizer.js:43:18:43:25 | tainted3 | optionalSanitizer.js:26:16:26:32 | document.location | optionalSanitizer.js:43:18:43:25 | tainted3 | Cross-site scripting vulnerability due to $@. | optionalSanitizer.js:26:16:26:32 | document.location | user-provided value | +| optionalSanitizer.js:45:18:45:56 | sanitiz ... target | optionalSanitizer.js:26:16:26:32 | document.location | optionalSanitizer.js:45:18:45:56 | sanitiz ... target | Cross-site scripting vulnerability due to $@. | optionalSanitizer.js:26:16:26:32 | document.location | user-provided value | | react-native.js:8:18:8:24 | tainted | react-native.js:7:17:7:33 | req.param("code") | react-native.js:8:18:8:24 | tainted | Cross-site scripting vulnerability due to $@. | react-native.js:7:17:7:33 | req.param("code") | user-provided value | | react-native.js:9:27:9:33 | tainted | react-native.js:7:17:7:33 | req.param("code") | react-native.js:9:27:9:33 | tainted | Cross-site scripting vulnerability due to $@. | react-native.js:7:17:7:33 | req.param("code") | user-provided value | | stored-xss.js:5:20:5:52 | session ... ssion') | stored-xss.js:2:39:2:55 | document.location | stored-xss.js:5:20:5:52 | session ... ssion') | Cross-site scripting vulnerability due to $@. | stored-xss.js:2:39:2:55 | document.location | user-provided value | | stored-xss.js:8:20:8:48 | localSt ... local') | stored-xss.js:3:35:3:51 | document.location | stored-xss.js:8:20:8:48 | localSt ... local') | Cross-site scripting vulnerability due to $@. | stored-xss.js:3:35:3:51 | document.location | user-provided value | +| stored-xss.js:12:20:12:54 | "" | stored-xss.js:3:35:3:51 | document.location | stored-xss.js:12:20:12:54 | "" | Cross-site scripting vulnerability due to $@. | stored-xss.js:3:35:3:51 | document.location | user-provided value | | string-manipulations.js:3:16:3:32 | document.location | string-manipulations.js:3:16:3:32 | document.location | string-manipulations.js:3:16:3:32 | document.location | Cross-site scripting vulnerability due to $@. | string-manipulations.js:3:16:3:32 | document.location | user-provided value | | string-manipulations.js:4:16:4:37 | documen ... on.href | string-manipulations.js:4:16:4:32 | document.location | string-manipulations.js:4:16:4:37 | documen ... on.href | Cross-site scripting vulnerability due to $@. | string-manipulations.js:4:16:4:32 | document.location | user-provided value | | string-manipulations.js:5:16:5:47 | documen ... lueOf() | string-manipulations.js:5:16:5:32 | document.location | string-manipulations.js:5:16:5:47 | documen ... lueOf() | Cross-site scripting vulnerability due to $@. | string-manipulations.js:5:16:5:32 | document.location | user-provided value | diff --git a/javascript/ql/test/query-tests/Security/CWE-079/XssWithAdditionalSources.expected b/javascript/ql/test/query-tests/Security/CWE-079/XssWithAdditionalSources.expected index a73dae129f2..3deaa30c3aa 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/XssWithAdditionalSources.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/XssWithAdditionalSources.expected @@ -36,6 +36,47 @@ nodes | nodemailer.js:13:11:13:69 | `Hi, yo ... sage}.` | | nodemailer.js:13:50:13:66 | req.query.message | | nodemailer.js:13:50:13:66 | req.query.message | +| optionalSanitizer.js:2:7:2:39 | target | +| optionalSanitizer.js:2:16:2:32 | document.location | +| optionalSanitizer.js:2:16:2:32 | document.location | +| optionalSanitizer.js:2:16:2:39 | documen ... .search | +| optionalSanitizer.js:6:18:6:23 | target | +| optionalSanitizer.js:6:18:6:23 | target | +| optionalSanitizer.js:8:7:8:22 | tainted | +| optionalSanitizer.js:8:17:8:22 | target | +| optionalSanitizer.js:9:18:9:24 | tainted | +| optionalSanitizer.js:9:18:9:24 | tainted | +| optionalSanitizer.js:15:9:15:14 | target | +| optionalSanitizer.js:16:18:16:18 | x | +| optionalSanitizer.js:17:20:17:20 | x | +| optionalSanitizer.js:17:20:17:20 | x | +| optionalSanitizer.js:26:7:26:39 | target | +| optionalSanitizer.js:26:16:26:32 | document.location | +| optionalSanitizer.js:26:16:26:32 | document.location | +| optionalSanitizer.js:26:16:26:39 | documen ... .search | +| optionalSanitizer.js:31:7:31:23 | tainted2 | +| optionalSanitizer.js:31:18:31:23 | target | +| optionalSanitizer.js:32:18:32:25 | tainted2 | +| optionalSanitizer.js:32:18:32:25 | tainted2 | +| optionalSanitizer.js:34:5:34:36 | tainted2 | +| optionalSanitizer.js:34:16:34:36 | sanitiz ... inted2) | +| optionalSanitizer.js:34:28:34:35 | tainted2 | +| optionalSanitizer.js:36:18:36:25 | tainted2 | +| optionalSanitizer.js:36:18:36:25 | tainted2 | +| optionalSanitizer.js:38:7:38:23 | tainted3 | +| optionalSanitizer.js:38:18:38:23 | target | +| optionalSanitizer.js:39:18:39:25 | tainted3 | +| optionalSanitizer.js:39:18:39:25 | tainted3 | +| optionalSanitizer.js:41:5:41:36 | tainted3 | +| optionalSanitizer.js:41:16:41:36 | sanitiz ... inted3) | +| optionalSanitizer.js:41:28:41:35 | tainted3 | +| optionalSanitizer.js:43:18:43:25 | tainted3 | +| optionalSanitizer.js:43:18:43:25 | tainted3 | +| optionalSanitizer.js:45:18:45:56 | sanitiz ... target | +| optionalSanitizer.js:45:18:45:56 | sanitiz ... target | +| optionalSanitizer.js:45:29:45:47 | sanitizeBad(target) | +| optionalSanitizer.js:45:41:45:46 | target | +| optionalSanitizer.js:45:51:45:56 | target | | react-native.js:7:7:7:33 | tainted | | react-native.js:7:17:7:33 | req.param("code") | | react-native.js:7:17:7:33 | req.param("code") | @@ -53,6 +94,11 @@ nodes | stored-xss.js:5:20:5:52 | session ... ssion') | | stored-xss.js:8:20:8:48 | localSt ... local') | | stored-xss.js:8:20:8:48 | localSt ... local') | +| stored-xss.js:10:9:10:44 | href | +| stored-xss.js:10:16:10:44 | localSt ... local') | +| stored-xss.js:12:20:12:54 | "" | +| stored-xss.js:12:20:12:54 | "" | +| stored-xss.js:12:35:12:38 | href | | string-manipulations.js:3:16:3:32 | document.location | | string-manipulations.js:3:16:3:32 | document.location | | string-manipulations.js:3:16:3:32 | document.location | @@ -421,6 +467,51 @@ edges | nodemailer.js:13:50:13:66 | req.query.message | nodemailer.js:13:11:13:69 | `Hi, yo ... sage}.` | | nodemailer.js:13:50:13:66 | req.query.message | nodemailer.js:13:11:13:69 | `Hi, yo ... sage}.` | | nodemailer.js:13:50:13:66 | req.query.message | nodemailer.js:13:11:13:69 | `Hi, yo ... sage}.` | +| optionalSanitizer.js:2:7:2:39 | target | optionalSanitizer.js:6:18:6:23 | target | +| optionalSanitizer.js:2:7:2:39 | target | optionalSanitizer.js:6:18:6:23 | target | +| optionalSanitizer.js:2:7:2:39 | target | optionalSanitizer.js:8:17:8:22 | target | +| optionalSanitizer.js:2:7:2:39 | target | optionalSanitizer.js:15:9:15:14 | target | +| optionalSanitizer.js:2:16:2:32 | document.location | optionalSanitizer.js:2:16:2:39 | documen ... .search | +| optionalSanitizer.js:2:16:2:32 | document.location | optionalSanitizer.js:2:16:2:39 | documen ... .search | +| optionalSanitizer.js:2:16:2:39 | documen ... .search | optionalSanitizer.js:2:7:2:39 | target | +| optionalSanitizer.js:8:7:8:22 | tainted | optionalSanitizer.js:9:18:9:24 | tainted | +| optionalSanitizer.js:8:7:8:22 | tainted | optionalSanitizer.js:9:18:9:24 | tainted | +| optionalSanitizer.js:8:17:8:22 | target | optionalSanitizer.js:8:7:8:22 | tainted | +| optionalSanitizer.js:15:9:15:14 | target | optionalSanitizer.js:16:18:16:18 | x | +| optionalSanitizer.js:16:18:16:18 | x | optionalSanitizer.js:17:20:17:20 | x | +| optionalSanitizer.js:16:18:16:18 | x | optionalSanitizer.js:17:20:17:20 | x | +| optionalSanitizer.js:26:7:26:39 | target | optionalSanitizer.js:31:18:31:23 | target | +| optionalSanitizer.js:26:7:26:39 | target | optionalSanitizer.js:38:18:38:23 | target | +| optionalSanitizer.js:26:7:26:39 | target | optionalSanitizer.js:45:41:45:46 | target | +| optionalSanitizer.js:26:7:26:39 | target | optionalSanitizer.js:45:51:45:56 | target | +| optionalSanitizer.js:26:16:26:32 | document.location | optionalSanitizer.js:26:16:26:39 | documen ... .search | +| optionalSanitizer.js:26:16:26:32 | document.location | optionalSanitizer.js:26:16:26:39 | documen ... .search | +| optionalSanitizer.js:26:16:26:39 | documen ... .search | optionalSanitizer.js:26:7:26:39 | target | +| optionalSanitizer.js:31:7:31:23 | tainted2 | optionalSanitizer.js:32:18:32:25 | tainted2 | +| optionalSanitizer.js:31:7:31:23 | tainted2 | optionalSanitizer.js:32:18:32:25 | tainted2 | +| optionalSanitizer.js:31:7:31:23 | tainted2 | optionalSanitizer.js:34:28:34:35 | tainted2 | +| optionalSanitizer.js:31:7:31:23 | tainted2 | optionalSanitizer.js:36:18:36:25 | tainted2 | +| optionalSanitizer.js:31:7:31:23 | tainted2 | optionalSanitizer.js:36:18:36:25 | tainted2 | +| optionalSanitizer.js:31:18:31:23 | target | optionalSanitizer.js:31:7:31:23 | tainted2 | +| optionalSanitizer.js:34:5:34:36 | tainted2 | optionalSanitizer.js:36:18:36:25 | tainted2 | +| optionalSanitizer.js:34:5:34:36 | tainted2 | optionalSanitizer.js:36:18:36:25 | tainted2 | +| optionalSanitizer.js:34:16:34:36 | sanitiz ... inted2) | optionalSanitizer.js:34:5:34:36 | tainted2 | +| optionalSanitizer.js:34:28:34:35 | tainted2 | optionalSanitizer.js:34:16:34:36 | sanitiz ... inted2) | +| optionalSanitizer.js:38:7:38:23 | tainted3 | optionalSanitizer.js:39:18:39:25 | tainted3 | +| optionalSanitizer.js:38:7:38:23 | tainted3 | optionalSanitizer.js:39:18:39:25 | tainted3 | +| optionalSanitizer.js:38:7:38:23 | tainted3 | optionalSanitizer.js:41:28:41:35 | tainted3 | +| optionalSanitizer.js:38:7:38:23 | tainted3 | optionalSanitizer.js:43:18:43:25 | tainted3 | +| optionalSanitizer.js:38:7:38:23 | tainted3 | optionalSanitizer.js:43:18:43:25 | tainted3 | +| optionalSanitizer.js:38:18:38:23 | target | optionalSanitizer.js:38:7:38:23 | tainted3 | +| optionalSanitizer.js:41:5:41:36 | tainted3 | optionalSanitizer.js:43:18:43:25 | tainted3 | +| optionalSanitizer.js:41:5:41:36 | tainted3 | optionalSanitizer.js:43:18:43:25 | tainted3 | +| optionalSanitizer.js:41:16:41:36 | sanitiz ... inted3) | optionalSanitizer.js:41:5:41:36 | tainted3 | +| optionalSanitizer.js:41:28:41:35 | tainted3 | optionalSanitizer.js:41:16:41:36 | sanitiz ... inted3) | +| optionalSanitizer.js:45:29:45:47 | sanitizeBad(target) | optionalSanitizer.js:45:18:45:56 | sanitiz ... target | +| optionalSanitizer.js:45:29:45:47 | sanitizeBad(target) | optionalSanitizer.js:45:18:45:56 | sanitiz ... target | +| optionalSanitizer.js:45:41:45:46 | target | optionalSanitizer.js:45:29:45:47 | sanitizeBad(target) | +| optionalSanitizer.js:45:51:45:56 | target | optionalSanitizer.js:45:18:45:56 | sanitiz ... target | +| optionalSanitizer.js:45:51:45:56 | target | optionalSanitizer.js:45:18:45:56 | sanitiz ... target | | react-native.js:7:7:7:33 | tainted | react-native.js:8:18:8:24 | tainted | | react-native.js:7:7:7:33 | tainted | react-native.js:8:18:8:24 | tainted | | react-native.js:7:7:7:33 | tainted | react-native.js:9:27:9:33 | tainted | @@ -435,6 +526,11 @@ edges | stored-xss.js:3:35:3:51 | document.location | stored-xss.js:3:35:3:58 | documen ... .search | | stored-xss.js:3:35:3:58 | documen ... .search | stored-xss.js:8:20:8:48 | localSt ... local') | | stored-xss.js:3:35:3:58 | documen ... .search | stored-xss.js:8:20:8:48 | localSt ... local') | +| stored-xss.js:3:35:3:58 | documen ... .search | stored-xss.js:10:16:10:44 | localSt ... local') | +| stored-xss.js:10:9:10:44 | href | stored-xss.js:12:35:12:38 | href | +| stored-xss.js:10:16:10:44 | localSt ... local') | stored-xss.js:10:9:10:44 | href | +| stored-xss.js:12:35:12:38 | href | stored-xss.js:12:20:12:54 | "" | +| stored-xss.js:12:35:12:38 | href | stored-xss.js:12:20:12:54 | "" | | string-manipulations.js:3:16:3:32 | document.location | string-manipulations.js:3:16:3:32 | document.location | | string-manipulations.js:4:16:4:32 | document.location | string-manipulations.js:4:16:4:37 | documen ... on.href | | string-manipulations.js:4:16:4:32 | document.location | string-manipulations.js:4:16:4:37 | documen ... on.href | diff --git a/javascript/ql/test/query-tests/Security/CWE-079/optionalSanitizer.js b/javascript/ql/test/query-tests/Security/CWE-079/optionalSanitizer.js new file mode 100644 index 00000000000..e8139936bc7 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-079/optionalSanitizer.js @@ -0,0 +1,46 @@ +function test() { + var target = document.location.search + + $('myId').html(sanitize ? DOMPurify.sanitize(target) : target); // OK + + $('myId').html(target); // NOT OK + + var tainted = target; + $('myId').html(tainted); // NOT OK + if (sanitize) { + tainted = DOMPurify.sanitize(tainted); + } + $('myId').html(tainted); // OK + + inner(target); + function inner(x) { + $('myId').html(x); // NOT OK + if (sanitize) { + x = DOMPurify.sanitize(x); + } + $('myId').html(x); // OK + } +} + +function badSanitizer() { + var target = document.location.search + + function sanitizeBad(x) { + return x; // No sanitization; + } + var tainted2 = target; + $('myId').html(tainted2); // NOT OK + if (sanitize) { + tainted2 = sanitizeBad(tainted2); + } + $('myId').html(tainted2); // NOT OK + + var tainted3 = target; + $('myId').html(tainted3); // NOT OK + if (sanitize) { + tainted3 = sanitizeBad(tainted3); + } + $('myId').html(tainted3); // NOT OK + + $('myId').html(sanitize ? sanitizeBad(target) : target); // NOT OK +} diff --git a/javascript/ql/test/query-tests/Security/CWE-079/stored-xss.js b/javascript/ql/test/query-tests/Security/CWE-079/stored-xss.js index 4a9cc51bce7..6c13ae8cc3e 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/stored-xss.js +++ b/javascript/ql/test/query-tests/Security/CWE-079/stored-xss.js @@ -6,4 +6,25 @@ $('myId').html(localStorage.getItem('session')); // OK $('myId').html(sessionStorage.getItem('local')); // OK $('myId').html(localStorage.getItem('local')); // NOT OK + + var href = localStorage.getItem('local'); + + $('myId').html("foobar"); // NOT OK + + if (href.indexOf("\"") !== -1) { + return; + } + $('myId').html(""); // OK + + var href2 = localStorage.getItem('local'); + if (href2.indexOf("\"") !== -1) { + return; + } + $('myId').html("\nfoobar"); // OK + + var href3 = localStorage.getItem('local'); + if (href3.indexOf("\"") !== -1) { + return; + } + $('myId').html('\r\n' + "something" + ''); // OK }); diff --git a/python/ql/examples/snippets/filename.ql b/python/ql/examples/snippets/filename.ql index 579cceea47a..d0a2d122603 100644 --- a/python/ql/examples/snippets/filename.ql +++ b/python/ql/examples/snippets/filename.ql @@ -8,5 +8,5 @@ import python from File f -where f.getName() = "spam.py" +where f.getShortName() = "spam.py" select f diff --git a/python/ql/src/Classes/DefineEqualsWhenAddingAttributes.ql b/python/ql/src/Classes/DefineEqualsWhenAddingAttributes.ql index 8bf7d24b729..a0fff36b344 100644 --- a/python/ql/src/Classes/DefineEqualsWhenAddingAttributes.ql +++ b/python/ql/src/Classes/DefineEqualsWhenAddingAttributes.ql @@ -14,21 +14,21 @@ import python import semmle.python.SelfAttribute import Equality -predicate class_stores_to_attribute(ClassObject cls, SelfAttributeStore store, string name) { - exists(FunctionObject f | - f = cls.declaredAttribute(_) and store.getScope() = f.getFunction() and store.getName() = name +predicate class_stores_to_attribute(ClassValue cls, SelfAttributeStore store, string name) { + exists(FunctionValue f | + f = cls.declaredAttribute(_) and store.getScope() = f.getScope() and store.getName() = name ) and /* Exclude classes used as metaclasses */ - not cls.getASuperType() = theTypeType() + not cls.getASuperType() = ClassValue::type() } -predicate should_override_eq(ClassObject cls, Object base_eq) { +predicate should_override_eq(ClassValue cls, Value base_eq) { not cls.declaresAttribute("__eq__") and - exists(ClassObject sup | sup = cls.getABaseType() and sup.declaredAttribute("__eq__") = base_eq | - not exists(GenericEqMethod eq | eq.getScope() = sup.getPyClass()) and - not exists(IdentityEqMethod eq | eq.getScope() = sup.getPyClass()) and - not base_eq.(FunctionObject).getFunction() instanceof IdentityEqMethod and - not base_eq = theObjectType().declaredAttribute("__eq__") + exists(ClassValue sup | sup = cls.getABaseType() and sup.declaredAttribute("__eq__") = base_eq | + not exists(GenericEqMethod eq | eq.getScope() = sup.getScope()) and + not exists(IdentityEqMethod eq | eq.getScope() = sup.getScope()) and + not base_eq.(FunctionValue).getScope() instanceof IdentityEqMethod and + not base_eq = ClassValue::object().declaredAttribute("__eq__") ) } @@ -36,16 +36,16 @@ predicate should_override_eq(ClassObject cls, Object base_eq) { * Does the non-overridden __eq__ method access the attribute, * which implies that the __eq__ method does not need to be overridden. */ -predicate superclassEqExpectsAttribute(ClassObject cls, PyFunctionObject base_eq, string attrname) { +predicate superclassEqExpectsAttribute(ClassValue cls, FunctionValue base_eq, string attrname) { not cls.declaresAttribute("__eq__") and - exists(ClassObject sup | sup = cls.getABaseType() and sup.declaredAttribute("__eq__") = base_eq | + exists(ClassValue sup | sup = cls.getABaseType() and sup.declaredAttribute("__eq__") = base_eq | exists(SelfAttributeRead store | store.getName() = attrname | - store.getScope() = base_eq.getFunction() + store.getScope() = base_eq.getScope() ) ) } -from ClassObject cls, SelfAttributeStore store, Object base_eq +from ClassValue cls, SelfAttributeStore store, Value base_eq where class_stores_to_attribute(cls, store, _) and should_override_eq(cls, base_eq) and diff --git a/python/ql/src/Security/CWE-022/TarSlip.ql b/python/ql/src/Security/CWE-022/TarSlip.ql index aedc47b92ec..5769bae409f 100644 --- a/python/ql/src/Security/CWE-022/TarSlip.ql +++ b/python/ql/src/Security/CWE-022/TarSlip.ql @@ -13,7 +13,7 @@ import python import semmle.python.security.Paths -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Basic /** A TaintKind to represent open tarfile objects. That is, the result of calling `tarfile.open(...)` */ diff --git a/python/ql/src/Security/CWE-312/CleartextLogging.ql b/python/ql/src/Security/CWE-312/CleartextLogging.ql index de71a56e3d3..d1c6ac94d4b 100644 --- a/python/ql/src/Security/CWE-312/CleartextLogging.ql +++ b/python/ql/src/Security/CWE-312/CleartextLogging.ql @@ -14,7 +14,7 @@ import python import semmle.python.security.Paths -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.security.SensitiveData import semmle.python.security.ClearText diff --git a/python/ql/src/Security/CWE-312/CleartextStorage.ql b/python/ql/src/Security/CWE-312/CleartextStorage.ql index 99dc8f6626c..f1f898b00dd 100644 --- a/python/ql/src/Security/CWE-312/CleartextStorage.ql +++ b/python/ql/src/Security/CWE-312/CleartextStorage.ql @@ -14,7 +14,7 @@ import python import semmle.python.security.Paths -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.security.SensitiveData import semmle.python.security.ClearText diff --git a/python/ql/src/Security/CWE-798/HardcodedCredentials.ql b/python/ql/src/Security/CWE-798/HardcodedCredentials.ql index 4f79a3382b7..edc03fb5f36 100644 --- a/python/ql/src/Security/CWE-798/HardcodedCredentials.ql +++ b/python/ql/src/Security/CWE-798/HardcodedCredentials.ql @@ -13,7 +13,7 @@ import python import semmle.python.security.Paths -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.filters.Tests class HardcodedValue extends TaintKind { diff --git a/python/ql/src/Variables/Undefined.qll b/python/ql/src/Variables/Undefined.qll index 78e6438ec69..2c757733af4 100644 --- a/python/ql/src/Variables/Undefined.qll +++ b/python/ql/src/Variables/Undefined.qll @@ -1,6 +1,6 @@ import python import Loop -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking /** Marker for "uninitialized". */ class Uninitialized extends TaintKind { diff --git a/python/ql/src/Variables/UndefinedExport.ql b/python/ql/src/Variables/UndefinedExport.ql index 4d81e0f3c8d..d536e63b856 100644 --- a/python/ql/src/Variables/UndefinedExport.ql +++ b/python/ql/src/Variables/UndefinedExport.ql @@ -59,7 +59,7 @@ predicate contains_unknown_import_star(ModuleValue m) { from ModuleValue m, StrConst name, string exported_name where declaredInAll(m.getScope(), name) and - exported_name = name.strValue() and + exported_name = name.getText() and not m.hasAttribute(exported_name) and not is_exported_submodule_name(m, exported_name) and not contains_unknown_import_star(m) and diff --git a/python/ql/src/semmle/python/Exprs.qll b/python/ql/src/semmle/python/Exprs.qll index 2b58fe38e4e..a137f4d4fb0 100644 --- a/python/ql/src/semmle/python/Exprs.qll +++ b/python/ql/src/semmle/python/Exprs.qll @@ -597,7 +597,7 @@ class StrConst extends Str_, ImmutableLiteral { this.getEnclosingModule().hasFromFuture("unicode_literals") } - override string strValue() { result = this.getS() } + deprecated override string strValue() { result = this.getS() } override Expr getASubExpression() { none() } diff --git a/python/ql/src/semmle/python/dataflow/Configuration.qll b/python/ql/src/semmle/python/dataflow/Configuration.qll index 851578999a8..91a9971c97d 100644 --- a/python/ql/src/semmle/python/dataflow/Configuration.qll +++ b/python/ql/src/semmle/python/dataflow/Configuration.qll @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking private import semmle.python.objects.ObjectInternal private import semmle.python.dataflow.Implementation diff --git a/python/ql/src/semmle/python/dataflow/DataFlow.qll b/python/ql/src/semmle/python/dataflow/DataFlow.qll index 4684441563c..42eda521bc8 100644 --- a/python/ql/src/semmle/python/dataflow/DataFlow.qll +++ b/python/ql/src/semmle/python/dataflow/DataFlow.qll @@ -1 +1 @@ -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking diff --git a/python/ql/src/semmle/python/dataflow/Files.qll b/python/ql/src/semmle/python/dataflow/Files.qll index 467a8bec29e..a0cd1753f9d 100644 --- a/python/ql/src/semmle/python/dataflow/Files.qll +++ b/python/ql/src/semmle/python/dataflow/Files.qll @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking class OpenFile extends TaintKind { OpenFile() { this = "file.open" } diff --git a/python/ql/src/semmle/python/dataflow/Legacy.qll b/python/ql/src/semmle/python/dataflow/Legacy.qll index 61961921514..ffdb7aee869 100644 --- a/python/ql/src/semmle/python/dataflow/Legacy.qll +++ b/python/ql/src/semmle/python/dataflow/Legacy.qll @@ -1,4 +1,4 @@ -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking private import semmle.python.objects.ObjectInternal import semmle.python.dataflow.Implementation diff --git a/python/ql/src/semmle/python/objects/Callables.qll b/python/ql/src/semmle/python/objects/Callables.qll index b915e4bd5c2..4021f04c510 100644 --- a/python/ql/src/semmle/python/objects/Callables.qll +++ b/python/ql/src/semmle/python/objects/Callables.qll @@ -27,8 +27,10 @@ abstract class CallableObjectInternal extends ObjectInternal { none() } + /** Gets the `n`th parameter node of this callable. */ abstract NameNode getParameter(int n); + /** Gets the `name`d parameter node of this callable. */ abstract NameNode getParameterByName(string name); abstract predicate neverReturns(); @@ -438,16 +440,30 @@ class BoundMethodObjectInternal extends CallableObjectInternal, TBoundMethod { PointsTo::pointsTo(result.getFunction(), ctx, this, _) } - override NameNode getParameter(int n) { result = this.getFunction().getParameter(n + 1) } + /** Gets the parameter node that will be used for `self`. */ + NameNode getSelfParameter() { result = this.getFunction().getParameter(0) } + override NameNode getParameter(int n) { + result = this.getFunction().getParameter(n + 1) and + // don't return the parameter for `self` at `n = -1` + n >= 0 + } + + /** + * Gets the `name`d parameter node of this callable. + * Will not return the parameter node for `self`, instead use `getSelfParameter`. + */ override NameNode getParameterByName(string name) { - result = this.getFunction().getParameterByName(name) + result = this.getFunction().getParameterByName(name) and + not result = this.getSelfParameter() } override predicate neverReturns() { this.getFunction().neverReturns() } override predicate functionAndOffset(CallableObjectInternal function, int offset) { function = this.getFunction() and offset = 1 + or + function = this and offset = 0 } override predicate useOriginAsLegacyObject() { any() } diff --git a/python/ql/src/semmle/python/objects/ObjectAPI.qll b/python/ql/src/semmle/python/objects/ObjectAPI.qll index 65b3326e602..320135814e1 100644 --- a/python/ql/src/semmle/python/objects/ObjectAPI.qll +++ b/python/ql/src/semmle/python/objects/ObjectAPI.qll @@ -352,7 +352,29 @@ class CallableValue extends Value { result = this.(CallableObjectInternal).getParameterByName(name) } - /** Gets the argument corresponding to the `n'th parameter node of this callable. */ + /** + * Gets the argument in `call` corresponding to the `n`'th positional parameter of this callable. + * + * Use this method instead of `call.getArg(n)` to handle the fact that this function might be used as + * a bound-method, such that argument `n` of the call corresponds to the `n+1` parameter of the callable. + * + * This method also gives results when the argument is passed as a keyword argument in `call`, as long + * as `this` is not a builtin function or a builtin method. + * + * Examples: + * + * - if `this` represents the `PythonFunctionValue` for `def func(a, b):`, and `call` represents + * `func(10, 20)`, then `getArgumentForCall(call, 0)` will give the `ControlFlowNode` for `10`. + * + * - with `call` representing `func(b=20, a=10)`, `getArgumentForCall(call, 0)` will give + * the `ControlFlowNode` for `10`. + * + * - if `this` represents the `PythonFunctionValue` for `def func(self, a, b):`, and `call` + * represents `foo.func(10, 20)`, then `getArgumentForCall(call, 1)` will give the + * `ControlFlowNode` for `10`. + * Note: There will also exist a `BoundMethodValue bm` where `bm.getArgumentForCall(call, 0)` + * will give the `ControlFlowNode` for `10` (notice the shift in index used). + */ cached ControlFlowNode getArgumentForCall(CallNode call, int n) { exists(ObjectInternal called, int offset | @@ -363,7 +385,7 @@ class CallableValue extends Value { or exists(string name | call.getArgByName(name) = result and - this.(PythonFunctionObjectInternal).getScope().getArg(n + offset).getName() = name + this.getParameter(n).getId() = name ) or called instanceof BoundMethodObjectInternal and @@ -373,21 +395,37 @@ class CallableValue extends Value { ) } - /** Gets the argument corresponding to the `name`d parameter node of this callable. */ + /** + * Gets the argument in `call` corresponding to the `name`d keyword parameter of this callable. + * + * This method also gives results when the argument is passed as a positional argument in `call`, as long + * as `this` is not a builtin function or a builtin method. + * + * Examples: + * + * - if `this` represents the `PythonFunctionValue` for `def func(a, b):`, and `call` represents + * `func(10, 20)`, then `getNamedArgumentForCall(call, "a")` will give the `ControlFlowNode` for `10`. + * + * - with `call` representing `func(b=20, a=10)`, `getNamedArgumentForCall(call, "a")` will give + * the `ControlFlowNode` for `10`. + * + * - if `this` represents the `PythonFunctionValue` for `def func(self, a, b):`, and `call` + * represents `foo.func(10, 20)`, then `getNamedArgumentForCall(call, "a")` will give the + * `ControlFlowNode` for `10`. + */ cached ControlFlowNode getNamedArgumentForCall(CallNode call, string name) { exists(CallableObjectInternal called, int offset | PointsToInternal::pointsTo(call.getFunction(), _, called, _) and called.functionAndOffset(this, offset) | + call.getArgByName(name) = result + or exists(int n | call.getArg(n) = result and - this.(PythonFunctionObjectInternal).getScope().getArg(n + offset).getName() = name + this.getParameter(n + offset).getId() = name ) or - call.getArgByName(name) = result and - exists(this.(PythonFunctionObjectInternal).getScope().getArgByName(name)) - or called instanceof BoundMethodObjectInternal and offset = 1 and name = "self" and @@ -396,6 +434,29 @@ class CallableValue extends Value { } } +/** + * Class representing bound-methods, such as `o.func`, where `o` is an instance + * of a class that has a callable attribute `func`. + */ +class BoundMethodValue extends CallableValue { + BoundMethodValue() { this instanceof BoundMethodObjectInternal } + + /** + * Gets the callable that will be used when `this` is called. + * The actual callable for `func` in `o.func`. + */ + CallableValue getFunction() { result = this.(BoundMethodObjectInternal).getFunction() } + + /** + * Gets the value that will be used for the `self` parameter when `this` is called. + * The value for `o` in `o.func`. + */ + Value getSelf() { result = this.(BoundMethodObjectInternal).getSelf() } + + /** Gets the parameter node that will be used for `self`. */ + NameNode getSelfParameter() { result = this.(BoundMethodObjectInternal).getSelfParameter() } +} + /** * Class representing classes in the Python program, both Python and built-in. */ @@ -663,11 +724,13 @@ class PythonFunctionValue extends FunctionValue { ControlFlowNode getAReturnedNode() { result = this.getScope().getAReturnValueFlowNode() } override ClassValue getARaisedType() { scope_raises(result, this.getScope()) } - + override ClassValue getAnInferredReturnType() { - /* We have to do a special version of this because builtin functions have no + /* + * We have to do a special version of this because builtin functions have no * explicit return nodes that we can query and get the class of. */ + result = this.getAReturnedNode().pointsTo().getClass() } } @@ -690,9 +753,11 @@ class BuiltinFunctionValue extends FunctionValue { } override ClassValue getAnInferredReturnType() { - /* We have to do a special version of this because builtin functions have no + /* + * We have to do a special version of this because builtin functions have no * explicit return nodes that we can query and get the class of. */ + result = TBuiltinClassObject(this.(BuiltinFunctionObjectInternal).getReturnType()) } } @@ -719,7 +784,7 @@ class BuiltinMethodValue extends FunctionValue { /* Information is unavailable for C code in general */ none() } - + override ClassValue getAnInferredReturnType() { result = TBuiltinClassObject(this.(BuiltinMethodObjectInternal).getReturnType()) } diff --git a/python/ql/src/semmle/python/security/ClearText.qll b/python/ql/src/semmle/python/security/ClearText.qll index d08d2b03883..a26e33218dd 100644 --- a/python/ql/src/semmle/python/security/ClearText.qll +++ b/python/ql/src/semmle/python/security/ClearText.qll @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.security.SensitiveData import semmle.python.dataflow.Files import semmle.python.web.Http diff --git a/python/ql/src/semmle/python/security/Crypto.qll b/python/ql/src/semmle/python/security/Crypto.qll index 06244851018..98ec8ecb2f1 100644 --- a/python/ql/src/semmle/python/security/Crypto.qll +++ b/python/ql/src/semmle/python/security/Crypto.qll @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking private import semmle.python.security.SensitiveData private import semmle.crypto.Crypto as CryptoLib diff --git a/python/ql/src/semmle/python/security/Exceptions.qll b/python/ql/src/semmle/python/security/Exceptions.qll index 5344808caac..4288761c565 100644 --- a/python/ql/src/semmle/python/security/Exceptions.qll +++ b/python/ql/src/semmle/python/security/Exceptions.qll @@ -4,7 +4,7 @@ */ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Basic private Value traceback_function(string name) { result = Module::named("traceback").attr(name) } diff --git a/python/ql/src/semmle/python/security/SQL.qll b/python/ql/src/semmle/python/security/SQL.qll index a09d74134e6..4d2ef6218cc 100644 --- a/python/ql/src/semmle/python/security/SQL.qll +++ b/python/ql/src/semmle/python/security/SQL.qll @@ -1,4 +1,4 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking abstract class SqlInjectionSink extends TaintSink { } diff --git a/python/ql/src/semmle/python/security/SensitiveData.qll b/python/ql/src/semmle/python/security/SensitiveData.qll index 6e0b44d3c33..18e52423d19 100644 --- a/python/ql/src/semmle/python/security/SensitiveData.qll +++ b/python/ql/src/semmle/python/security/SensitiveData.qll @@ -10,7 +10,7 @@ */ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.web.HttpRequest /** diff --git a/python/ql/src/semmle/python/security/injection/Command.qll b/python/ql/src/semmle/python/security/injection/Command.qll index 1a6c2508719..3084f2c8cf4 100644 --- a/python/ql/src/semmle/python/security/injection/Command.qll +++ b/python/ql/src/semmle/python/security/injection/Command.qll @@ -7,7 +7,7 @@ */ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Untrusted /** Abstract taint sink that is potentially vulnerable to malicious shell commands. */ diff --git a/python/ql/src/semmle/python/security/injection/Deserialization.qll b/python/ql/src/semmle/python/security/injection/Deserialization.qll index 14bf9d2233d..1f73ede22f2 100644 --- a/python/ql/src/semmle/python/security/injection/Deserialization.qll +++ b/python/ql/src/semmle/python/security/injection/Deserialization.qll @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking /** `pickle.loads(untrusted)` vulnerability. */ abstract class DeserializationSink extends TaintSink { diff --git a/python/ql/src/semmle/python/security/injection/Exec.qll b/python/ql/src/semmle/python/security/injection/Exec.qll index 59ed181023a..462847e7d3e 100644 --- a/python/ql/src/semmle/python/security/injection/Exec.qll +++ b/python/ql/src/semmle/python/security/injection/Exec.qll @@ -7,7 +7,7 @@ */ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Untrusted /** diff --git a/python/ql/src/semmle/python/security/injection/Marshal.qll b/python/ql/src/semmle/python/security/injection/Marshal.qll index 274392c8b4f..7ae77e597a5 100644 --- a/python/ql/src/semmle/python/security/injection/Marshal.qll +++ b/python/ql/src/semmle/python/security/injection/Marshal.qll @@ -7,7 +7,7 @@ */ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Untrusted import semmle.python.security.injection.Deserialization diff --git a/python/ql/src/semmle/python/security/injection/Path.qll b/python/ql/src/semmle/python/security/injection/Path.qll index 02c0eb12697..e871d11cf2b 100644 --- a/python/ql/src/semmle/python/security/injection/Path.qll +++ b/python/ql/src/semmle/python/security/injection/Path.qll @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Untrusted /** diff --git a/python/ql/src/semmle/python/security/injection/Pickle.qll b/python/ql/src/semmle/python/security/injection/Pickle.qll index 2d56bc25f8e..1135587df50 100644 --- a/python/ql/src/semmle/python/security/injection/Pickle.qll +++ b/python/ql/src/semmle/python/security/injection/Pickle.qll @@ -7,7 +7,7 @@ */ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Untrusted import semmle.python.security.injection.Deserialization diff --git a/python/ql/src/semmle/python/security/injection/Sql.qll b/python/ql/src/semmle/python/security/injection/Sql.qll index 38c0eaec7d7..7fc9515c08b 100644 --- a/python/ql/src/semmle/python/security/injection/Sql.qll +++ b/python/ql/src/semmle/python/security/injection/Sql.qll @@ -7,7 +7,7 @@ */ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Untrusted import semmle.python.security.SQL diff --git a/python/ql/src/semmle/python/security/injection/Xml.qll b/python/ql/src/semmle/python/security/injection/Xml.qll index 080df3067d0..3a4e6ebc552 100644 --- a/python/ql/src/semmle/python/security/injection/Xml.qll +++ b/python/ql/src/semmle/python/security/injection/Xml.qll @@ -7,7 +7,7 @@ */ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Untrusted import semmle.python.security.injection.Deserialization diff --git a/python/ql/src/semmle/python/security/injection/Yaml.qll b/python/ql/src/semmle/python/security/injection/Yaml.qll index 3b0156b2812..0799d9b9160 100644 --- a/python/ql/src/semmle/python/security/injection/Yaml.qll +++ b/python/ql/src/semmle/python/security/injection/Yaml.qll @@ -7,7 +7,7 @@ */ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Untrusted import semmle.python.security.injection.Deserialization diff --git a/python/ql/src/semmle/python/security/strings/Basic.qll b/python/ql/src/semmle/python/security/strings/Basic.qll index 1eed2bb327a..cb2178addde 100755 --- a/python/ql/src/semmle/python/security/strings/Basic.qll +++ b/python/ql/src/semmle/python/security/strings/Basic.qll @@ -1,6 +1,6 @@ import python private import Common -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking /** An extensible kind of taint representing any kind of string. */ abstract class StringKind extends TaintKind { diff --git a/python/ql/src/semmle/python/types/ModuleObject.qll b/python/ql/src/semmle/python/types/ModuleObject.qll index 3b5d15c41b1..644d4e60244 100644 --- a/python/ql/src/semmle/python/types/ModuleObject.qll +++ b/python/ql/src/semmle/python/types/ModuleObject.qll @@ -118,7 +118,7 @@ class BuiltinModuleObject extends ModuleObject { override predicate hasAttribute(string name) { exists(this.asBuiltin().getMember(name)) } - override predicate exportsComplete() { any() } + deprecated override predicate exportsComplete() { any() } } class PythonModuleObject extends ModuleObject { @@ -132,7 +132,7 @@ class PythonModuleObject extends ModuleObject { override Container getPath() { result = this.getModule().getFile() } - override predicate exportsComplete() { + deprecated override predicate exportsComplete() { exists(Module m | m = this.getModule() | not exists(Call modify, Attribute attr, GlobalVariable all | modify.getScope() = m and @@ -196,7 +196,7 @@ class PackageObject extends ModuleObject { ) } - override predicate exportsComplete() { + deprecated override predicate exportsComplete() { not exists(this.getInitModule()) or this.getInitModule().exportsComplete() diff --git a/python/ql/src/semmle/python/web/bottle/Redirect.qll b/python/ql/src/semmle/python/web/bottle/Redirect.qll index 187839f30e8..be4c552fea2 100644 --- a/python/ql/src/semmle/python/web/bottle/Redirect.qll +++ b/python/ql/src/semmle/python/web/bottle/Redirect.qll @@ -5,7 +5,7 @@ */ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Basic import semmle.python.web.bottle.General diff --git a/python/ql/src/semmle/python/web/bottle/Request.qll b/python/ql/src/semmle/python/web/bottle/Request.qll index 585336ac65a..10d2b223863 100644 --- a/python/ql/src/semmle/python/web/bottle/Request.qll +++ b/python/ql/src/semmle/python/web/bottle/Request.qll @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Untrusted import semmle.python.web.Http import semmle.python.web.bottle.General diff --git a/python/ql/src/semmle/python/web/bottle/Response.qll b/python/ql/src/semmle/python/web/bottle/Response.qll index 7dd53377a8c..dede231c27d 100644 --- a/python/ql/src/semmle/python/web/bottle/Response.qll +++ b/python/ql/src/semmle/python/web/bottle/Response.qll @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Untrusted import semmle.python.web.Http import semmle.python.web.bottle.General diff --git a/python/ql/src/semmle/python/web/cherrypy/Request.qll b/python/ql/src/semmle/python/web/cherrypy/Request.qll index 2440a2710f6..309d51f5539 100644 --- a/python/ql/src/semmle/python/web/cherrypy/Request.qll +++ b/python/ql/src/semmle/python/web/cherrypy/Request.qll @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Basic import semmle.python.web.Http import semmle.python.web.cherrypy.General diff --git a/python/ql/src/semmle/python/web/cherrypy/Response.qll b/python/ql/src/semmle/python/web/cherrypy/Response.qll index 7702b8ce500..3ed1d0d9b57 100644 --- a/python/ql/src/semmle/python/web/cherrypy/Response.qll +++ b/python/ql/src/semmle/python/web/cherrypy/Response.qll @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Untrusted import semmle.python.web.Http import semmle.python.web.cherrypy.General diff --git a/python/ql/src/semmle/python/web/django/Model.qll b/python/ql/src/semmle/python/web/django/Model.qll index b8f47b64bdf..f8a61bda10e 100644 --- a/python/ql/src/semmle/python/web/django/Model.qll +++ b/python/ql/src/semmle/python/web/django/Model.qll @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Basic import semmle.python.web.Http import semmle.python.security.injection.Sql diff --git a/python/ql/src/semmle/python/web/django/Redirect.qll b/python/ql/src/semmle/python/web/django/Redirect.qll index a550088eaf6..67342517a99 100644 --- a/python/ql/src/semmle/python/web/django/Redirect.qll +++ b/python/ql/src/semmle/python/web/django/Redirect.qll @@ -5,7 +5,7 @@ */ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Basic private import semmle.python.web.django.Shared private import semmle.python.web.Http diff --git a/python/ql/src/semmle/python/web/django/Request.qll b/python/ql/src/semmle/python/web/django/Request.qll index e054407fcee..503264c2817 100644 --- a/python/ql/src/semmle/python/web/django/Request.qll +++ b/python/ql/src/semmle/python/web/django/Request.qll @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.web.Http import semmle.python.web.django.General diff --git a/python/ql/src/semmle/python/web/django/Response.qll b/python/ql/src/semmle/python/web/django/Response.qll index dc6a3634440..35d8cd63553 100644 --- a/python/ql/src/semmle/python/web/django/Response.qll +++ b/python/ql/src/semmle/python/web/django/Response.qll @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Basic private import semmle.python.web.django.Shared private import semmle.python.web.Http diff --git a/python/ql/src/semmle/python/web/falcon/Request.qll b/python/ql/src/semmle/python/web/falcon/Request.qll index 13f3fa4c441..66707b01d0c 100644 --- a/python/ql/src/semmle/python/web/falcon/Request.qll +++ b/python/ql/src/semmle/python/web/falcon/Request.qll @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.web.Http import semmle.python.web.falcon.General import semmle.python.security.strings.External diff --git a/python/ql/src/semmle/python/web/falcon/Response.qll b/python/ql/src/semmle/python/web/falcon/Response.qll index ab7798cc2cb..c66a6315ce5 100644 --- a/python/ql/src/semmle/python/web/falcon/Response.qll +++ b/python/ql/src/semmle/python/web/falcon/Response.qll @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.web.Http import semmle.python.web.falcon.General import semmle.python.security.strings.External diff --git a/python/ql/src/semmle/python/web/flask/Redirect.qll b/python/ql/src/semmle/python/web/flask/Redirect.qll index f01f13f6ef7..4c4e289c605 100644 --- a/python/ql/src/semmle/python/web/flask/Redirect.qll +++ b/python/ql/src/semmle/python/web/flask/Redirect.qll @@ -5,7 +5,7 @@ */ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Basic import semmle.python.web.flask.General diff --git a/python/ql/src/semmle/python/web/flask/Request.qll b/python/ql/src/semmle/python/web/flask/Request.qll index 7e2650a1ca0..5548e409c32 100644 --- a/python/ql/src/semmle/python/web/flask/Request.qll +++ b/python/ql/src/semmle/python/web/flask/Request.qll @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.web.Http import semmle.python.web.flask.General diff --git a/python/ql/src/semmle/python/web/flask/Response.qll b/python/ql/src/semmle/python/web/flask/Response.qll index 0828c180e9a..e070f19b1f6 100644 --- a/python/ql/src/semmle/python/web/flask/Response.qll +++ b/python/ql/src/semmle/python/web/flask/Response.qll @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Basic import semmle.python.web.flask.General diff --git a/python/ql/src/semmle/python/web/pyramid/Redirect.qll b/python/ql/src/semmle/python/web/pyramid/Redirect.qll index 8c7e57a4285..2ab68b40621 100644 --- a/python/ql/src/semmle/python/web/pyramid/Redirect.qll +++ b/python/ql/src/semmle/python/web/pyramid/Redirect.qll @@ -5,7 +5,7 @@ */ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Basic import semmle.python.web.Http diff --git a/python/ql/src/semmle/python/web/pyramid/Request.qll b/python/ql/src/semmle/python/web/pyramid/Request.qll index dc5be31e68a..f3422b682d6 100644 --- a/python/ql/src/semmle/python/web/pyramid/Request.qll +++ b/python/ql/src/semmle/python/web/pyramid/Request.qll @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.web.Http private import semmle.python.web.webob.Request private import semmle.python.web.pyramid.View diff --git a/python/ql/src/semmle/python/web/pyramid/Response.qll b/python/ql/src/semmle/python/web/pyramid/Response.qll index 37dc4be783c..c51a437350d 100644 --- a/python/ql/src/semmle/python/web/pyramid/Response.qll +++ b/python/ql/src/semmle/python/web/pyramid/Response.qll @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Basic import semmle.python.web.Http private import semmle.python.web.pyramid.View diff --git a/python/ql/src/semmle/python/web/stdlib/Request.qll b/python/ql/src/semmle/python/web/stdlib/Request.qll index ce150371279..459a5091389 100644 --- a/python/ql/src/semmle/python/web/stdlib/Request.qll +++ b/python/ql/src/semmle/python/web/stdlib/Request.qll @@ -4,7 +4,7 @@ * (or subclasses) and form parsing using `cgi.FieldStorage`. */ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.web.Http /** Source of BaseHTTPRequestHandler instances. */ diff --git a/python/ql/src/semmle/python/web/stdlib/Response.qll b/python/ql/src/semmle/python/web/stdlib/Response.qll index fb056d49525..58949e0a6d9 100644 --- a/python/ql/src/semmle/python/web/stdlib/Response.qll +++ b/python/ql/src/semmle/python/web/stdlib/Response.qll @@ -3,7 +3,7 @@ */ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.web.Http private predicate is_wfile(AttrNode wfile) { diff --git a/python/ql/src/semmle/python/web/tornado/Redirect.qll b/python/ql/src/semmle/python/web/tornado/Redirect.qll index 2d2c39907eb..f846f113816 100644 --- a/python/ql/src/semmle/python/web/tornado/Redirect.qll +++ b/python/ql/src/semmle/python/web/tornado/Redirect.qll @@ -5,7 +5,7 @@ */ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Basic import semmle.python.web.Http import Tornado diff --git a/python/ql/src/semmle/python/web/tornado/Request.qll b/python/ql/src/semmle/python/web/tornado/Request.qll index 66c77d4f269..cfb7bfa7b04 100644 --- a/python/ql/src/semmle/python/web/tornado/Request.qll +++ b/python/ql/src/semmle/python/web/tornado/Request.qll @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.web.Http import Tornado diff --git a/python/ql/src/semmle/python/web/tornado/Response.qll b/python/ql/src/semmle/python/web/tornado/Response.qll index 2c2da1a4c70..b9213ac8446 100644 --- a/python/ql/src/semmle/python/web/tornado/Response.qll +++ b/python/ql/src/semmle/python/web/tornado/Response.qll @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Basic private import semmle.python.web.Http import Tornado diff --git a/python/ql/src/semmle/python/web/tornado/Tornado.qll b/python/ql/src/semmle/python/web/tornado/Tornado.qll index 10a5c5be962..d9f6ab823b9 100644 --- a/python/ql/src/semmle/python/web/tornado/Tornado.qll +++ b/python/ql/src/semmle/python/web/tornado/Tornado.qll @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.web.Http private ClassValue theTornadoRequestHandlerClass() { diff --git a/python/ql/src/semmle/python/web/turbogears/Response.qll b/python/ql/src/semmle/python/web/turbogears/Response.qll index cab083bf8b7..307806dc485 100644 --- a/python/ql/src/semmle/python/web/turbogears/Response.qll +++ b/python/ql/src/semmle/python/web/turbogears/Response.qll @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Basic import semmle.python.web.Http import TurboGears diff --git a/python/ql/src/semmle/python/web/turbogears/TurboGears.qll b/python/ql/src/semmle/python/web/turbogears/TurboGears.qll index 1cef2f51c84..547a6c0e505 100644 --- a/python/ql/src/semmle/python/web/turbogears/TurboGears.qll +++ b/python/ql/src/semmle/python/web/turbogears/TurboGears.qll @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking private ClassValue theTurboGearsControllerClass() { result = Value::named("tg.TGController") } diff --git a/python/ql/src/semmle/python/web/twisted/Request.qll b/python/ql/src/semmle/python/web/twisted/Request.qll index 969392d0eef..0be6fc78f2c 100644 --- a/python/ql/src/semmle/python/web/twisted/Request.qll +++ b/python/ql/src/semmle/python/web/twisted/Request.qll @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.web.Http import Twisted diff --git a/python/ql/src/semmle/python/web/twisted/Response.qll b/python/ql/src/semmle/python/web/twisted/Response.qll index b7f67ff6b20..be32ba08188 100644 --- a/python/ql/src/semmle/python/web/twisted/Response.qll +++ b/python/ql/src/semmle/python/web/twisted/Response.qll @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.web.Http import semmle.python.security.strings.Basic import Twisted diff --git a/python/ql/src/semmle/python/web/twisted/Twisted.qll b/python/ql/src/semmle/python/web/twisted/Twisted.qll index e3b0ab0f9be..9ecd12b9620 100644 --- a/python/ql/src/semmle/python/web/twisted/Twisted.qll +++ b/python/ql/src/semmle/python/web/twisted/Twisted.qll @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking private ClassValue theTwistedHttpRequestClass() { result = Value::named("twisted.web.http.Request") diff --git a/python/ql/src/semmle/python/web/webob/Request.qll b/python/ql/src/semmle/python/web/webob/Request.qll index 70fa311f6b0..4d6e98bb2e9 100644 --- a/python/ql/src/semmle/python/web/webob/Request.qll +++ b/python/ql/src/semmle/python/web/webob/Request.qll @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.web.Http abstract class BaseWebobRequest extends TaintKind { diff --git a/python/ql/src/semmle/python/xml/XML.qll b/python/ql/src/semmle/python/xml/XML.qll index dc7836aaabe..713903b63e6 100755 --- a/python/ql/src/semmle/python/xml/XML.qll +++ b/python/ql/src/semmle/python/xml/XML.qll @@ -116,7 +116,7 @@ class XMLFile extends XMLParent, File { XMLFile() { xmlEncoding(this, _) } /** Gets a printable representation of this XML file. */ - override string toString() { result = XMLParent.super.toString() } + override string toString() { result = getName() } /** Gets the name of this XML file. */ override string getName() { result = File.super.getAbsolutePath() } @@ -236,7 +236,7 @@ class XMLElement extends @xmlelement, XMLParent, XMLLocatable { string getAttributeValue(string name) { result = this.getAttribute(name).getValue() } /** Gets a printable representation of this XML element. */ - override string toString() { result = XMLParent.super.toString() } + override string toString() { result = getName() } } /** diff --git a/python/ql/test/3/library-tests/taint/unpacking/Taint.qll b/python/ql/test/3/library-tests/taint/unpacking/Taint.qll index b97f65225f2..21e16aabac5 100644 --- a/python/ql/test/3/library-tests/taint/unpacking/Taint.qll +++ b/python/ql/test/3/library-tests/taint/unpacking/Taint.qll @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Untrusted class SimpleSource extends TaintSource { diff --git a/python/ql/test/3/library-tests/taint/unpacking/TestTaint.ql b/python/ql/test/3/library-tests/taint/unpacking/TestTaint.ql index 8347bd25433..fb1d102aa7a 100644 --- a/python/ql/test/3/library-tests/taint/unpacking/TestTaint.ql +++ b/python/ql/test/3/library-tests/taint/unpacking/TestTaint.ql @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import Taint from Call call, Expr arg, string taint_string diff --git a/python/ql/test/3/query-tests/Classes/equals-attr/DefineEqualsWhenAddingAttributes.expected b/python/ql/test/3/query-tests/Classes/equals-attr/DefineEqualsWhenAddingAttributes.expected index 8f5f5e7b762..8843b6d52ef 100644 --- a/python/ql/test/3/query-tests/Classes/equals-attr/DefineEqualsWhenAddingAttributes.expected +++ b/python/ql/test/3/query-tests/Classes/equals-attr/DefineEqualsWhenAddingAttributes.expected @@ -1,2 +1,2 @@ -| test.py:12:1:12:24 | class C | The class 'C' does not override $@, but adds the new attribute $@. | test.py:9:5:9:28 | Function __eq__ | '__eq__' | test.py:15:9:15:14 | Attribute | a | -| test.py:12:1:12:24 | class C | The class 'C' does not override $@, but adds the new attribute $@. | test.py:9:5:9:28 | Function __eq__ | '__eq__' | test.py:15:17:15:22 | Attribute | b | +| test.py:12:1:12:24 | class C | The class 'C' does not override $@, but adds the new attribute $@. | test.py:9:5:9:28 | Function RedefineEquals.__eq__ | '__eq__' | test.py:15:9:15:14 | Attribute | a | +| test.py:12:1:12:24 | class C | The class 'C' does not override $@, but adds the new attribute $@. | test.py:9:5:9:28 | Function RedefineEquals.__eq__ | '__eq__' | test.py:15:17:15:22 | Attribute | b | diff --git a/python/ql/test/library-tests/PointsTo/calls/Argument.expected b/python/ql/test/library-tests/PointsTo/calls/Argument.expected deleted file mode 100644 index dbc7e586f47..00000000000 --- a/python/ql/test/library-tests/PointsTo/calls/Argument.expected +++ /dev/null @@ -1,15 +0,0 @@ -| 19 | 0 | ControlFlowNode for w | Function f | -| 19 | 1 | ControlFlowNode for x | Function f | -| 19 | 2 | ControlFlowNode for y | Function f | -| 21 | 0 | ControlFlowNode for y | Function f | -| 21 | 1 | ControlFlowNode for w | Function f | -| 21 | 2 | ControlFlowNode for z | Function f | -| 23 | 0 | ControlFlowNode for c | Function f | -| 23 | 1 | ControlFlowNode for w | Function f | -| 23 | 2 | ControlFlowNode for z | Function f | -| 24 | 0 | ControlFlowNode for c | Function n | -| 24 | 1 | ControlFlowNode for x | Function n | -| 25 | 0 | ControlFlowNode for y | Function n | -| 25 | 1 | ControlFlowNode for z | Function n | -| 33 | 0 | ControlFlowNode for IntegerLiteral | Function foo | -| 34 | 0 | ControlFlowNode for IntegerLiteral | Function foo | diff --git a/python/ql/test/library-tests/PointsTo/calls/Argument.ql b/python/ql/test/library-tests/PointsTo/calls/Argument.ql deleted file mode 100644 index 1678c02c182..00000000000 --- a/python/ql/test/library-tests/PointsTo/calls/Argument.ql +++ /dev/null @@ -1,5 +0,0 @@ -import python - -from ControlFlowNode arg, FunctionObject func, int i -where arg = func.getArgumentForCall(_, i) -select arg.getLocation().getStartLine(), i, arg.toString(), func.toString() diff --git a/python/ql/test/library-tests/PointsTo/calls/Call.expected b/python/ql/test/library-tests/PointsTo/calls/Call.expected deleted file mode 100644 index 9e9c5646d89..00000000000 --- a/python/ql/test/library-tests/PointsTo/calls/Call.expected +++ /dev/null @@ -1,7 +0,0 @@ -| 19 | ControlFlowNode for f() | Function f | -| 21 | ControlFlowNode for f() | Function f | -| 23 | ControlFlowNode for Attribute() | Function f | -| 24 | ControlFlowNode for Attribute() | Function n | -| 25 | ControlFlowNode for Attribute() | Function n | -| 33 | ControlFlowNode for Attribute() | Function foo | -| 34 | ControlFlowNode for Attribute() | Function foo | diff --git a/python/ql/test/library-tests/PointsTo/calls/CallPointsTo.expected b/python/ql/test/library-tests/PointsTo/calls/CallPointsTo.expected new file mode 100644 index 00000000000..05c4d41406e --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/calls/CallPointsTo.expected @@ -0,0 +1,19 @@ +| 19 | ControlFlowNode for f() | Function f | +| 21 | ControlFlowNode for f() | Function f | +| 22 | ControlFlowNode for C() | class C | +| 23 | ControlFlowNode for Attribute() | Method(Function f, C()) | +| 24 | ControlFlowNode for Attribute() | Method(Function C.n, C()) | +| 25 | ControlFlowNode for Attribute() | Function C.n | +| 29 | ControlFlowNode for staticmethod() | builtin-class staticmethod | +| 33 | ControlFlowNode for Attribute() | Function D.foo | +| 34 | ControlFlowNode for Attribute() | Function D.foo | +| 34 | ControlFlowNode for D() | class D | +| 37 | ControlFlowNode for Attribute() | Method(builtin method append, List) | +| 38 | ControlFlowNode for len() | Builtin-function len | +| 40 | ControlFlowNode for f() | Function f | +| 41 | ControlFlowNode for C() | class C | +| 42 | ControlFlowNode for Attribute() | Method(Function C.n, C()) | +| 45 | ControlFlowNode for open() | Builtin-function open | +| 46 | ControlFlowNode for open() | Builtin-function open | +| 51 | ControlFlowNode for foo() | Function foo | +| 55 | ControlFlowNode for bar() | Function bar | diff --git a/python/ql/test/library-tests/PointsTo/calls/CallPointsTo.ql b/python/ql/test/library-tests/PointsTo/calls/CallPointsTo.ql new file mode 100644 index 00000000000..10247a98f94 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/calls/CallPointsTo.ql @@ -0,0 +1,5 @@ +import python + +from CallNode call, Value func +where call.getFunction().pointsTo(func) +select call.getLocation().getStartLine(), call.toString(), func.toString() diff --git a/python/ql/test/library-tests/PointsTo/calls/GetACall.expected b/python/ql/test/library-tests/PointsTo/calls/GetACall.expected new file mode 100644 index 00000000000..c9b7822b278 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/calls/GetACall.expected @@ -0,0 +1,23 @@ +| 19 | ControlFlowNode for f() | Function f | +| 21 | ControlFlowNode for f() | Function f | +| 22 | ControlFlowNode for C() | class C | +| 23 | ControlFlowNode for Attribute() | Function f | +| 23 | ControlFlowNode for Attribute() | Method(Function f, C()) | +| 24 | ControlFlowNode for Attribute() | Function C.n | +| 24 | ControlFlowNode for Attribute() | Method(Function C.n, C()) | +| 25 | ControlFlowNode for Attribute() | Function C.n | +| 29 | ControlFlowNode for staticmethod() | builtin-class staticmethod | +| 33 | ControlFlowNode for Attribute() | Function D.foo | +| 34 | ControlFlowNode for Attribute() | Function D.foo | +| 34 | ControlFlowNode for D() | class D | +| 37 | ControlFlowNode for Attribute() | Method(builtin method append, List) | +| 37 | ControlFlowNode for Attribute() | builtin method append | +| 38 | ControlFlowNode for len() | Builtin-function len | +| 40 | ControlFlowNode for f() | Function f | +| 41 | ControlFlowNode for C() | class C | +| 42 | ControlFlowNode for Attribute() | Function C.n | +| 42 | ControlFlowNode for Attribute() | Method(Function C.n, C()) | +| 45 | ControlFlowNode for open() | Builtin-function open | +| 46 | ControlFlowNode for open() | Builtin-function open | +| 51 | ControlFlowNode for foo() | Function foo | +| 55 | ControlFlowNode for bar() | Function bar | diff --git a/python/ql/test/library-tests/PointsTo/calls/Call.ql b/python/ql/test/library-tests/PointsTo/calls/GetACall.ql similarity index 71% rename from python/ql/test/library-tests/PointsTo/calls/Call.ql rename to python/ql/test/library-tests/PointsTo/calls/GetACall.ql index 94c4212cc64..84f2ab4fb4a 100644 --- a/python/ql/test/library-tests/PointsTo/calls/Call.ql +++ b/python/ql/test/library-tests/PointsTo/calls/GetACall.ql @@ -1,5 +1,5 @@ import python -from ControlFlowNode call, FunctionObject func +from ControlFlowNode call, Value func where call = func.getACall() select call.getLocation().getStartLine(), call.toString(), func.toString() diff --git a/python/ql/test/library-tests/PointsTo/calls/getArgumentForCall.expected b/python/ql/test/library-tests/PointsTo/calls/getArgumentForCall.expected new file mode 100644 index 00000000000..c834c72049f --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/calls/getArgumentForCall.expected @@ -0,0 +1,34 @@ +| 19 | ControlFlowNode for f() | Function f | 0 | ControlFlowNode for w | +| 19 | ControlFlowNode for f() | Function f | 1 | ControlFlowNode for x | +| 19 | ControlFlowNode for f() | Function f | 2 | ControlFlowNode for y | +| 21 | ControlFlowNode for f() | Function f | 0 | ControlFlowNode for y | +| 21 | ControlFlowNode for f() | Function f | 1 | ControlFlowNode for w | +| 21 | ControlFlowNode for f() | Function f | 2 | ControlFlowNode for z | +| 23 | ControlFlowNode for Attribute() | Function f | 0 | ControlFlowNode for c | +| 23 | ControlFlowNode for Attribute() | Function f | 1 | ControlFlowNode for w | +| 23 | ControlFlowNode for Attribute() | Function f | 2 | ControlFlowNode for z | +| 23 | ControlFlowNode for Attribute() | Method(Function f, C()) | 0 | ControlFlowNode for w | +| 23 | ControlFlowNode for Attribute() | Method(Function f, C()) | 1 | ControlFlowNode for z | +| 24 | ControlFlowNode for Attribute() | Function C.n | 0 | ControlFlowNode for c | +| 24 | ControlFlowNode for Attribute() | Function C.n | 1 | ControlFlowNode for x | +| 24 | ControlFlowNode for Attribute() | Method(Function C.n, C()) | 0 | ControlFlowNode for x | +| 25 | ControlFlowNode for Attribute() | Function C.n | 0 | ControlFlowNode for y | +| 25 | ControlFlowNode for Attribute() | Function C.n | 1 | ControlFlowNode for z | +| 33 | ControlFlowNode for Attribute() | Function D.foo | 0 | ControlFlowNode for IntegerLiteral | +| 34 | ControlFlowNode for Attribute() | Function D.foo | 0 | ControlFlowNode for IntegerLiteral | +| 37 | ControlFlowNode for Attribute() | Method(builtin method append, List) | 0 | ControlFlowNode for IntegerLiteral | +| 37 | ControlFlowNode for Attribute() | builtin method append | 0 | ControlFlowNode for l | +| 37 | ControlFlowNode for Attribute() | builtin method append | 1 | ControlFlowNode for IntegerLiteral | +| 38 | ControlFlowNode for len() | Builtin-function len | 0 | ControlFlowNode for l | +| 40 | ControlFlowNode for f() | Function f | 0 | ControlFlowNode for IntegerLiteral | +| 40 | ControlFlowNode for f() | Function f | 1 | ControlFlowNode for IntegerLiteral | +| 40 | ControlFlowNode for f() | Function f | 2 | ControlFlowNode for IntegerLiteral | +| 42 | ControlFlowNode for Attribute() | Function C.n | 0 | ControlFlowNode for c | +| 42 | ControlFlowNode for Attribute() | Function C.n | 1 | ControlFlowNode for IntegerLiteral | +| 42 | ControlFlowNode for Attribute() | Method(Function C.n, C()) | 0 | ControlFlowNode for IntegerLiteral | +| 45 | ControlFlowNode for open() | Builtin-function open | 0 | ControlFlowNode for Str | +| 45 | ControlFlowNode for open() | Builtin-function open | 1 | ControlFlowNode for Str | +| 51 | ControlFlowNode for foo() | Function foo | 0 | ControlFlowNode for IntegerLiteral | +| 51 | ControlFlowNode for foo() | Function foo | 1 | ControlFlowNode for IntegerLiteral | +| 51 | ControlFlowNode for foo() | Function foo | 2 | ControlFlowNode for IntegerLiteral | +| 55 | ControlFlowNode for bar() | Function bar | 0 | ControlFlowNode for IntegerLiteral | diff --git a/python/ql/test/library-tests/PointsTo/calls/getArgumentForCall.ql b/python/ql/test/library-tests/PointsTo/calls/getArgumentForCall.ql new file mode 100644 index 00000000000..de13f0504e8 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/calls/getArgumentForCall.ql @@ -0,0 +1,5 @@ +import python + +from CallNode call, CallableValue callable, int i +select call.getLocation().getStartLine(), call.toString(), callable.toString(), i, + callable.getArgumentForCall(call, i).toString() diff --git a/python/ql/test/library-tests/PointsTo/calls/getNamedArgumentForCall.expected b/python/ql/test/library-tests/PointsTo/calls/getNamedArgumentForCall.expected new file mode 100644 index 00000000000..3df8b4336bf --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/calls/getNamedArgumentForCall.expected @@ -0,0 +1,31 @@ +| 19 | ControlFlowNode for f() | Function f | arg0 | ControlFlowNode for w | +| 19 | ControlFlowNode for f() | Function f | arg1 | ControlFlowNode for x | +| 19 | ControlFlowNode for f() | Function f | arg2 | ControlFlowNode for y | +| 21 | ControlFlowNode for f() | Function f | arg0 | ControlFlowNode for y | +| 21 | ControlFlowNode for f() | Function f | arg1 | ControlFlowNode for w | +| 21 | ControlFlowNode for f() | Function f | arg2 | ControlFlowNode for z | +| 23 | ControlFlowNode for Attribute() | Function f | arg1 | ControlFlowNode for w | +| 23 | ControlFlowNode for Attribute() | Function f | arg2 | ControlFlowNode for z | +| 23 | ControlFlowNode for Attribute() | Function f | self | ControlFlowNode for c | +| 23 | ControlFlowNode for Attribute() | Method(Function f, C()) | arg1 | ControlFlowNode for w | +| 23 | ControlFlowNode for Attribute() | Method(Function f, C()) | arg2 | ControlFlowNode for z | +| 24 | ControlFlowNode for Attribute() | Function C.n | arg1 | ControlFlowNode for x | +| 24 | ControlFlowNode for Attribute() | Function C.n | self | ControlFlowNode for c | +| 24 | ControlFlowNode for Attribute() | Method(Function C.n, C()) | arg1 | ControlFlowNode for x | +| 25 | ControlFlowNode for Attribute() | Function C.n | arg1 | ControlFlowNode for z | +| 25 | ControlFlowNode for Attribute() | Function C.n | self | ControlFlowNode for y | +| 33 | ControlFlowNode for Attribute() | Function D.foo | arg | ControlFlowNode for IntegerLiteral | +| 34 | ControlFlowNode for Attribute() | Function D.foo | arg | ControlFlowNode for IntegerLiteral | +| 37 | ControlFlowNode for Attribute() | builtin method append | self | ControlFlowNode for l | +| 40 | ControlFlowNode for f() | Function f | arg0 | ControlFlowNode for IntegerLiteral | +| 40 | ControlFlowNode for f() | Function f | arg1 | ControlFlowNode for IntegerLiteral | +| 40 | ControlFlowNode for f() | Function f | arg2 | ControlFlowNode for IntegerLiteral | +| 42 | ControlFlowNode for Attribute() | Function C.n | arg1 | ControlFlowNode for IntegerLiteral | +| 42 | ControlFlowNode for Attribute() | Function C.n | self | ControlFlowNode for c | +| 42 | ControlFlowNode for Attribute() | Method(Function C.n, C()) | arg1 | ControlFlowNode for IntegerLiteral | +| 46 | ControlFlowNode for open() | Builtin-function open | file | ControlFlowNode for Str | +| 46 | ControlFlowNode for open() | Builtin-function open | mode | ControlFlowNode for Str | +| 51 | ControlFlowNode for foo() | Function foo | a | ControlFlowNode for IntegerLiteral | +| 55 | ControlFlowNode for bar() | Function bar | a | ControlFlowNode for IntegerLiteral | +| 55 | ControlFlowNode for bar() | Function bar | b | ControlFlowNode for IntegerLiteral | +| 55 | ControlFlowNode for bar() | Function bar | c | ControlFlowNode for IntegerLiteral | diff --git a/python/ql/test/library-tests/PointsTo/calls/getNamedArgumentForCall.ql b/python/ql/test/library-tests/PointsTo/calls/getNamedArgumentForCall.ql new file mode 100644 index 00000000000..c531a9ab57a --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/calls/getNamedArgumentForCall.ql @@ -0,0 +1,5 @@ +import python + +from CallNode call, CallableValue callable, string name +select call.getLocation().getStartLine(), call.toString(), callable.toString(), name, + callable.getNamedArgumentForCall(call, name).toString() diff --git a/python/ql/test/library-tests/PointsTo/calls/getParameter.expected b/python/ql/test/library-tests/PointsTo/calls/getParameter.expected new file mode 100644 index 00000000000..3f384d38786 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/calls/getParameter.expected @@ -0,0 +1,12 @@ +| Function C.n | 0 | ControlFlowNode for self | +| Function C.n | 1 | ControlFlowNode for arg1 | +| Function D.foo | 0 | ControlFlowNode for arg | +| Function bar | 0 | ControlFlowNode for a | +| Function f | 0 | ControlFlowNode for arg0 | +| Function f | 1 | ControlFlowNode for arg1 | +| Function f | 2 | ControlFlowNode for arg2 | +| Function foo | 0 | ControlFlowNode for a | +| Method(Function C.n, C()) | 0 | ControlFlowNode for arg1 | +| Method(Function C.n, class C) | 0 | ControlFlowNode for arg1 | +| Method(Function f, C()) | 0 | ControlFlowNode for arg1 | +| Method(Function f, C()) | 1 | ControlFlowNode for arg2 | diff --git a/python/ql/test/library-tests/PointsTo/calls/getParameter.ql b/python/ql/test/library-tests/PointsTo/calls/getParameter.ql new file mode 100644 index 00000000000..07f12cce36f --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/calls/getParameter.ql @@ -0,0 +1,4 @@ +import python + +from CallableValue callable, int i +select callable.toString(), i, callable.getParameter(i).toString() diff --git a/python/ql/test/library-tests/PointsTo/calls/getParameterByName.expected b/python/ql/test/library-tests/PointsTo/calls/getParameterByName.expected new file mode 100644 index 00000000000..da61f6296a7 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/calls/getParameterByName.expected @@ -0,0 +1,12 @@ +| Function C.n | arg1 | ControlFlowNode for arg1 | +| Function C.n | self | ControlFlowNode for self | +| Function D.foo | arg | ControlFlowNode for arg | +| Function bar | a | ControlFlowNode for a | +| Function f | arg0 | ControlFlowNode for arg0 | +| Function f | arg1 | ControlFlowNode for arg1 | +| Function f | arg2 | ControlFlowNode for arg2 | +| Function foo | a | ControlFlowNode for a | +| Method(Function C.n, C()) | arg1 | ControlFlowNode for arg1 | +| Method(Function C.n, class C) | arg1 | ControlFlowNode for arg1 | +| Method(Function f, C()) | arg1 | ControlFlowNode for arg1 | +| Method(Function f, C()) | arg2 | ControlFlowNode for arg2 | diff --git a/python/ql/test/library-tests/PointsTo/calls/getParameterByName.ql b/python/ql/test/library-tests/PointsTo/calls/getParameterByName.ql new file mode 100644 index 00000000000..d4766b680f7 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/calls/getParameterByName.ql @@ -0,0 +1,4 @@ +import python + +from CallableValue callable, string name +select callable.toString(), name, callable.getParameterByName(name).toString() diff --git a/python/ql/test/library-tests/PointsTo/calls/test.py b/python/ql/test/library-tests/PointsTo/calls/test.py index 38667a4a6e1..449f7fe49fc 100644 --- a/python/ql/test/library-tests/PointsTo/calls/test.py +++ b/python/ql/test/library-tests/PointsTo/calls/test.py @@ -32,3 +32,24 @@ class D(object): D.foo(1) D().foo(2) + +l = [1,2,3] +l.append(4) +len(l) + +f(arg0=0, arg1=1, arg2=2) +c = C() +c.n(arg1=1) + +# positional/keyword arguments for a builtin function +open("foo.txt", "rb") # TODO: Not handled by getNamedArgumentForCall +open(file="foo.txt", mode="rb") + +# Testing how arguments to *args and **kwargs are handled +def foo(a, *args): + pass +foo(1, 2, 3) + +def bar(a, **kwargs): + pass +bar(a=1, b=2, c=3) diff --git a/python/ql/test/library-tests/examples/custom-sanitizer/Taint.qll b/python/ql/test/library-tests/examples/custom-sanitizer/Taint.qll index 9b2216dbcd9..64cbacae2a6 100644 --- a/python/ql/test/library-tests/examples/custom-sanitizer/Taint.qll +++ b/python/ql/test/library-tests/examples/custom-sanitizer/Taint.qll @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Untrusted class SimpleSource extends TaintSource { diff --git a/python/ql/test/library-tests/examples/custom-sanitizer/TestTaint.ql b/python/ql/test/library-tests/examples/custom-sanitizer/TestTaint.ql index 4df37b9cdfa..571672cb312 100644 --- a/python/ql/test/library-tests/examples/custom-sanitizer/TestTaint.ql +++ b/python/ql/test/library-tests/examples/custom-sanitizer/TestTaint.ql @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import Taint from diff --git a/python/ql/test/library-tests/taint/collections/Taint.qll b/python/ql/test/library-tests/taint/collections/Taint.qll index b97f65225f2..21e16aabac5 100644 --- a/python/ql/test/library-tests/taint/collections/Taint.qll +++ b/python/ql/test/library-tests/taint/collections/Taint.qll @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Untrusted class SimpleSource extends TaintSource { diff --git a/python/ql/test/library-tests/taint/collections/TestStep.ql b/python/ql/test/library-tests/taint/collections/TestStep.ql index e7c014f2eb2..7e42b878e74 100644 --- a/python/ql/test/library-tests/taint/collections/TestStep.ql +++ b/python/ql/test/library-tests/taint/collections/TestStep.ql @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import Taint from TaintedNode n, TaintedNode s diff --git a/python/ql/test/library-tests/taint/collections/TestTaint.ql b/python/ql/test/library-tests/taint/collections/TestTaint.ql index 8347bd25433..fb1d102aa7a 100644 --- a/python/ql/test/library-tests/taint/collections/TestTaint.ql +++ b/python/ql/test/library-tests/taint/collections/TestTaint.ql @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import Taint from Call call, Expr arg, string taint_string diff --git a/python/ql/test/library-tests/taint/config/RockPaperScissors.ql b/python/ql/test/library-tests/taint/config/RockPaperScissors.ql index 311039a6553..abcc862f418 100644 --- a/python/ql/test/library-tests/taint/config/RockPaperScissors.ql +++ b/python/ql/test/library-tests/taint/config/RockPaperScissors.ql @@ -3,7 +3,7 @@ */ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import TaintLib import semmle.python.security.Paths diff --git a/python/ql/test/library-tests/taint/config/Simple.ql b/python/ql/test/library-tests/taint/config/Simple.ql index 76e8c261048..b3593354f5e 100644 --- a/python/ql/test/library-tests/taint/config/Simple.ql +++ b/python/ql/test/library-tests/taint/config/Simple.ql @@ -3,7 +3,7 @@ */ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import TaintLib import semmle.python.security.Paths diff --git a/python/ql/test/library-tests/taint/config/TaintLib.qll b/python/ql/test/library-tests/taint/config/TaintLib.qll index 670a9515c33..52e7c71858b 100644 --- a/python/ql/test/library-tests/taint/config/TaintLib.qll +++ b/python/ql/test/library-tests/taint/config/TaintLib.qll @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking class SimpleTest extends TaintKind { SimpleTest() { this = "simple.test" } diff --git a/python/ql/test/library-tests/taint/config/TaintedArgument.ql b/python/ql/test/library-tests/taint/config/TaintedArgument.ql index ca351d878a5..0663fce65e1 100644 --- a/python/ql/test/library-tests/taint/config/TaintedArgument.ql +++ b/python/ql/test/library-tests/taint/config/TaintedArgument.ql @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import TaintLib import semmle.python.dataflow.Implementation diff --git a/python/ql/test/library-tests/taint/config/TestNode.ql b/python/ql/test/library-tests/taint/config/TestNode.ql index 02a4dd278c3..688002f3eb0 100644 --- a/python/ql/test/library-tests/taint/config/TestNode.ql +++ b/python/ql/test/library-tests/taint/config/TestNode.ql @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.dataflow.Implementation import TaintLib diff --git a/python/ql/test/library-tests/taint/config/TestSink.ql b/python/ql/test/library-tests/taint/config/TestSink.ql index 4df3f48b939..0e191e16e84 100644 --- a/python/ql/test/library-tests/taint/config/TestSink.ql +++ b/python/ql/test/library-tests/taint/config/TestSink.ql @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import TaintLib from TestConfig config, DataFlow::Node sink, TaintKind kind diff --git a/python/ql/test/library-tests/taint/config/TestSource.ql b/python/ql/test/library-tests/taint/config/TestSource.ql index 191583becb7..45c5dd3ac57 100644 --- a/python/ql/test/library-tests/taint/config/TestSource.ql +++ b/python/ql/test/library-tests/taint/config/TestSource.ql @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import TaintLib from TestConfig config, DataFlow::Node source, TaintKind kind diff --git a/python/ql/test/library-tests/taint/config/TestStep.ql b/python/ql/test/library-tests/taint/config/TestStep.ql index f16f2e36bb8..2773321d300 100644 --- a/python/ql/test/library-tests/taint/config/TestStep.ql +++ b/python/ql/test/library-tests/taint/config/TestStep.ql @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import TaintLib import semmle.python.dataflow.Implementation diff --git a/python/ql/test/library-tests/taint/example/Edges.ql b/python/ql/test/library-tests/taint/example/Edges.ql index 0674f3a073c..063f4883316 100644 --- a/python/ql/test/library-tests/taint/example/Edges.ql +++ b/python/ql/test/library-tests/taint/example/Edges.ql @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.dataflow.Implementation import DilbertConfig diff --git a/python/ql/test/library-tests/taint/example/Nodes.ql b/python/ql/test/library-tests/taint/example/Nodes.ql index 0a5ff02c2a3..c7544767bba 100644 --- a/python/ql/test/library-tests/taint/example/Nodes.ql +++ b/python/ql/test/library-tests/taint/example/Nodes.ql @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.dataflow.Implementation import DilbertConfig diff --git a/python/ql/test/library-tests/taint/extensions/ExtensionsLib.qll b/python/ql/test/library-tests/taint/extensions/ExtensionsLib.qll index bc0534df455..19e369412ac 100644 --- a/python/ql/test/library-tests/taint/extensions/ExtensionsLib.qll +++ b/python/ql/test/library-tests/taint/extensions/ExtensionsLib.qll @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking class SimpleTest extends TaintKind { SimpleTest() { this = "simple.test" } diff --git a/python/ql/test/library-tests/taint/flowpath_regression/Config.qll b/python/ql/test/library-tests/taint/flowpath_regression/Config.qll index 0e3d5a71f8f..446365b2d12 100644 --- a/python/ql/test/library-tests/taint/flowpath_regression/Config.qll +++ b/python/ql/test/library-tests/taint/flowpath_regression/Config.qll @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Untrusted class FooSource extends TaintSource { diff --git a/python/ql/test/library-tests/taint/general/ParamSource.ql b/python/ql/test/library-tests/taint/general/ParamSource.ql index f0956d0333d..192de466882 100644 --- a/python/ql/test/library-tests/taint/general/ParamSource.ql +++ b/python/ql/test/library-tests/taint/general/ParamSource.ql @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking /* Standard library sink */ import semmle.python.security.injection.Command diff --git a/python/ql/test/library-tests/taint/general/TaintLib.qll b/python/ql/test/library-tests/taint/general/TaintLib.qll index 9e4cbc31a89..d0e8b9902ec 100644 --- a/python/ql/test/library-tests/taint/general/TaintLib.qll +++ b/python/ql/test/library-tests/taint/general/TaintLib.qll @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking class SimpleTest extends TaintKind { SimpleTest() { this = "simple.test" } diff --git a/python/ql/test/library-tests/taint/general/TestSanitizers.ql b/python/ql/test/library-tests/taint/general/TestSanitizers.ql index cee31378f7d..97c48dfa8e5 100644 --- a/python/ql/test/library-tests/taint/general/TestSanitizers.ql +++ b/python/ql/test/library-tests/taint/general/TestSanitizers.ql @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import TaintLib from Sanitizer s, TaintKind taint, PyEdgeRefinement test diff --git a/python/ql/test/library-tests/taint/general/TestSink.ql b/python/ql/test/library-tests/taint/general/TestSink.ql index 422527fbee2..2405ee3af06 100644 --- a/python/ql/test/library-tests/taint/general/TestSink.ql +++ b/python/ql/test/library-tests/taint/general/TestSink.ql @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import TaintLib from TaintSource src, TaintSink sink, TaintKind srckind, TaintKind sinkkind diff --git a/python/ql/test/library-tests/taint/general/TestSource.ql b/python/ql/test/library-tests/taint/general/TestSource.ql index d71bab289e0..4a06025a1f0 100644 --- a/python/ql/test/library-tests/taint/general/TestSource.ql +++ b/python/ql/test/library-tests/taint/general/TestSource.ql @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import TaintLib from TaintSource src, TaintKind kind diff --git a/python/ql/test/library-tests/taint/general/TestStep.ql b/python/ql/test/library-tests/taint/general/TestStep.ql index c6de9cad361..5274cd0af44 100644 --- a/python/ql/test/library-tests/taint/general/TestStep.ql +++ b/python/ql/test/library-tests/taint/general/TestStep.ql @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import TaintLib from TaintedNode n, TaintedNode s diff --git a/python/ql/test/library-tests/taint/general/TestTaint.ql b/python/ql/test/library-tests/taint/general/TestTaint.ql index 904cbbbded9..7c513d7b52c 100644 --- a/python/ql/test/library-tests/taint/general/TestTaint.ql +++ b/python/ql/test/library-tests/taint/general/TestTaint.ql @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import TaintLib from Call call, Expr arg, string taint_string diff --git a/python/ql/test/library-tests/taint/namedtuple/Taint.qll b/python/ql/test/library-tests/taint/namedtuple/Taint.qll index 580ed13f8f1..bb40491c202 100644 --- a/python/ql/test/library-tests/taint/namedtuple/Taint.qll +++ b/python/ql/test/library-tests/taint/namedtuple/Taint.qll @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Untrusted class SimpleSource extends TaintSource { diff --git a/python/ql/test/library-tests/taint/namedtuple/TestTaint.ql b/python/ql/test/library-tests/taint/namedtuple/TestTaint.ql index 8347bd25433..fb1d102aa7a 100644 --- a/python/ql/test/library-tests/taint/namedtuple/TestTaint.ql +++ b/python/ql/test/library-tests/taint/namedtuple/TestTaint.ql @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import Taint from Call call, Expr arg, string taint_string diff --git a/python/ql/test/library-tests/taint/strings/Taint.qll b/python/ql/test/library-tests/taint/strings/Taint.qll index 62dba92a45d..3840df662ef 100644 --- a/python/ql/test/library-tests/taint/strings/Taint.qll +++ b/python/ql/test/library-tests/taint/strings/Taint.qll @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Untrusted import semmle.python.security.Exceptions diff --git a/python/ql/test/library-tests/taint/strings/TestStep.ql b/python/ql/test/library-tests/taint/strings/TestStep.ql index e7c014f2eb2..7e42b878e74 100644 --- a/python/ql/test/library-tests/taint/strings/TestStep.ql +++ b/python/ql/test/library-tests/taint/strings/TestStep.ql @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import Taint from TaintedNode n, TaintedNode s diff --git a/python/ql/test/library-tests/taint/strings/TestTaint.ql b/python/ql/test/library-tests/taint/strings/TestTaint.ql index 8347bd25433..fb1d102aa7a 100644 --- a/python/ql/test/library-tests/taint/strings/TestTaint.ql +++ b/python/ql/test/library-tests/taint/strings/TestTaint.ql @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import Taint from Call call, Expr arg, string taint_string diff --git a/python/ql/test/library-tests/taint/unpacking/Taint.qll b/python/ql/test/library-tests/taint/unpacking/Taint.qll index b97f65225f2..21e16aabac5 100644 --- a/python/ql/test/library-tests/taint/unpacking/Taint.qll +++ b/python/ql/test/library-tests/taint/unpacking/Taint.qll @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.security.strings.Untrusted class SimpleSource extends TaintSource { diff --git a/python/ql/test/library-tests/taint/unpacking/TestStep.ql b/python/ql/test/library-tests/taint/unpacking/TestStep.ql index e7c014f2eb2..7e42b878e74 100644 --- a/python/ql/test/library-tests/taint/unpacking/TestStep.ql +++ b/python/ql/test/library-tests/taint/unpacking/TestStep.ql @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import Taint from TaintedNode n, TaintedNode s diff --git a/python/ql/test/library-tests/taint/unpacking/TestTaint.ql b/python/ql/test/library-tests/taint/unpacking/TestTaint.ql index 8347bd25433..fb1d102aa7a 100644 --- a/python/ql/test/library-tests/taint/unpacking/TestTaint.ql +++ b/python/ql/test/library-tests/taint/unpacking/TestTaint.ql @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import Taint from Call call, Expr arg, string taint_string diff --git a/python/ql/test/library-tests/web/stdlib/TestTaint.ql b/python/ql/test/library-tests/web/stdlib/TestTaint.ql index 87133eda869..1ac84c3d290 100644 --- a/python/ql/test/library-tests/web/stdlib/TestTaint.ql +++ b/python/ql/test/library-tests/web/stdlib/TestTaint.ql @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import semmle.python.web.HttpRequest import semmle.python.security.strings.Untrusted diff --git a/python/ql/test/query-tests/Classes/equals-hash/DefineEqualsWhenAddingFields.expected b/python/ql/test/query-tests/Classes/equals-hash/DefineEqualsWhenAddingFields.expected index dcdb8992b18..2f5a5a249f5 100644 --- a/python/ql/test/query-tests/Classes/equals-hash/DefineEqualsWhenAddingFields.expected +++ b/python/ql/test/query-tests/Classes/equals-hash/DefineEqualsWhenAddingFields.expected @@ -1 +1 @@ -| attr_eq_test.py:21:1:21:27 | class BadColorPoint | The class 'BadColorPoint' does not override $@, but adds the new attribute $@. | attr_eq_test.py:10:5:10:28 | Function __eq__ | '__eq__' | attr_eq_test.py:25:9:25:19 | Attribute | _color | +| attr_eq_test.py:21:1:21:27 | class BadColorPoint | The class 'BadColorPoint' does not override $@, but adds the new attribute $@. | attr_eq_test.py:10:5:10:28 | Function Point.__eq__ | '__eq__' | attr_eq_test.py:25:9:25:19 | Attribute | _color | diff --git a/python/ql/test/query-tests/Security/CWE-327/TestNode.ql b/python/ql/test/query-tests/Security/CWE-327/TestNode.ql index 50305f21a2e..420ed8bb38e 100644 --- a/python/ql/test/query-tests/Security/CWE-327/TestNode.ql +++ b/python/ql/test/query-tests/Security/CWE-327/TestNode.ql @@ -1,5 +1,5 @@ import python -import semmle.python.security.TaintTracking +import semmle.python.dataflow.TaintTracking import python import semmle.python.security.SensitiveData import semmle.python.security.Crypto