mirror of
https://github.com/github/codeql.git
synced 2025-12-17 01:03:14 +01:00
Merge branch 'master' of github.com:github/codeql into OptionalSanitizer
This commit is contained in:
@@ -16,6 +16,7 @@ The following changes in version 1.25 affect C/C++ analysis in all applications.
|
|||||||
|
|
||||||
## Changes to libraries
|
## 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
|
* 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.
|
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
|
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.
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,10 @@
|
|||||||
## General improvements
|
## General improvements
|
||||||
|
|
||||||
* Support for the following frameworks and libraries has been improved:
|
* 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/)
|
- [bluebird](http://bluebirdjs.com/)
|
||||||
- [express](https://www.npmjs.com/package/express)
|
- [express](https://www.npmjs.com/package/express)
|
||||||
|
- [fastify](https://www.npmjs.com/package/fastify)
|
||||||
- [fstream](https://www.npmjs.com/package/fstream)
|
- [fstream](https://www.npmjs.com/package/fstream)
|
||||||
- [jGrowl](https://github.com/stanlemon/jGrowl)
|
- [jGrowl](https://github.com/stanlemon/jGrowl)
|
||||||
- [jQuery](https://jquery.com/)
|
- [jQuery](https://jquery.com/)
|
||||||
@@ -13,12 +15,11 @@
|
|||||||
- [mssql](https://www.npmjs.com/package/mssql)
|
- [mssql](https://www.npmjs.com/package/mssql)
|
||||||
- [mysql](https://www.npmjs.com/package/mysql)
|
- [mysql](https://www.npmjs.com/package/mysql)
|
||||||
- [pg](https://www.npmjs.com/package/pg)
|
- [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)
|
- [sequelize](https://www.npmjs.com/package/sequelize)
|
||||||
- [spanner](https://www.npmjs.com/package/spanner)
|
- [spanner](https://www.npmjs.com/package/spanner)
|
||||||
- [sqlite](https://www.npmjs.com/package/sqlite)
|
- [sqlite](https://www.npmjs.com/package/sqlite)
|
||||||
- [ssh2](https://www.npmjs.com/package/ssh2)
|
|
||||||
- [ssh2-streams](https://www.npmjs.com/package/ssh2-streams)
|
- [ssh2-streams](https://www.npmjs.com/package/ssh2-streams)
|
||||||
|
- [ssh2](https://www.npmjs.com/package/ssh2)
|
||||||
|
|
||||||
* TypeScript 3.9 is now supported.
|
* TypeScript 3.9 is now supported.
|
||||||
|
|
||||||
@@ -35,42 +36,43 @@
|
|||||||
|
|
||||||
| **Query** | **Expected impact** | **Change** |
|
| **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. |
|
|
||||||
| Code injection (`js/code-injection`) | More results | More potential vulnerabilities involving NoSQL code operators are now recognized. |
|
|
||||||
| 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. |
|
|
||||||
| Client-side cross-site scripting (`js/xss`) | Less results | This query no longer flags optionally sanitized values. |
|
| Client-side cross-site scripting (`js/xss`) | Less 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 strings based on URLs. |
|
||||||
|
| 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. |
|
||||||
|
|
||||||
The following low-precision queries are no longer run by default on LGTM (their results already were not displayed):
|
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/dead-event-listener`
|
||||||
- `js/angular/unused-dependency`
|
- `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/bitwise-sign-check`
|
||||||
- `js/comparison-of-identical-expressions`
|
- `js/comparison-of-identical-expressions`
|
||||||
- `js/misspelled-identifier`
|
- `js/conflicting-html-attribute`
|
||||||
- `js/jsdoc/malformed-param-tag`
|
|
||||||
- `js/jsdoc/unknown-parameter`
|
|
||||||
- `js/jsdoc/missing-parameter`
|
|
||||||
- `js/omitted-array-element`
|
|
||||||
- `js/ignored-setter-parameter`
|
- `js/ignored-setter-parameter`
|
||||||
|
- `js/jsdoc/malformed-param-tag`
|
||||||
|
- `js/jsdoc/missing-parameter`
|
||||||
|
- `js/jsdoc/unknown-parameter`
|
||||||
- `js/json-in-javascript-file`
|
- `js/json-in-javascript-file`
|
||||||
|
- `js/misspelled-identifier`
|
||||||
|
- `js/nested-loops-with-same-variable`
|
||||||
- `js/node/cyclic-import`
|
- `js/node/cyclic-import`
|
||||||
- `js/node/unused-npm-dependency`
|
- `js/node/unused-npm-dependency`
|
||||||
- `js/single-run-loop`
|
- `js/omitted-array-element`
|
||||||
- `js/nested-loops-with-same-variable`
|
|
||||||
- `js/return-outside-function`
|
- `js/return-outside-function`
|
||||||
|
- `js/single-run-loop`
|
||||||
|
- `js/too-many-parameters`
|
||||||
|
- `js/unused-property`
|
||||||
|
- `js/useless-assignment-to-global`
|
||||||
|
|
||||||
## Changes to libraries
|
## Changes to libraries
|
||||||
|
|
||||||
@@ -80,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.
|
- `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.
|
- `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.
|
- `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".
|
||||||
|
|||||||
@@ -6,29 +6,50 @@
|
|||||||
|
|
||||||
import cpp
|
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) {
|
private predicate hasZeroParamDecl(Function f) {
|
||||||
exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() |
|
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) {
|
private predicate isCompiledAsC(File f) {
|
||||||
f.compiledAsC()
|
f.compiledAsC()
|
||||||
or
|
or
|
||||||
exists(File src | isCompiledAsC(src) | src.getAnIncludedFile() = f)
|
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) {
|
predicate tooFewArguments(FunctionCall fc, Function f) {
|
||||||
f = fc.getTarget() and
|
f = fc.getTarget() and
|
||||||
not f.isVarargs() and
|
not f.isVarargs() and
|
||||||
not f instanceof BuiltInFunction 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
|
hasZeroParamDecl(f) and
|
||||||
isCompiledAsC(f.getFile()) and
|
isCompiledAsC(f.getFile()) and
|
||||||
// There is an explicit declaration of the function whose parameter count is larger
|
// Produce an alert when all declarations that are authoritative on the
|
||||||
// than the number of call arguments
|
// parameter count specify a parameter count larger than the number of call
|
||||||
exists(FunctionDeclarationEntry fde | fde = f.getADeclarationEntry() |
|
// arguments.
|
||||||
|
forex(FunctionDeclarationEntry fde |
|
||||||
|
fde = f.getADeclarationEntry() and
|
||||||
|
hasDefiniteNumberOfParameters(fde)
|
||||||
|
|
|
||||||
fde.getNumberOfParameters() > fc.getNumberOfArguments()
|
fde.getNumberOfParameters() > fc.getNumberOfArguments()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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)
|
|
||||||
@@ -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))
|
|
||||||
@@ -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)
|
|
||||||
@@ -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)
|
|
||||||
@@ -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)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -1 +1,7 @@
|
|||||||
|
/**
|
||||||
|
* DEPRECATED: use `import cpp` instead of `import default`.
|
||||||
|
*
|
||||||
|
* Provides classes and predicates for working with C/C++ code.
|
||||||
|
*/
|
||||||
|
|
||||||
import cpp
|
import cpp
|
||||||
|
|||||||
65
cpp/ql/src/external/CodeDuplication.qll
vendored
65
cpp/ql/src/external/CodeDuplication.qll
vendored
@@ -1,3 +1,5 @@
|
|||||||
|
/** Provides classes for detecting duplicate or similar code. */
|
||||||
|
|
||||||
import cpp
|
import cpp
|
||||||
|
|
||||||
private string relativePath(File file) { result = file.getRelativePath().replaceAll("\\", "/") }
|
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)
|
tokens(copy, index, sl, sc, ec, el)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** A token block used for detection of duplicate and similar code. */
|
||||||
class Copy extends @duplication_or_similarity {
|
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) }
|
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) {
|
int tokenStartingAt(Location loc) {
|
||||||
exists(string filepath, int startline, int startcol |
|
exists(string filepath, int startline, int startcol |
|
||||||
loc.hasLocationInfo(filepath, startline, startcol, _, _) and
|
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) {
|
int tokenEndingAt(Location loc) {
|
||||||
exists(string filepath, int endline, int endcol |
|
exists(string filepath, int endline, int endcol |
|
||||||
loc.hasLocationInfo(filepath, _, _, endline, endcol) and
|
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, _, _, _) }
|
int sourceStartLine() { tokens(this, 0, result, _, _, _) }
|
||||||
|
|
||||||
|
/** Gets the column on which the first token in this block starts. */
|
||||||
int sourceStartColumn() { tokens(this, 0, _, result, _, _) }
|
int sourceStartColumn() { tokens(this, 0, _, result, _, _) }
|
||||||
|
|
||||||
|
/** Gets the line on which the last token in this block ends. */
|
||||||
int sourceEndLine() { tokens(this, lastToken(), _, _, result, _) }
|
int sourceEndLine() { tokens(this, lastToken(), _, _, result, _) }
|
||||||
|
|
||||||
|
/** Gets the column on which the last token in this block ends. */
|
||||||
int sourceEndColumn() { tokens(this, lastToken(), _, _, _, result) }
|
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() }
|
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) }
|
int getEquivalenceClass() { duplicateCode(this, _, result) or similarCode(this, _, result) }
|
||||||
|
|
||||||
|
/** Gets the source file in which this block appears. */
|
||||||
File sourceFile() {
|
File sourceFile() {
|
||||||
exists(string name | duplicateCode(this, name, _) or similarCode(this, name, _) |
|
exists(string name | duplicateCode(this, name, _) or similarCode(this, name, _) |
|
||||||
name.replaceAll("\\", "/") = relativePath(result)
|
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(
|
predicate hasLocationInfo(
|
||||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||||
) {
|
) {
|
||||||
@@ -53,25 +73,30 @@ class Copy extends @duplication_or_similarity {
|
|||||||
endcolumn = sourceEndColumn()
|
endcolumn = sourceEndColumn()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Gets a textual representation of this element. */
|
||||||
string toString() { none() }
|
string toString() { none() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** A block of duplicated code. */
|
||||||
class DuplicateBlock extends Copy, @duplication {
|
class DuplicateBlock extends Copy, @duplication {
|
||||||
override string toString() { result = "Duplicate code: " + sourceLines() + " duplicated lines." }
|
override string toString() { result = "Duplicate code: " + sourceLines() + " duplicated lines." }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** A block of similar code. */
|
||||||
class SimilarBlock extends Copy, @similarity {
|
class SimilarBlock extends Copy, @similarity {
|
||||||
override string toString() {
|
override string toString() {
|
||||||
result = "Similar code: " + sourceLines() + " almost duplicated lines."
|
result = "Similar code: " + sourceLines() + " almost duplicated lines."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Gets a function with a body and a location. */
|
||||||
FunctionDeclarationEntry sourceMethod() {
|
FunctionDeclarationEntry sourceMethod() {
|
||||||
result.isDefinition() and
|
result.isDefinition() and
|
||||||
exists(result.getLocation()) and
|
exists(result.getLocation()) and
|
||||||
numlines(unresolveElement(result.getFunction()), _, _, _)
|
numlines(unresolveElement(result.getFunction()), _, _, _)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Gets the number of member functions in `c` with a body and a location. */
|
||||||
int numberOfSourceMethods(Class c) {
|
int numberOfSourceMethods(Class c) {
|
||||||
result =
|
result =
|
||||||
count(FunctionDeclarationEntry m |
|
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(
|
predicate duplicateStatements(
|
||||||
FunctionDeclarationEntry m1, FunctionDeclarationEntry m2, int duplicate, int total
|
FunctionDeclarationEntry m1, FunctionDeclarationEntry m2, int duplicate, int total
|
||||||
) {
|
) {
|
||||||
@@ -115,13 +144,16 @@ predicate duplicateStatements(
|
|||||||
total = strictcount(statementInMethod(m1))
|
total = strictcount(statementInMethod(m1))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Holds if `m` and other are identical functions. */
|
||||||
* Find pairs of methods are identical
|
|
||||||
*/
|
|
||||||
predicate duplicateMethod(FunctionDeclarationEntry m, FunctionDeclarationEntry other) {
|
predicate duplicateMethod(FunctionDeclarationEntry m, FunctionDeclarationEntry other) {
|
||||||
exists(int total | duplicateStatements(m, other, total, total))
|
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) {
|
predicate similarLines(File f, int line) {
|
||||||
exists(SimilarBlock b | b.sourceFile() = f and line in [b.sourceStartLine() .. b.sourceEndLine()])
|
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) {
|
predicate similarLinesCovered(File f, int coveredLines, File otherFile) {
|
||||||
exists(int numLines | numLines = f.getMetrics().getNumberOfLines() |
|
exists(int numLines | numLines = f.getMetrics().getNumberOfLines() |
|
||||||
similarLinesCoveredFiles(f, otherFile) and
|
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) {
|
predicate duplicateLines(File f, int line) {
|
||||||
exists(DuplicateBlock b |
|
exists(DuplicateBlock b |
|
||||||
b.sourceFile() = f and line in [b.sourceStartLine() .. b.sourceEndLine()]
|
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) {
|
predicate duplicateLinesCovered(File f, int coveredLines, File otherFile) {
|
||||||
exists(int numLines | numLines = f.getMetrics().getNumberOfLines() |
|
exists(int numLines | numLines = f.getMetrics().getNumberOfLines() |
|
||||||
exists(int coveredApprox |
|
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) {
|
predicate similarFiles(File f, File other, int percent) {
|
||||||
exists(int covered, int total |
|
exists(int covered, int total |
|
||||||
similarLinesCovered(f, covered, other) and
|
similarLinesCovered(f, covered, other) and
|
||||||
@@ -216,6 +256,7 @@ predicate similarFiles(File f, File other, int percent) {
|
|||||||
not duplicateFiles(f, other, _)
|
not duplicateFiles(f, other, _)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Holds if most of `f` (`percent`%) is duplicated by `other`. */
|
||||||
predicate duplicateFiles(File f, File other, int percent) {
|
predicate duplicateFiles(File f, File other, int percent) {
|
||||||
exists(int covered, int total |
|
exists(int covered, int total |
|
||||||
duplicateLinesCovered(f, covered, other) and
|
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) {
|
predicate mostlyDuplicateClassBase(Class c, Class other, int numDup, int total) {
|
||||||
numDup =
|
numDup =
|
||||||
strictcount(FunctionDeclarationEntry m1 |
|
strictcount(FunctionDeclarationEntry m1 |
|
||||||
@@ -240,6 +285,11 @@ predicate mostlyDuplicateClassBase(Class c, Class other, int numDup, int total)
|
|||||||
(numDup * 100) / total > 80
|
(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) {
|
predicate mostlyDuplicateClass(Class c, Class other, string message) {
|
||||||
exists(int numDup, int total |
|
exists(int numDup, int total |
|
||||||
mostlyDuplicateClassBase(c, other, numDup, total) and
|
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) {
|
predicate fileLevelDuplication(File f, File other) {
|
||||||
similarFiles(f, other, _) or duplicateFiles(f, 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, _) }
|
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) {
|
predicate whitelistedLineForDuplication(File f, int line) {
|
||||||
exists(Include i | i.getFile() = f and i.getLocation().getStartLine() = line)
|
exists(Include i | i.getFile() = f and i.getLocation().getStartLine() = line)
|
||||||
}
|
}
|
||||||
|
|||||||
92
cpp/ql/src/external/VCS.qll
vendored
92
cpp/ql/src/external/VCS.qll
vendored
@@ -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 }
|
|
||||||
20
cpp/ql/src/external/tests/DefectFromSVN.ql
vendored
20
cpp/ql/src/external/tests/DefectFromSVN.ql
vendored
@@ -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."
|
|
||||||
17
cpp/ql/src/external/tests/MetricFromSVN.ql
vendored
17
cpp/ql/src/external/tests/MetricFromSVN.ql
vendored
@@ -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
|
|
||||||
@@ -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()
|
|
||||||
@@ -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()
|
|
||||||
@@ -1 +1,7 @@
|
|||||||
|
/**
|
||||||
|
* DEPRECATED: Objective C is no longer supported.
|
||||||
|
*
|
||||||
|
* Import `cpp` instead of `objc`.
|
||||||
|
*/
|
||||||
|
|
||||||
import cpp
|
import cpp
|
||||||
|
|||||||
@@ -98,7 +98,12 @@ class Declaration extends Locatable, @declaration {
|
|||||||
this.hasQualifiedName(namespaceQualifier, "", baseName)
|
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.
|
* Gets the name of this declaration.
|
||||||
|
|||||||
@@ -79,7 +79,10 @@ class Namespace extends NameQualifyingElement, @namespace {
|
|||||||
/** Gets the metric namespace. */
|
/** Gets the metric namespace. */
|
||||||
MetricNamespace getMetrics() { result = this }
|
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. */
|
/** Gets a declaration of (part of) this namespace. */
|
||||||
NamespaceDeclarationEntry getADeclarationEntry() { result.getNamespace() = this }
|
NamespaceDeclarationEntry getADeclarationEntry() { result.getNamespace() = this }
|
||||||
@@ -104,7 +107,7 @@ class NamespaceDeclarationEntry extends Locatable, @namespace_decl {
|
|||||||
namespace_decls(underlyingElement(this), unresolveElement(result), _, _)
|
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
|
* 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), _) }
|
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), _) }
|
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() }
|
deprecated string getFullName() { result = this.getName() }
|
||||||
|
|
||||||
override string toString() { result = "(global namespace)" }
|
override string getFriendlyName() { result = "(global namespace)" }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -260,10 +260,8 @@ class ParameterDeclarationEntry extends VariableDeclarationEntry {
|
|||||||
*/
|
*/
|
||||||
int getIndex() { param_decl_bind(underlyingElement(this), result, _) }
|
int getIndex() { param_decl_bind(underlyingElement(this), result, _) }
|
||||||
|
|
||||||
override string toString() {
|
private string getAnonymousParameterDescription() {
|
||||||
if exists(getName())
|
not exists(getName()) and
|
||||||
then result = super.toString()
|
|
||||||
else
|
|
||||||
exists(string idx |
|
exists(string idx |
|
||||||
idx =
|
idx =
|
||||||
((getIndex() + 1).toString() + "th")
|
((getIndex() + 1).toString() + "th")
|
||||||
@@ -272,14 +270,25 @@ class ParameterDeclarationEntry extends VariableDeclarationEntry {
|
|||||||
.replaceAll("3th", "3rd")
|
.replaceAll("3th", "3rd")
|
||||||
.replaceAll("11st", "11th")
|
.replaceAll("11st", "11th")
|
||||||
.replaceAll("12nd", "12th")
|
.replaceAll("12nd", "12th")
|
||||||
.replaceAll("13rd", "13th")
|
.replaceAll("13rd", "13th") and
|
||||||
|
|
|
||||||
if exists(getCanonicalName())
|
if exists(getCanonicalName())
|
||||||
then result = "declaration of " + getCanonicalName() + " as anonymous " + idx + " parameter"
|
then result = "declaration of " + getCanonicalName() + " as anonymous " + idx + " parameter"
|
||||||
else result = "declaration of " + idx + " parameter"
|
else result = "declaration of " + idx + " parameter"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override string toString() {
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the name of this `ParameterDeclarationEntry` including it's type.
|
* Gets the name of this `ParameterDeclarationEntry` including it's type.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ class XMLFile extends XMLParent, File {
|
|||||||
XMLFile() { xmlEncoding(this, _) }
|
XMLFile() { xmlEncoding(this, _) }
|
||||||
|
|
||||||
/** Gets a printable representation of this XML file. */
|
/** 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. */
|
/** Gets the name of this XML file. */
|
||||||
override string getName() { result = File.super.getAbsolutePath() }
|
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() }
|
string getAttributeValue(string name) { result = this.getAttribute(name).getValue() }
|
||||||
|
|
||||||
/** Gets a printable representation of this XML element. */
|
/** Gets a printable representation of this XML element. */
|
||||||
override string toString() { result = XMLParent.super.toString() }
|
override string toString() { result = getName() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -66,9 +66,6 @@ abstract class Configuration extends string {
|
|||||||
*/
|
*/
|
||||||
predicate isBarrier(Node node) { none() }
|
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. */
|
/** Holds if data flow into `node` is prohibited. */
|
||||||
predicate isBarrierIn(Node node) { none() }
|
predicate isBarrierIn(Node node) { none() }
|
||||||
|
|
||||||
|
|||||||
@@ -66,9 +66,6 @@ abstract class Configuration extends string {
|
|||||||
*/
|
*/
|
||||||
predicate isBarrier(Node node) { none() }
|
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. */
|
/** Holds if data flow into `node` is prohibited. */
|
||||||
predicate isBarrierIn(Node node) { none() }
|
predicate isBarrierIn(Node node) { none() }
|
||||||
|
|
||||||
|
|||||||
@@ -66,9 +66,6 @@ abstract class Configuration extends string {
|
|||||||
*/
|
*/
|
||||||
predicate isBarrier(Node node) { none() }
|
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. */
|
/** Holds if data flow into `node` is prohibited. */
|
||||||
predicate isBarrierIn(Node node) { none() }
|
predicate isBarrierIn(Node node) { none() }
|
||||||
|
|
||||||
|
|||||||
@@ -66,9 +66,6 @@ abstract class Configuration extends string {
|
|||||||
*/
|
*/
|
||||||
predicate isBarrier(Node node) { none() }
|
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. */
|
/** Holds if data flow into `node` is prohibited. */
|
||||||
predicate isBarrierIn(Node node) { none() }
|
predicate isBarrierIn(Node node) { none() }
|
||||||
|
|
||||||
|
|||||||
@@ -66,9 +66,6 @@ abstract class Configuration extends string {
|
|||||||
*/
|
*/
|
||||||
predicate isBarrier(Node node) { none() }
|
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. */
|
/** Holds if data flow into `node` is prohibited. */
|
||||||
predicate isBarrierIn(Node node) { none() }
|
predicate isBarrierIn(Node node) { none() }
|
||||||
|
|
||||||
|
|||||||
@@ -79,13 +79,6 @@ abstract class Configuration extends DataFlow::Configuration {
|
|||||||
defaultTaintBarrier(node)
|
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. */
|
/** Holds if data flow into `node` is prohibited. */
|
||||||
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
||||||
|
|
||||||
|
|||||||
@@ -79,13 +79,6 @@ abstract class Configuration extends DataFlow::Configuration {
|
|||||||
defaultTaintBarrier(node)
|
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. */
|
/** Holds if data flow into `node` is prohibited. */
|
||||||
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
||||||
|
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ class Closure extends Class {
|
|||||||
result.getName() = "operator()"
|
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 {
|
class LambdaCapture extends Locatable, @lambdacapture {
|
||||||
override string toString() { result = getField().toString() }
|
override string toString() { result = getField().getName() }
|
||||||
|
|
||||||
override string getCanonicalQLClass() { result = "LambdaCapture" }
|
override string getCanonicalQLClass() { result = "LambdaCapture" }
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ private import semmle.code.cpp.ir.dataflow.DataFlow2
|
|||||||
private import semmle.code.cpp.ir.dataflow.DataFlow3
|
private import semmle.code.cpp.ir.dataflow.DataFlow3
|
||||||
private import semmle.code.cpp.ir.IR
|
private import semmle.code.cpp.ir.IR
|
||||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowDispatch as Dispatch
|
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.Taint
|
||||||
private import semmle.code.cpp.models.interfaces.DataFlow
|
private import semmle.code.cpp.models.interfaces.DataFlow
|
||||||
|
|
||||||
@@ -170,11 +171,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) {
|
private predicate nodeIsBarrier(DataFlow::Node node) {
|
||||||
exists(Variable checkedVar |
|
exists(Variable checkedVar |
|
||||||
readsVariable(node.asInstruction(), checkedVar) and
|
readsVariable(node.asInstruction(), checkedVar) and
|
||||||
hasUpperBoundsCheck(checkedVar)
|
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) {
|
private predicate nodeIsBarrierIn(DataFlow::Node node) {
|
||||||
|
|||||||
@@ -66,9 +66,6 @@ abstract class Configuration extends string {
|
|||||||
*/
|
*/
|
||||||
predicate isBarrier(Node node) { none() }
|
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. */
|
/** Holds if data flow into `node` is prohibited. */
|
||||||
predicate isBarrierIn(Node node) { none() }
|
predicate isBarrierIn(Node node) { none() }
|
||||||
|
|
||||||
|
|||||||
@@ -66,9 +66,6 @@ abstract class Configuration extends string {
|
|||||||
*/
|
*/
|
||||||
predicate isBarrier(Node node) { none() }
|
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. */
|
/** Holds if data flow into `node` is prohibited. */
|
||||||
predicate isBarrierIn(Node node) { none() }
|
predicate isBarrierIn(Node node) { none() }
|
||||||
|
|
||||||
|
|||||||
@@ -66,9 +66,6 @@ abstract class Configuration extends string {
|
|||||||
*/
|
*/
|
||||||
predicate isBarrier(Node node) { none() }
|
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. */
|
/** Holds if data flow into `node` is prohibited. */
|
||||||
predicate isBarrierIn(Node node) { none() }
|
predicate isBarrierIn(Node node) { none() }
|
||||||
|
|
||||||
|
|||||||
@@ -66,9 +66,6 @@ abstract class Configuration extends string {
|
|||||||
*/
|
*/
|
||||||
predicate isBarrier(Node node) { none() }
|
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. */
|
/** Holds if data flow into `node` is prohibited. */
|
||||||
predicate isBarrierIn(Node node) { none() }
|
predicate isBarrierIn(Node node) { none() }
|
||||||
|
|
||||||
|
|||||||
@@ -79,13 +79,6 @@ abstract class Configuration extends DataFlow::Configuration {
|
|||||||
defaultTaintBarrier(node)
|
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. */
|
/** Holds if data flow into `node` is prohibited. */
|
||||||
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
||||||
|
|
||||||
|
|||||||
@@ -79,13 +79,6 @@ abstract class Configuration extends DataFlow::Configuration {
|
|||||||
defaultTaintBarrier(node)
|
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. */
|
/** Holds if data flow into `node` is prohibited. */
|
||||||
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import default
|
import cpp
|
||||||
|
|
||||||
from Class c, string n
|
from Class c, string n
|
||||||
where n = count(Class x | x.getName() = c.getName()) + " distinct class(es) called " + c.getName()
|
where n = count(Class x | x.getName() = c.getName()) + " distinct class(es) called " + c.getName()
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import default
|
import cpp
|
||||||
import semmle.code.cpp.models.implementations.Allocation
|
import semmle.code.cpp.models.implementations.Allocation
|
||||||
|
|
||||||
query predicate newExprs(
|
query predicate newExprs(
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import default
|
import cpp
|
||||||
|
|
||||||
string getValueCategoryString(Expr expr) {
|
string getValueCategoryString(Expr expr) {
|
||||||
if expr.isLValueCategory()
|
if expr.isLValueCategory()
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import default
|
import cpp
|
||||||
import semmle.code.cpp.ir.IR
|
import semmle.code.cpp.ir.IR
|
||||||
import semmle.code.cpp.ir.implementation.aliased_ssa.constant.ConstantAnalysis
|
import semmle.code.cpp.ir.implementation.aliased_ssa.constant.ConstantAnalysis
|
||||||
import semmle.code.cpp.ir.internal.IntegerConstant
|
import semmle.code.cpp.ir.internal.IntegerConstant
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import default
|
import cpp
|
||||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.AliasAnalysis
|
import semmle.code.cpp.ir.implementation.unaliased_ssa.internal.AliasAnalysis
|
||||||
import semmle.code.cpp.ir.implementation.raw.IR
|
import semmle.code.cpp.ir.implementation.raw.IR
|
||||||
import semmle.code.cpp.ir.implementation.UseSoundEscapeAnalysis
|
import semmle.code.cpp.ir.implementation.UseSoundEscapeAnalysis
|
||||||
|
|||||||
@@ -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.AliasAnalysis
|
||||||
import semmle.code.cpp.ir.implementation.aliased_ssa.internal.AliasConfiguration
|
import semmle.code.cpp.ir.implementation.aliased_ssa.internal.AliasConfiguration
|
||||||
import semmle.code.cpp.ir.implementation.unaliased_ssa.IR
|
import semmle.code.cpp.ir.implementation.unaliased_ssa.IR
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import default
|
import cpp
|
||||||
|
|
||||||
query predicate classUuids(Class cls, string uuid) {
|
query predicate classUuids(Class cls, string uuid) {
|
||||||
if exists(cls.getUuid()) then uuid = cls.getUuid() else uuid = ""
|
if exists(cls.getUuid()) then uuid = cls.getUuid() else uuid = ""
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import cpp
|
|||||||
|
|
||||||
class FunctionMonkeyPatch extends Function {
|
class FunctionMonkeyPatch extends Function {
|
||||||
language[monotonicAggregates]
|
language[monotonicAggregates]
|
||||||
override string toString() {
|
override string getDescription() {
|
||||||
exists(string name, string templateArgs, string args |
|
exists(string name, string templateArgs, string args |
|
||||||
result = name + templateArgs + args and
|
result = name + templateArgs + args and
|
||||||
name = this.getQualifiedName() and
|
name = this.getQualifiedName() and
|
||||||
@@ -30,7 +30,9 @@ class FunctionMonkeyPatch extends Function {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ParameterMonkeyPatch extends Parameter {
|
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
|
from Element e, Element ti
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
| templates.cpp:9:5:9:14 | using c |
|
| templates.cpp:9:5:9:14 | using c | UsingDeclarationEntry, enclosingElement:std |
|
||||||
| usings.cpp:8:1:8:11 | using nf |
|
| usings.cpp:8:1:8:11 | using nf | UsingDeclarationEntry, enclosingElement:(global namespace) |
|
||||||
| usings.cpp:9:1:9:17 | using namespace N |
|
| usings.cpp:9:1:9:17 | using namespace N | UsingDirectiveEntry, enclosingElement:(global namespace) |
|
||||||
| usings.cpp:18:3:18:13 | using bf |
|
| usings.cpp:18:3:18:13 | using bf | UsingDeclarationEntry, enclosingElement:D |
|
||||||
| usings.cpp:21:5:21:14 | using gf |
|
| usings.cpp:21:5:21:14 | using gf | UsingDeclarationEntry, enclosingElement:{ ... } |
|
||||||
| usings.cpp:34:3:34:20 | using tbf |
|
| usings.cpp:34:3:34:20 | using tbf | UsingDeclarationEntry, enclosingElement:TD |
|
||||||
| usings.cpp:42:5:42:22 | using foo |
|
| usings.cpp:42:5:42:22 | using foo | UsingDeclarationEntry, enclosingElement:nsbar |
|
||||||
|
|||||||
@@ -1,4 +1,14 @@
|
|||||||
import cpp
|
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
|
from UsingEntry ue
|
||||||
select ue
|
select ue, concat(describe(ue), ", ")
|
||||||
|
|||||||
@@ -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 |
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
import cpp
|
|
||||||
|
|
||||||
from UsingEntry ue, Element e
|
|
||||||
where e = ue.getEnclosingElement()
|
|
||||||
select ue, e
|
|
||||||
@@ -68,6 +68,26 @@ edges
|
|||||||
| test.cpp:227:24:227:37 | (const char *)... | test.cpp:237:10:237:19 | (size_t)... |
|
| 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: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: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
|
nodes
|
||||||
| field_conflation.c:12:22:12:27 | call to getenv | semmle.label | call to getenv |
|
| 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:12:22:12:34 | (const char *)... | semmle.label | (const char *)... |
|
||||||
@@ -140,6 +160,32 @@ nodes
|
|||||||
| test.cpp:231:9:231:24 | call to get_tainted_size | semmle.label | call to get_tainted_size |
|
| 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:235:11:235:20 | (size_t)... | semmle.label | (size_t)... |
|
||||||
| test.cpp:237:10:237:19 | (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
|
#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) |
|
| 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: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) |
|
||||||
@@ -155,3 +201,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: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: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: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) |
|
||||||
|
|||||||
@@ -236,3 +236,81 @@ void more_cases() {
|
|||||||
my_func(100); // GOOD
|
my_func(100); // GOOD
|
||||||
my_func(local_size); // 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]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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: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: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: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 |
|
| 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 |
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ void test_guards3(int cond) {
|
|||||||
|
|
||||||
if (x != 0) return;
|
if (x != 0) return;
|
||||||
|
|
||||||
return x + 1; // GOOD [FALSE POSITIVE]
|
return x + 1; // GOOD
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_guards4(int cond) {
|
void test_guards4(int cond) {
|
||||||
|
|||||||
@@ -1160,26 +1160,6 @@ class UsingStmt extends Stmt, @using_stmt {
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
Expr getAnExpr() { none() }
|
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() {
|
override Expr getAnExpr() {
|
||||||
result = this.getAVariableDeclExpr().getInitializer()
|
result = this.getAVariableDeclExpr().getInitializer()
|
||||||
@@ -1221,7 +1201,7 @@ class UsingBlockStmt extends UsingStmt, @using_block_stmt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Gets the body of this `using` statement. */
|
/** Gets the body of this `using` statement. */
|
||||||
override Stmt getBody() { result.getParent() = this }
|
Stmt getBody() { result.getParent() = this }
|
||||||
|
|
||||||
override string toString() { result = "using (...) {...}" }
|
override string toString() { result = "using (...) {...}" }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ class XMLFile extends XMLParent, File {
|
|||||||
XMLFile() { xmlEncoding(this, _) }
|
XMLFile() { xmlEncoding(this, _) }
|
||||||
|
|
||||||
/** Gets a printable representation of this XML file. */
|
/** 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. */
|
/** Gets the name of this XML file. */
|
||||||
override string getName() { result = File.super.getAbsolutePath() }
|
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() }
|
string getAttributeValue(string name) { result = this.getAttribute(name).getValue() }
|
||||||
|
|
||||||
/** Gets a printable representation of this XML element. */
|
/** Gets a printable representation of this XML element. */
|
||||||
override string toString() { result = XMLParent.super.toString() }
|
override string toString() { result = getName() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -66,9 +66,6 @@ abstract class Configuration extends string {
|
|||||||
*/
|
*/
|
||||||
predicate isBarrier(Node node) { none() }
|
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. */
|
/** Holds if data flow into `node` is prohibited. */
|
||||||
predicate isBarrierIn(Node node) { none() }
|
predicate isBarrierIn(Node node) { none() }
|
||||||
|
|
||||||
|
|||||||
@@ -66,9 +66,6 @@ abstract class Configuration extends string {
|
|||||||
*/
|
*/
|
||||||
predicate isBarrier(Node node) { none() }
|
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. */
|
/** Holds if data flow into `node` is prohibited. */
|
||||||
predicate isBarrierIn(Node node) { none() }
|
predicate isBarrierIn(Node node) { none() }
|
||||||
|
|
||||||
|
|||||||
@@ -66,9 +66,6 @@ abstract class Configuration extends string {
|
|||||||
*/
|
*/
|
||||||
predicate isBarrier(Node node) { none() }
|
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. */
|
/** Holds if data flow into `node` is prohibited. */
|
||||||
predicate isBarrierIn(Node node) { none() }
|
predicate isBarrierIn(Node node) { none() }
|
||||||
|
|
||||||
|
|||||||
@@ -66,9 +66,6 @@ abstract class Configuration extends string {
|
|||||||
*/
|
*/
|
||||||
predicate isBarrier(Node node) { none() }
|
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. */
|
/** Holds if data flow into `node` is prohibited. */
|
||||||
predicate isBarrierIn(Node node) { none() }
|
predicate isBarrierIn(Node node) { none() }
|
||||||
|
|
||||||
|
|||||||
@@ -66,9 +66,6 @@ abstract class Configuration extends string {
|
|||||||
*/
|
*/
|
||||||
predicate isBarrier(Node node) { none() }
|
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. */
|
/** Holds if data flow into `node` is prohibited. */
|
||||||
predicate isBarrierIn(Node node) { none() }
|
predicate isBarrierIn(Node node) { none() }
|
||||||
|
|
||||||
|
|||||||
@@ -79,13 +79,6 @@ abstract class Configuration extends DataFlow::Configuration {
|
|||||||
defaultTaintBarrier(node)
|
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. */
|
/** Holds if data flow into `node` is prohibited. */
|
||||||
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
||||||
|
|
||||||
|
|||||||
@@ -79,13 +79,6 @@ abstract class Configuration extends DataFlow::Configuration {
|
|||||||
defaultTaintBarrier(node)
|
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. */
|
/** Holds if data flow into `node` is prohibited. */
|
||||||
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
||||||
|
|
||||||
|
|||||||
@@ -79,13 +79,6 @@ abstract class Configuration extends DataFlow::Configuration {
|
|||||||
defaultTaintBarrier(node)
|
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. */
|
/** Holds if data flow into `node` is prohibited. */
|
||||||
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
||||||
|
|
||||||
|
|||||||
@@ -79,13 +79,6 @@ abstract class Configuration extends DataFlow::Configuration {
|
|||||||
defaultTaintBarrier(node)
|
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. */
|
/** Holds if data flow into `node` is prohibited. */
|
||||||
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
||||||
|
|
||||||
|
|||||||
@@ -79,13 +79,6 @@ abstract class Configuration extends DataFlow::Configuration {
|
|||||||
defaultTaintBarrier(node)
|
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. */
|
/** Holds if data flow into `node` is prohibited. */
|
||||||
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
<!DOCTYPE qhelp PUBLIC
|
||||||
|
"-//Semmle//qhelp//EN"
|
||||||
|
"qhelp.dtd">
|
||||||
|
<qhelp>
|
||||||
|
|
||||||
|
<overview>
|
||||||
|
<p>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.</p>
|
||||||
|
</overview>
|
||||||
|
|
||||||
|
<recommendation>
|
||||||
|
<p>Verify the whole host and domain (FQDN) or check endsWith dot+domain.</p>
|
||||||
|
</recommendation>
|
||||||
|
|
||||||
|
<example>
|
||||||
|
<p>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.</p>
|
||||||
|
<sample src="IncorrectURLVerification.java" />
|
||||||
|
</example>
|
||||||
|
|
||||||
|
<references>
|
||||||
|
<li>
|
||||||
|
<a href="https://drive.google.com/file/d/0BwMN49Gzo3x6T1N5WGQ4TTNlMHBOb1ZRQTVEWnVBZjFUaE5N/view">Common Android app vulnerabilities from Sebastian Porst of Google</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="https://www.bugcrowd.com/resources/webinars/overview-of-common-android-app-vulnerabilities/">Common Android app vulnerabilities from bugcrowd</a>
|
||||||
|
</li>
|
||||||
|
</references>
|
||||||
|
</qhelp>
|
||||||
97
java/ql/src/experimental/CWE-939/IncorrectURLVerification.ql
Normal file
97
java/ql/src/experimental/CWE-939/IncorrectURLVerification.ql
Normal file
@@ -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, <resources>...<string name="host">example.com</string>...</resources> 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"
|
||||||
@@ -22,8 +22,7 @@ class TypeAuthorizedUrl extends Class {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The class
|
* The class `org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry`.
|
||||||
* `org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry`.
|
|
||||||
*/
|
*/
|
||||||
class TypeAbstractRequestMatcherRegistry extends Class {
|
class TypeAbstractRequestMatcherRegistry extends Class {
|
||||||
TypeAbstractRequestMatcherRegistry() {
|
TypeAbstractRequestMatcherRegistry() {
|
||||||
@@ -34,38 +33,44 @@ class TypeAbstractRequestMatcherRegistry extends Class {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The class
|
* The class `org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest`.
|
||||||
* `org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest.EndpointRequestMatcher`.
|
|
||||||
*/
|
*/
|
||||||
class TypeEndpointRequestMatcher extends Class {
|
class TypeEndpointRequest extends Class {
|
||||||
TypeEndpointRequestMatcher() {
|
TypeEndpointRequest() {
|
||||||
this
|
this
|
||||||
.hasQualifiedName("org.springframework.boot.actuate.autoconfigure.security.servlet",
|
.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
|
* A call to `HttpSecurity.requestMatcher` method with argument `RequestMatcher.toAnyEndpoint()`.
|
||||||
* `EndpointRequestMatcher`.
|
|
||||||
*/
|
*/
|
||||||
class RequestMatcherCall extends MethodAccess {
|
class RequestMatcherCall extends MethodAccess {
|
||||||
RequestMatcherCall() {
|
RequestMatcherCall() {
|
||||||
getMethod().hasName("requestMatcher") and
|
getMethod().hasName("requestMatcher") and
|
||||||
getMethod().getDeclaringType() instanceof TypeHttpSecurity 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
|
* A call to `HttpSecurity.requestMatchers` method with lambda argument
|
||||||
* `EndpointRequestMatcher` type.
|
* `RequestMatcher.toAnyEndpoint()`.
|
||||||
*/
|
*/
|
||||||
class RequestMatchersCall extends MethodAccess {
|
class RequestMatchersCall extends MethodAccess {
|
||||||
RequestMatchersCall() {
|
RequestMatchersCall() {
|
||||||
getMethod().hasName("requestMatchers") and
|
getMethod().hasName("requestMatchers") and
|
||||||
getMethod().getDeclaringType() instanceof TypeHttpSecurity 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
|
or
|
||||||
// .requestMatchers(matcher -> EndpointRequest).authorizeRequests([...]).[...]
|
// .requestMatchers(matcher -> EndpointRequest).authorizeRequests([...]).[...]
|
||||||
authorizeRequestsCall.getQualifier() instanceof RequestMatchersCall
|
authorizeRequestsCall.getQualifier() instanceof RequestMatchersCall
|
||||||
or
|
|
||||||
// http.authorizeRequests([...]).[...]
|
|
||||||
authorizeRequestsCall.getQualifier() instanceof VarAccess
|
|
||||||
|
|
|
|
||||||
// [...].authorizeRequests(r -> r.anyRequest().permitAll()) or
|
// [...].authorizeRequests(r -> r.anyRequest().permitAll()) or
|
||||||
// [...].authorizeRequests(r -> r.requestMatchers(EndpointRequest).permitAll())
|
// [...].authorizeRequests(r -> r.requestMatchers(EndpointRequest).permitAll())
|
||||||
@@ -117,6 +119,22 @@ class PermitAllCall extends MethodAccess {
|
|||||||
this.getQualifier() = anyRequestCall
|
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
|
* A call to `AbstractRequestMatcherRegistry.requestMatchers` method with an argument
|
||||||
* `EndpointRequestMatcher`.
|
* `RequestMatcher.toAnyEndpoint()`.
|
||||||
*/
|
*/
|
||||||
class RegistryRequestMatchersCall extends MethodAccess {
|
class RegistryRequestMatchersCall extends MethodAccess {
|
||||||
RegistryRequestMatchersCall() {
|
RegistryRequestMatchersCall() {
|
||||||
getMethod().hasName("requestMatchers") and
|
getMethod().hasName("requestMatchers") and
|
||||||
getMethod().getDeclaringType() instanceof TypeAbstractRequestMatcherRegistry and
|
getMethod().getDeclaringType() instanceof TypeAbstractRequestMatcherRegistry and
|
||||||
getAnArgument().getType() instanceof TypeEndpointRequestMatcher
|
getAnArgument() instanceof ToAnyEndpointCall
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,9 +66,6 @@ abstract class Configuration extends string {
|
|||||||
*/
|
*/
|
||||||
predicate isBarrier(Node node) { none() }
|
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. */
|
/** Holds if data flow into `node` is prohibited. */
|
||||||
predicate isBarrierIn(Node node) { none() }
|
predicate isBarrierIn(Node node) { none() }
|
||||||
|
|
||||||
|
|||||||
@@ -66,9 +66,6 @@ abstract class Configuration extends string {
|
|||||||
*/
|
*/
|
||||||
predicate isBarrier(Node node) { none() }
|
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. */
|
/** Holds if data flow into `node` is prohibited. */
|
||||||
predicate isBarrierIn(Node node) { none() }
|
predicate isBarrierIn(Node node) { none() }
|
||||||
|
|
||||||
|
|||||||
@@ -66,9 +66,6 @@ abstract class Configuration extends string {
|
|||||||
*/
|
*/
|
||||||
predicate isBarrier(Node node) { none() }
|
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. */
|
/** Holds if data flow into `node` is prohibited. */
|
||||||
predicate isBarrierIn(Node node) { none() }
|
predicate isBarrierIn(Node node) { none() }
|
||||||
|
|
||||||
|
|||||||
@@ -66,9 +66,6 @@ abstract class Configuration extends string {
|
|||||||
*/
|
*/
|
||||||
predicate isBarrier(Node node) { none() }
|
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. */
|
/** Holds if data flow into `node` is prohibited. */
|
||||||
predicate isBarrierIn(Node node) { none() }
|
predicate isBarrierIn(Node node) { none() }
|
||||||
|
|
||||||
|
|||||||
@@ -66,9 +66,6 @@ abstract class Configuration extends string {
|
|||||||
*/
|
*/
|
||||||
predicate isBarrier(Node node) { none() }
|
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. */
|
/** Holds if data flow into `node` is prohibited. */
|
||||||
predicate isBarrierIn(Node node) { none() }
|
predicate isBarrierIn(Node node) { none() }
|
||||||
|
|
||||||
|
|||||||
@@ -79,13 +79,6 @@ abstract class Configuration extends DataFlow::Configuration {
|
|||||||
defaultTaintBarrier(node)
|
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. */
|
/** Holds if data flow into `node` is prohibited. */
|
||||||
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
||||||
|
|
||||||
|
|||||||
@@ -79,13 +79,6 @@ abstract class Configuration extends DataFlow::Configuration {
|
|||||||
defaultTaintBarrier(node)
|
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. */
|
/** Holds if data flow into `node` is prohibited. */
|
||||||
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
predicate isSanitizerIn(DataFlow::Node node) { none() }
|
||||||
|
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ class XMLFile extends XMLParent, File {
|
|||||||
XMLFile() { xmlEncoding(this, _) }
|
XMLFile() { xmlEncoding(this, _) }
|
||||||
|
|
||||||
/** Gets a printable representation of this XML file. */
|
/** 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. */
|
/** Gets the name of this XML file. */
|
||||||
override string getName() { result = File.super.getAbsolutePath() }
|
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() }
|
string getAttributeValue(string name) { result = this.getAttribute(name).getValue() }
|
||||||
|
|
||||||
/** Gets a printable representation of this XML element. */
|
/** Gets a printable representation of this XML element. */
|
||||||
override string toString() { result = XMLParent.super.toString() }
|
override string toString() { result = getName() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -37,4 +37,68 @@ public class SpringBootActuators {
|
|||||||
protected void configureOk2(HttpSecurity http) throws Exception {
|
protected void configureOk2(HttpSecurity http) throws Exception {
|
||||||
http.requestMatchers().requestMatchers(EndpointRequest.toAnyEndpoint());
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,10 @@ public final class EndpointRequest {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static EndpointRequestMatcher to(String... endpoints) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public static final class EndpointRequestMatcher extends AbstractRequestMatcher {}
|
public static final class EndpointRequestMatcher extends AbstractRequestMatcher {}
|
||||||
|
|
||||||
private abstract static class AbstractRequestMatcher
|
private abstract static class AbstractRequestMatcher
|
||||||
|
|||||||
14
javascript/ql/src/external/CodeDuplication.qll
vendored
14
javascript/ql/src/external/CodeDuplication.qll
vendored
@@ -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) {
|
predicate similarLines(File f, int line) {
|
||||||
exists(SimilarBlock b | b.sourceFile() = f and line in [b.sourceStartLine() .. b.sourceEndLine()])
|
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]
|
pragma[noopt]
|
||||||
private predicate similarLinesCovered(File f, int coveredLines, File otherFile) {
|
private predicate similarLinesCovered(File f, int coveredLines, File otherFile) {
|
||||||
exists(int numLines | numLines = f.getNumberOfLines() |
|
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) {
|
predicate duplicateLines(File f, int line) {
|
||||||
exists(DuplicateBlock b |
|
exists(DuplicateBlock b |
|
||||||
b.sourceFile() = f and line in [b.sourceStartLine() .. b.sourceEndLine()]
|
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]
|
pragma[noopt]
|
||||||
private predicate duplicateLinesCovered(File f, int coveredLines, File otherFile) {
|
private predicate duplicateLinesCovered(File f, int coveredLines, File otherFile) {
|
||||||
exists(int numLines | numLines = f.getNumberOfLines() |
|
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) {
|
predicate similarFiles(File f, File other, int percent) {
|
||||||
exists(int covered, int total |
|
exists(int covered, int total |
|
||||||
similarLinesCovered(f, covered, other) and
|
similarLinesCovered(f, covered, other) and
|
||||||
@@ -343,6 +356,7 @@ predicate similarFiles(File f, File other, int percent) {
|
|||||||
not duplicateFiles(f, other, _)
|
not duplicateFiles(f, other, _)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Holds if most of `f` (`percent`%) is duplicated by `other`. */
|
||||||
predicate duplicateFiles(File f, File other, int percent) {
|
predicate duplicateFiles(File f, File other, int percent) {
|
||||||
exists(int covered, int total |
|
exists(int covered, int total |
|
||||||
duplicateLinesCovered(f, covered, other) and
|
duplicateLinesCovered(f, covered, other) and
|
||||||
|
|||||||
@@ -152,6 +152,18 @@ private class RequireVariable extends Variable {
|
|||||||
*/
|
*/
|
||||||
private predicate moduleInFile(Module m, File f) { m.getFile() = f }
|
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.
|
* A `require` import.
|
||||||
*
|
*
|
||||||
@@ -162,12 +174,7 @@ private predicate moduleInFile(Module m, File f) { m.getFile() = f }
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
class Require extends CallExpr, Import {
|
class Require extends CallExpr, Import {
|
||||||
cached
|
Require() { isRequire(getCallee().flow()) }
|
||||||
Require() {
|
|
||||||
any(RequireVariable req).getAnAccess() = getCallee() and
|
|
||||||
// `mjs` files explicitly disallow `require`
|
|
||||||
not getFile().getExtension() = "mjs"
|
|
||||||
}
|
|
||||||
|
|
||||||
override PathExpr getImportedPath() { result = getArgument(0) }
|
override PathExpr getImportedPath() { result = getArgument(0) }
|
||||||
|
|
||||||
@@ -257,8 +264,8 @@ private class RequirePath extends PathExprCandidate {
|
|||||||
RequirePath() {
|
RequirePath() {
|
||||||
this = any(Require req).getArgument(0)
|
this = any(Require req).getArgument(0)
|
||||||
or
|
or
|
||||||
exists(RequireVariable req, MethodCallExpr reqres |
|
exists(MethodCallExpr reqres |
|
||||||
reqres.getReceiver() = req.getAnAccess() and
|
isRequire(reqres.getReceiver().flow()) and
|
||||||
reqres.getMethodName() = "resolve" and
|
reqres.getMethodName() = "resolve" and
|
||||||
this = reqres.getArgument(0)
|
this = reqres.getArgument(0)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ class XMLFile extends XMLParent, File {
|
|||||||
XMLFile() { xmlEncoding(this, _) }
|
XMLFile() { xmlEncoding(this, _) }
|
||||||
|
|
||||||
/** Gets a printable representation of this XML file. */
|
/** 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. */
|
/** Gets the name of this XML file. */
|
||||||
override string getName() { result = File.super.getAbsolutePath() }
|
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() }
|
string getAttributeValue(string name) { result = this.getAttribute(name).getValue() }
|
||||||
|
|
||||||
/** Gets a printable representation of this XML element. */
|
/** Gets a printable representation of this XML element. */
|
||||||
override string toString() { result = XMLParent.super.toString() }
|
override string toString() { result = getName() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -151,13 +151,16 @@ private module CachedSteps {
|
|||||||
) {
|
) {
|
||||||
calls(invk, f) and
|
calls(invk, f) and
|
||||||
(
|
(
|
||||||
exists(int i, Parameter p |
|
exists(int i | arg = invk.getArgument(i) |
|
||||||
|
exists(Parameter p |
|
||||||
f.getParameter(i) = p and
|
f.getParameter(i) = p and
|
||||||
not p.isRestParameter() and
|
not p.isRestParameter() and
|
||||||
arg = invk.getArgument(i) and
|
|
||||||
parm = DataFlow::parameterNode(p)
|
parm = DataFlow::parameterNode(p)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
|
parm = reflectiveParameterAccess(f, i)
|
||||||
|
)
|
||||||
|
or
|
||||||
arg = invk.(DataFlow::CallNode).getReceiver() and
|
arg = invk.(DataFlow::CallNode).getReceiver() and
|
||||||
parm = DataFlow::thisNode(f)
|
parm = DataFlow::thisNode(f)
|
||||||
)
|
)
|
||||||
@@ -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
|
* Holds if there is a flow step from `pred` to `succ` through parameter passing
|
||||||
* to a function call.
|
* to a function call.
|
||||||
|
|||||||
291
javascript/ql/src/semmle/javascript/frameworks/Fastify.qll
Normal file
291
javascript/ql/src/semmle/javascript/frameworks/Fastify.qll
Normal file
@@ -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()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,3 +4,4 @@ import semmle.javascript.frameworks.Koa
|
|||||||
import semmle.javascript.frameworks.NodeJSLib
|
import semmle.javascript.frameworks.NodeJSLib
|
||||||
import semmle.javascript.frameworks.Restify
|
import semmle.javascript.frameworks.Restify
|
||||||
import semmle.javascript.frameworks.Connect
|
import semmle.javascript.frameworks.Connect
|
||||||
|
import semmle.javascript.frameworks.Fastify
|
||||||
|
|||||||
@@ -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 ] |
|
||||||
@@ -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
|
||||||
12
javascript/ql/test/library-tests/DataFlow/arguments.js
Normal file
12
javascript/ql/test/library-tests/DataFlow/arguments.js
Normal file
@@ -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);
|
||||||
|
})();
|
||||||
@@ -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: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: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 |
|
| eval.js:2:7:2:12 | x = 42 | eval.js:2:7:2:12 | x = 42 |
|
||||||
|
|||||||
@@ -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: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 |
|
| 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 |
|
| sources.js:1:6:1:6 | x | sources.js:1:6:1:6 | x |
|
||||||
|
|||||||
@@ -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 |
|
| eval.js:2:11:2:12 | 42 | 42 |
|
||||||
| sources.js:4:12:4:13 | 19 | 19 |
|
| sources.js:4:12:4:13 | 19 | 19 |
|
||||||
| sources.js:5:4:5:5 | 23 | 23 |
|
| sources.js:5:4:5:5 | 23 | 23 |
|
||||||
|
|||||||
@@ -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:1:1:5:1 | exceptional return of function k | call |
|
||||||
| eval.js:2:7:2:12 | x | eval |
|
| eval.js:2:7:2:12 | x | eval |
|
||||||
| eval.js:3:3:3:6 | eval | global |
|
| eval.js:3:3:3:6 | eval | global |
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
| arguments.js:2:16:2:16 | x |
|
||||||
| sources.js:1:6:1:6 | x |
|
| sources.js:1:6:1:6 | x |
|
||||||
| sources.js:3:11:3:11 | x |
|
| sources.js:3:11:3:11 | x |
|
||||||
| sources.js:9:14:9:18 | array |
|
| sources.js:9:14:9:18 | array |
|
||||||
|
|||||||
@@ -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:1:0 | this |
|
| eval.js:1:1:1:0 | this |
|
||||||
| eval.js:1:1:5:1 | functio ... eval`\\n} |
|
| eval.js:1:1:5:1 | functio ... eval`\\n} |
|
||||||
|
|||||||
@@ -11,6 +11,8 @@
|
|||||||
| d.js:7:1:7:14 | require('foo') |
|
| d.js:7:1:7:14 | require('foo') |
|
||||||
| e.js:5:1:5:18 | require("process") |
|
| e.js:5:1:5:18 | require("process") |
|
||||||
| f.js:2:1:2:7 | r("fs") |
|
| 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:1:12:1:26 | require('path') |
|
||||||
| index.js:2:1:2:41 | require ... b.js")) |
|
| index.js:2:1:2:41 | require ... b.js")) |
|
||||||
| mjs-files/require-from-js.js:1:12:1:36 | require ... on-me') |
|
| mjs-files/require-from-js.js:1:12:1:36 | require ... on-me') |
|
||||||
|
|||||||
1
javascript/ql/test/library-tests/NodeJS/g.js
Normal file
1
javascript/ql/test/library-tests/NodeJS/g.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
(process && "renderer" === process.type ? require("electron").remote.require : require)("https");
|
||||||
@@ -31,6 +31,7 @@ typeInferenceMismatch
|
|||||||
| callbacks.js:44:17:44:24 | source() | callbacks.js:41:10:41:10 | x |
|
| 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: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 |
|
| 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 |
|
| 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:8:8:8:31 | string. ... (taint) |
|
||||||
| closure.js:6:15:6:22 | source() | closure.js:9:8:9:25 | string.trim(taint) |
|
| closure.js:6:15:6:22 | source() | closure.js:9:8:9:25 | string.trim(taint) |
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user