Compare commits

...

151 Commits

Author SHA1 Message Date
Max Schaefer
5726591780 Revert "JS: Recognize DomSanitizer from @angular/core"
This reverts commit ff1d0cc4c7.
2021-11-22 21:08:49 +00:00
Henry Mercer
cc3ad50c39 Remove NoSQL sinks since September 2018 2021-11-22 21:08:49 +00:00
Esben Sparre Andreasen
11cd44ca69 Remove additional Xss sinks 2021-11-22 21:08:49 +00:00
Esben Sparre Andreasen
78c4690eee Remove additional SQL sinks 2021-11-22 21:08:49 +00:00
Esben Sparre Andreasen
fddc2cc6de Remove additional path-injection sinks 2021-11-22 21:08:49 +00:00
Esben Sparre Andreasen
700c1f5a35 Add benjamin-button.md 2021-11-22 21:08:49 +00:00
Esben Sparre Andreasen
426e635437 Remove pseudo-properties 2021-11-22 21:08:49 +00:00
Esben Sparre Andreasen
a769c96aa2 Remove 2020 sinks from SqlInjection.ql 2021-11-22 21:08:49 +00:00
Esben Sparre Andreasen
5b1bfe2799 Remove 2020 sinks from Xss.ql 2021-11-22 21:08:49 +00:00
Esben Sparre Andreasen
6e2897b0f8 Remove 2020 sinks from TaintedPath.ql 2021-11-22 21:08:49 +00:00
Tiferet Gazit
f28b85c059 Create many features with different scopes.
This will allow us to experiment with feature selection on the modeling side to pick out the best subset of scoped features.
2021-11-22 20:56:41 +00:00
Tiferet Gazit
b493702a5a Bug fix:
When getting the wrapper for the root node, we must give the container of the root node, not the endpoint, because they won't necessarily be the same for functions contained within functions.
2021-11-22 20:15:48 +00:00
Tiferet Gazit
e2ffba2e47 TODO Interim fix:
For now don't allow the neighborhood to go all the way out to the outermostEnclosingFunction, because that causes bugs.

After this change, `enclosingFunctionBodyEndpointNeighborhood` doesn't match `enclosingFunctionBody` even when the function is small, which is undesired behavior. `enclosingFunctionBody` includes the arguments to the function whereas `enclosingFunctionBodyEndpointNeighborhood` does not. We should find a way to fix this.
2021-11-22 20:10:24 +00:00
Tiferet Gazit
a507d6dc1d Formatting fixes 2021-11-22 20:02:28 +00:00
Tiferet Gazit
da5f664265 Create three features with different scopes:
Create three versions of `enclosingFunctionBodyEndpointNeighborhood`, each with a different threshold on the number of descendants.
2021-11-20 02:39:01 +00:00
Tiferet Gazit
d52af5599a Allow the neighborhood to be the entire enclosing function 2021-11-20 02:28:53 +00:00
Anna Railton
05460f6444 Add check for neighborhood node being top-level enclosing function 2021-11-18 16:34:06 +00:00
Anna Railton
c7f96928dc Reformat docstrings 2021-11-18 16:34:03 +00:00
Anna Railton
4351d9e861 Add predicates for the magic numbers in the predicates 2021-11-18 16:26:59 +00:00
Anna Railton
c3766477dc Apply suggestions from code review
Co-authored-by: Tiferet Gazit <tiferet@github.com>
2021-11-18 16:26:59 +00:00
annarailton
b6922813a8 Add neighborhoodBody feature to token features
Co-authored-by: Chris Smowton <smowton@github.com>
2021-11-18 16:26:59 +00:00
annarailton
5de0edc773 Add NeighborhoodBodies module
This provides functionality for getting the token features associated with a
neighborhood around an AST node. It is strongly related to `FunctionBodies`.

Co-authored-by: Chris Smowton <smowton@github.com>
2021-11-18 16:26:59 +00:00
annarailton
a8c1febf88 Add helper types for changes to EndpointFeatures
Co-authored-by: Chris Smowton <smowton@github.com>
2021-11-18 16:26:59 +00:00
annarailton
2b334055db Clean up to astNodes predicate to make more explicit
Co-authored-by: Chris Smowton <smowton@github.com>
2021-11-18 16:26:59 +00:00
Henry Mercer
46958e5bff Merge pull request #7168 from erik-krogh/useMinInsteadofRank1
ATM: use min() instead of rank[1]
2021-11-18 14:56:49 +00:00
Anders Schack-Mulligen
6815a13a00 Merge pull request #6931 from hvitved/dataflow/restrict-derived-summaries
Data flow: Restrict derived flow summaries
2021-11-18 15:31:55 +01:00
Erik Krogh Kristensen
2af7817691 use min() instead of rank[1] 2021-11-18 14:26:55 +01:00
Geoffrey White
6c2713dd8b Merge pull request #7159 from MathiasVP/pointers-are-always-iterators
C++: Always recognize pointers as iterators
2021-11-18 11:14:44 +00:00
Tony Torralba
9d22ec88fd Merge pull request #7165 from github/workflow/coverage/update
Update CSV framework coverage reports
2021-11-18 10:37:34 +01:00
Erik Krogh Kristensen
1cca377e7d Merge pull request #6561 from erik-krogh/htmlReg
JS/Py/Ruby: add a bad-tag-filter query
2021-11-18 09:39:13 +01:00
github-actions[bot]
ecdaeb0c10 Add changed framework coverage reports 2021-11-18 00:09:24 +00:00
Mathias Vorreiter Pedersen
c998370c84 C++: Accept more test changes. 2021-11-17 15:22:17 +00:00
Mathias Vorreiter Pedersen
36585a7469 C++: Accept test changes. 2021-11-17 14:41:30 +00:00
Mathias Vorreiter Pedersen
6dc6a78293 C++: Add a 'IteratorByPointer' class so pointers are always iterators. 2021-11-17 14:41:19 +00:00
Alex Ford
08b6a17097 Merge pull request #7151 from github/ruby/methodcallnode
Ruby: add `getMethodName` predicate to `DataFlow::CallNode` class
2021-11-17 14:40:07 +00:00
Anders Schack-Mulligen
22ebe68b1b Merge pull request #7132 from aschackmull/java/overrides
Java: Fix overrides to not be transitive.
2021-11-17 15:38:11 +01:00
Anders Schack-Mulligen
1645fcf79c Merge pull request #7088 from aschackmull/java/parameterized-subtyping
Java: Improve algorithm for subtyping of parameterized types.
2021-11-17 15:28:28 +01:00
Arthur Baars
5ddfb37f29 Merge pull request #7155 from aibaars/ruby-qualified-import
Ruby: QL generator: use qualified imports
2021-11-17 12:40:14 +01:00
Arthur Baars
6f24947ec6 Merge pull request #7156 from aibaars/ruby-prepare-db-upgrade-fix
Ruby: Fix scripts/prepare-db-upgrade.sh
2021-11-17 12:39:55 +01:00
Benjamin Muskalla
3c3a65243f Merge pull request #6664 from bmuskalla/bmuskalla/modelGenerator
Java: Initial CSV model generator
2021-11-17 12:30:45 +01:00
Arthur Baars
fbb3e8d780 Fix scripts/prepare-db-upgrade.sh 2021-11-17 12:11:20 +01:00
Geoffrey White
d232283647 Merge pull request #7149 from geoffw0/non-https-url2
C++: Minor improvements to 'Failure to use HTTPS URLs' query
2021-11-17 10:05:30 +00:00
Tom Hvitved
4eacbd1cbe Ruby: Sync files 2021-11-17 10:49:51 +01:00
Tom Hvitved
6d58dd2823 Java: Update expected test output 2021-11-17 10:49:51 +01:00
Tom Hvitved
3da73b9001 C#: Update expected test output 2021-11-17 10:49:49 +01:00
Tom Hvitved
58dd75881c C#: Update flow summary to avoid negative recursion 2021-11-17 10:39:13 +01:00
Tom Hvitved
ac41451798 Data flow: Sync files 2021-11-17 10:39:12 +01:00
Tom Hvitved
0c1285f5d9 Data flow: Restrict derived flow summaries 2021-11-17 10:39:12 +01:00
Arthur Baars
7c2841f058 Ruby: QL generator: use qualified imports 2021-11-17 10:37:44 +01:00
Erik Krogh Kristensen
474c808373 Merge pull request #7137 from erik-krogh/functionExport
JS: recognize library inputs when the library exports "through" a function
2021-11-17 09:49:02 +01:00
Anders Schack-Mulligen
69671ce90d Java: cache overrides 2021-11-17 09:16:58 +01:00
Tom Hvitved
7cfc696d62 Merge pull request #7141 from hvitved/ruby/synthesis-realnode-recursion
Ruby: Eliminate unnecessary recursion through `RealNode`
2021-11-17 09:03:30 +01:00
Pierre
f846915b58 Merge pull request #7069 from github/turbo-apple-silicon-requirements-1
Add requirements for Apple Silicon
2021-11-17 00:04:06 +01:00
Alex Ford
c8cdbfa352 ruby: push getMethodName into DataFlow::CallNode 2021-11-16 17:11:26 +00:00
Benjamin Muskalla
b4eadefb92 Fix test 2021-11-16 17:28:01 +01:00
Alex Ford
286c894f34 ruby: add DataFlow::MethodCallNode class 2021-11-16 15:39:47 +00:00
Chris Smowton
188915e597 Fix typos 2021-11-16 15:30:00 +00:00
Anders Schack-Mulligen
76606b5995 Java: Add more comments. 2021-11-16 16:11:14 +01:00
Geoffrey White
ea9640a39d C++: Autoformat. 2021-11-16 14:26:42 +00:00
Anders Schack-Mulligen
d408105fad Java: Fix bad join-order. 2021-11-16 14:25:19 +01:00
Geoffrey White
5ef71e6ef3 C++: Add a few more sinks. 2021-11-16 13:02:56 +00:00
Tom Hvitved
9e8e2e2b48 Ruby: Update CFG test output (some nodes have been reordered) 2021-11-16 12:45:24 +01:00
Tom Hvitved
e7b091086d Ruby: Eliminate unnecessary recursion through RealNode 2021-11-16 12:24:17 +01:00
Geoffrey White
aafa5762ad C++: Add a CWE tag associated with OWASP A8. 2021-11-16 11:21:25 +00:00
Benjamin Muskalla
0e6bb28016 Only consider store steps 2021-11-16 10:46:24 +01:00
Erik Krogh Kristensen
b9ea4a8709 recognize library inputs when the library exports "through" a function 2021-11-15 22:43:38 +01:00
Benjamin Muskalla
fd9199c0c0 Simplify handling of tainting fields 2021-11-15 16:40:09 +01:00
Benjamin Muskalla
d7ed325b3f Refactor content flow into predicate 2021-11-15 16:30:55 +01:00
Benjamin Muskalla
f4310898b3 Capture sources flowing into parameters 2021-11-15 16:28:28 +01:00
Anders Schack-Mulligen
1cd42ea668 Java: Fix test and some references. 2021-11-15 16:03:04 +01:00
Benjamin Muskalla
8040d9cfcf Only consider true return statements as sinks 2021-11-15 15:29:01 +01:00
Benjamin Muskalla
e6e52a3b32 190 2021-11-15 15:18:03 +01:00
Anders Schack-Mulligen
c616f5784d Java: Fix overrides to not be transitive. 2021-11-15 13:54:53 +01:00
Benjamin Muskalla
dc022430ee Remove superflous instanceof 2021-11-15 13:07:02 +01:00
Benjamin Muskalla
412bd32f45 Move more predicates into configuration 2021-11-15 13:04:23 +01:00
Benjamin Muskalla
b84c03672d Prefer types to TargetAPI 2021-11-15 12:43:46 +01:00
Benjamin Muskalla
bca6cecd1c Remove basic support for lambda flow 2021-11-15 12:38:30 +01:00
Benjamin Muskalla
78e3906ea7 Exclude more JDK internals 2021-11-15 11:58:10 +01:00
Benjamin Muskalla
cce3780481 Restrict param2return value features 2021-11-15 09:57:23 +01:00
Benjamin Muskalla
a0b7f267ff Only capture taint from own fields
Also exclude `Charset` as relevant taint-carrying type. This is generally
what we want to lets us avoid tracking arguments that lead to FP.
2021-11-12 10:15:15 +01:00
Benjamin Muskalla
0234e77d2f Let sink node be pluggable in any call context 2021-11-12 09:43:05 +01:00
Benjamin Muskalla
b8809a20d8 Support propagating taint of inner object 2021-11-12 09:39:59 +01:00
Erik Krogh Kristensen
b639a8d183 update ruby example
Co-authored-by: Nick Rolfe <nickrolfe@github.com>
2021-11-11 14:04:38 +01:00
Benjamin Muskalla
2d4176bec0 Ignore Number-derived types 2021-11-10 16:30:27 +01:00
Benjamin Muskalla
dbd393b77a Support flow into field of referenced objects 2021-11-10 16:30:27 +01:00
Benjamin Muskalla
974c7b0898 Avoid cross-class flow for field writes 2021-11-10 16:30:26 +01:00
Benjamin Muskalla
74ac234f1c Restrict field access to same type 2021-11-10 16:30:26 +01:00
Benjamin Muskalla
8740e879b4 Fix docs 2021-11-10 16:30:26 +01:00
Benjamin Muskalla
a546b38ee0 Restrict field access to corresponding type 2021-11-10 16:30:26 +01:00
Benjamin Muskalla
6960a7b97e Remove extraneous last column 2021-11-10 16:30:25 +01:00
Benjamin Muskalla
ef972159a6 Fix bug when generating output in a subfolder 2021-11-10 16:30:25 +01:00
Benjamin Muskalla
4cfd978bfe Support generating in respective folders 2021-11-10 16:30:25 +01:00
Benjamin Muskalla
b92758883b Auto-format generated qll files 2021-11-10 16:30:25 +01:00
Benjamin Muskalla
e2bd792fc2 Consider bulk-like data for argument accessors 2021-11-10 16:30:25 +01:00
Benjamin Muskalla
739fe75194 Support flow for factory and strategy pattern
* Support models for factories that create
new instances of an object while tainting it with incoming data
* Support models to infer super types for
private implementations to expose the models
at the right level
2021-11-10 16:30:24 +01:00
Benjamin Muskalla
58de6d143f Add docs to explain the models captured by the predicates 2021-11-10 16:30:24 +01:00
Benjamin Muskalla
747ab122c3 Restrict fluent api models to same type access 2021-11-10 16:30:24 +01:00
Benjamin Muskalla
8564c9001a Fix naming for source nodes 2021-11-10 16:30:24 +01:00
Benjamin Muskalla
9500c9c8bc Support lambda flow for source models
Also rely on public API to detect the source node
2021-11-10 16:30:24 +01:00
Benjamin Muskalla
35baa1c3df Support bulkdata for boxed types as well 2021-11-10 16:30:23 +01:00
Benjamin Muskalla
83b4070f31 Fix bug to accept bulk data for char/byte arrays 2021-11-10 16:30:23 +01:00
Benjamin Muskalla
281f25403d Match enclosing unit without casting to specific nodes 2021-11-10 16:30:23 +01:00
Benjamin Muskalla
bc10fd94cb Support generating only specific models 2021-11-10 16:30:23 +01:00
Benjamin Muskalla
0e9fcc6c39 Only generate models for local supertypes
Avoid generating models for classes
implementing external SPI (e.g. `FileFilter`).
Keep `toString` models intact as they're
commonly used as taint-propagation method
(e.g. see `Joiner`).
2021-11-10 16:30:23 +01:00
Benjamin Muskalla
157f56f48a Capture model for defining interface
Instead of modeling individual implementations, take a more general
approach of reuse dataflows for interfaces defined by a library. This allows
tracking flows across all implementations and aligns better with how we
manually model frameworks. This may have some FPs given all possible flows
are modeled for a specific interface but also covers more scenarios where
we don't know which implementation of an interface is used.
2021-11-10 16:30:22 +01:00
Benjamin Muskalla
f36bb8baaf Exclude models for simpler types
Avoid generating models for types that can't really propagate taint
in a valuable way (e.g. primitivies, BigInt, ..). Keep tracking
bulk-like data (e.g. char[] or byte[]).
2021-11-10 16:30:22 +01:00
Benjamin Muskalla
842f617bc1 Order sinks and sources first 2021-11-10 16:30:22 +01:00
Benjamin Muskalla
be150f269b Formatting 2021-11-10 16:30:22 +01:00
Benjamin Muskalla
2654e27123 Exclude known internal APIs from being modeled 2021-11-10 16:30:22 +01:00
Benjamin Muskalla
6b2460d4a1 Formatting 2021-11-10 16:30:21 +01:00
Benjamin Muskalla
060862ab3b Avoid certain test sources in models 2021-11-10 16:30:21 +01:00
Benjamin Muskalla
e607953b9c Simplify query 2021-11-10 16:30:21 +01:00
Benjamin Muskalla
9a859334d4 Formatting 2021-11-10 16:30:21 +01:00
Benjamin Muskalla
6c59333716 Remove workaround that accounted for missing flow 2021-11-10 16:30:21 +01:00
Benjamin Muskalla
7dae6122d9 Support CharSequence#toString
Given CharSequence is often used as an
alias for String, ensure taint through toString is flowing
2021-11-10 16:30:20 +01:00
Benjamin Muskalla
ca9d5439f0 Restrict source configuration to return nodes 2021-11-10 16:30:20 +01:00
Benjamin Muskalla
7a7ec06819 Simplify sink configuration 2021-11-10 16:30:20 +01:00
Benjamin Muskalla
1a4fd7bc7d Allow camelcase names 2021-11-10 16:30:20 +01:00
Benjamin Muskalla
2b2ac82fb7 Fix bug in sink detection 2021-11-10 16:30:20 +01:00
Benjamin Muskalla
c616eb1473 Fix finding more sources 2021-11-10 16:30:19 +01:00
Benjamin Muskalla
38579ef25b Add proper metadata to queries 2021-11-10 16:30:19 +01:00
Benjamin Muskalla
a80d50cbc0 Simplify field flow 2021-11-10 16:30:19 +01:00
Benjamin Muskalla
a1d8dfb524 Initial support for source models 2021-11-10 16:30:19 +01:00
Benjamin Muskalla
c844f5382f Add script to generate flow models 2021-11-10 16:30:19 +01:00
Benjamin Muskalla
f9fea15a52 Initial support for capturing sink models 2021-11-10 16:30:18 +01:00
Benjamin Muskalla
364de55b8d Support parameter->parameter flow 2021-11-10 16:30:18 +01:00
Benjamin Muskalla
cd11ef3bf6 Support outgoing taint flow from fields 2021-11-10 16:30:18 +01:00
Benjamin Muskalla
c3462be2c9 Capture argument->return value flows 2021-11-10 16:30:18 +01:00
Benjamin Muskalla
4ca006ba3d Only expose visible innner classes 2021-11-10 16:30:18 +01:00
Benjamin Muskalla
88032afdc3 Add test for final class 2021-11-10 16:30:17 +01:00
Benjamin Muskalla
ec772fb6b2 Add support for qualifier flow 2021-11-10 16:30:17 +01:00
Benjamin Muskalla
32ef40c77b Add scaffolding for summary model generator 2021-11-10 16:30:17 +01:00
Anders Schack-Mulligen
1efe1e0d10 Java: Improve algorithm for subtyping of parameterized types. 2021-11-09 15:49:17 +01:00
Pierre
4af3775b72 Add requirements for Apple Silicon 2021-11-08 01:37:31 +01:00
Erik Krogh Kristensen
02f500b9c2 Merge branch 'main' into htmlReg 2021-11-04 12:58:42 +01:00
Erik Krogh Kristensen
8a4b043cb1 fix imports 2021-10-26 15:39:45 +02:00
Erik Krogh Kristensen
62e729501c make the RegExpEscape::getUnescaped predicate public in python 2021-10-26 15:25:14 +02:00
Erik Krogh Kristensen
97264b5dda add the bad tag filter query to ruby 2021-10-26 15:25:12 +02:00
Erik Krogh Kristensen
c15ddf6e92 update ReDoSUtil in ruby 2021-10-26 15:03:09 +02:00
Erik Krogh Kristensen
2ddf445caf move ruby files to match file structure from js/py 2021-10-26 14:54:12 +02:00
Erik Krogh Kristensen
44afa34e37 Merge branch 'main' of github.com:github/codeql into htmlReg 2021-10-26 14:46:27 +02:00
Erik Krogh Kristensen
8d6cac76cc apply suggestions from asgerf 2021-10-04 12:45:02 +02:00
Erik Krogh Kristensen
aafae24ef2 update qhelp 2021-09-28 23:11:02 +02:00
Erik Krogh Kristensen
8d556ed1e1 Update python/ql/lib/semmle/python/security/BadTagFilterQuery.qll
Co-authored-by: Rasmus Wriedt Larsen <rasmuswriedtlarsen@gmail.com>
2021-09-28 23:04:28 +02:00
Erik Krogh Kristensen
99ed4a1a89 add a bad-tag-filter query for Python and JavaScript 2021-09-21 15:04:03 +02:00
Erik Krogh Kristensen
fd64ff9ef1 don't give group numbers to non-capturing groups 2021-09-21 12:15:27 +02:00
Erik Krogh Kristensen
c40ffab093 make isStartState public in ReDoSUtil 2021-09-21 12:14:21 +02:00
Erik Krogh Kristensen
672e4a3d72 cache TopLevel::isMinified 2021-09-21 12:13:37 +02:00
Erik Krogh Kristensen
60993214d5 cache isInterpretedAsRegExp 2021-09-21 12:13:37 +02:00
Erik Krogh Kristensen
01e345c2cc implement RegExpWordBoundary in RegexTreeView 2021-09-21 12:13:37 +02:00
Erik Krogh Kristensen
8535e6f281 use toUnicode in RegexTreeView 2021-09-21 12:13:37 +02:00
143 changed files with 4419 additions and 2139 deletions

51
benjamin-button.md Normal file
View File

@@ -0,0 +1,51 @@
# benjamin-buttons.md
This file describes the changes that have been applied to
the library to make it behave as if it was younger.
## TaintedPath.ql
Sinks added between 2020-01-01 and 2020-10-06 have been removed. Found by looking at:
- the commit titles of https://github.com/github/codeql/commits/main/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected
- the PR titles of https://github.com/github/codeql/pulls?page=2&q=is%3Apr+label%3AJS+is%3Aclosed+sink
Sinks added between 2018-08-02 and 2020-01-01 have been removed. Found by looking at:
- the commit titles of https://github.com/github/codeql/commits/main/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected
- the PR titles of https://github.com/github/codeql/pulls?page=2&q=is%3Apr+label%3AJS+is%3Aclosed+sink
- the PR titles of https://github.com/github/codeql/pulls?page=2&q=is%3Apr+label%3AJS+is%3Aclosed+pathinjection
- the PR titles of https://github.com/github/codeql/pulls?page=2&q=is%3Apr+label%3AJS+is%3Aclosed+tainted-path
Sinks from the "graceful-fs" and "fs-extra" (added before the open-sourcing squash).
## Xss.ql
Sinks added between 2020-01-01 and 2020-10-06 have been removed. Found by looking at:
- the commit titles of https://github.com/github/codeql/commits/main/javascript/ql/test/query-tests/Security/CWE-079/Xss.expected
- the PR titles of https://github.com/github/codeql/pulls?page=2&q=is%3Apr+label%3AJS+is%3Aclosed+sink
- recursive type tracking for `jQuery::dollar`, `DOM::domValueRef`.
## SqlInjection.ql
Sinks added between 2020-01-01 and 2020-10-06 have been removed. Found by looking at:
- the commit titles of https://github.com/github/codeql/commits/main/javascript/ql/test/query-tests/Security/CWE-089
- the PR titles of https://github.com/github/codeql/pulls?page=2&q=is%3Apr+label%3AJS+is%3Aclosed+sink
Sinks added between 2018-08-02 and 2020-01-01 have been removed. Found by looking at:
- the commit titles of https://github.com/github/codeql/commits/main/javascript/ql/test/query-tests/Security/CWE-089
- the PR titles of https://github.com/github/codeql/pulls?page=2&q=is%3Apr+label%3AJS+is%3Aclosed+sink
- the PR titles of https://github.com/github/codeql/pulls?page=2&q=is%3Apr+label%3AJS+is%3Aclosed+sql
TypeTracking in SQL.qll (added before the open-sourcing squash)
The model of `mssql` and `sequelize` (added before the open-sourcing squash)
## PseudoProperties
Pseudo-properties (`$name$`) used in type-tracking and global dataflow configurations have been disabled.
Found by searching for `"\$.*\$"`.

View File

@@ -471,7 +471,12 @@
"ReDoS Polynomial Python/JS": [
"javascript/ql/lib/semmle/javascript/security/performance/SuperlinearBackTracking.qll",
"python/ql/lib/semmle/python/security/performance/SuperlinearBackTracking.qll",
"ruby/ql/lib/codeql/ruby/regexp/SuperlinearBackTracking.qll"
"ruby/ql/lib/codeql/ruby/security/performance/SuperlinearBackTracking.qll"
],
"BadTagFilterQuery Python/JS/Ruby": [
"javascript/ql/lib/semmle/javascript/security/BadTagFilterQuery.qll",
"python/ql/lib/semmle/python/security/BadTagFilterQuery.qll",
"ruby/ql/lib/codeql/ruby/security/BadTagFilterQuery.qll"
],
"CFG": [
"csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplShared.qll",

View File

@@ -34,6 +34,16 @@ private class IteratorByTraits extends Iterator {
IteratorByTraits() { exists(IteratorTraits it | it.getIteratorType() = this) }
}
/**
* The C++ standard includes an `std::iterator_traits` specialization for pointer types. When
* this specialization is included in the database, a pointer type `T*` will be an instance
* of the `IteratorByTraits` class. However, if the `T*` specialization is not in the database,
* we need to explicitly include them with this class.
*/
private class IteratorByPointer extends Iterator instanceof PointerType {
IteratorByPointer() { not this instanceof IteratorByTraits }
}
/**
* A type which has the typedefs expected for an iterator.
*/

View File

@@ -7,6 +7,7 @@
* @id cpp/non-https-url
* @tags security
* external/cwe/cwe-319
* external/cwe/cwe-345
*/
import cpp
@@ -61,7 +62,11 @@ class HttpStringToUrlOpenConfig extends TaintTracking::Configuration {
// accessed as a URL, for example using it in a network access. Some
// URLs are only ever displayed or used for data processing.
exists(FunctionCall fc |
fc.getTarget().hasGlobalOrStdName(["system", "gethostbyname", "getaddrinfo"]) and
fc.getTarget()
.hasGlobalOrStdName([
"system", "gethostbyname", "gethostbyname2", "gethostbyname_r", "getaddrinfo",
"X509_load_http", "X509_CRL_load_http"
]) and
sink.asExpr() = fc.getArgument(0)
or
fc.getTarget().hasGlobalOrStdName(["send", "URLDownloadToFile", "URLDownloadToCacheFile"]) and

View File

@@ -1,3 +1,4 @@
| example.c:15:37:15:37 | b | example.c:15:37:15:37 | b |
| example.c:15:37:15:37 | b | example.c:19:6:19:6 | b |
| example.c:15:44:15:46 | pos | example.c:24:24:24:26 | pos |
| example.c:15:44:15:46 | pos | example.c:28:23:28:25 | pos |
@@ -70,8 +71,10 @@
| test.cpp:391:11:391:13 | tmp | test.cpp:391:10:391:13 | & ... |
| test.cpp:391:17:391:23 | source1 | test.cpp:391:10:391:13 | ref arg & ... |
| test.cpp:391:17:391:23 | source1 | test.cpp:391:16:391:23 | & ... |
| test.cpp:480:67:480:67 | s | test.cpp:480:67:480:67 | s |
| test.cpp:480:67:480:67 | s | test.cpp:481:21:481:21 | s |
| test.cpp:480:67:480:67 | s | test.cpp:482:20:482:20 | s |
| test.cpp:481:21:481:21 | s [post update] | test.cpp:480:67:480:67 | s |
| test.cpp:481:21:481:21 | s [post update] | test.cpp:482:20:482:20 | s |
| test.cpp:481:24:481:30 | ref arg content | test.cpp:482:23:482:29 | content |
| test.cpp:482:23:482:29 | content | test.cpp:483:9:483:17 | p_content |

View File

@@ -10,29 +10,35 @@ edges
| A.cpp:31:20:31:20 | c | A.cpp:23:10:23:10 | c |
| A.cpp:31:20:31:20 | c | A.cpp:31:14:31:21 | call to B [c] |
| A.cpp:41:15:41:21 | new | A.cpp:43:10:43:12 | & ... |
| A.cpp:43:10:43:12 | & ... | A.cpp:173:26:173:26 | o |
| A.cpp:47:12:47:18 | new | A.cpp:48:20:48:20 | c |
| A.cpp:48:12:48:18 | call to make [c] | A.cpp:49:10:49:10 | b [c] |
| A.cpp:48:20:48:20 | c | A.cpp:29:23:29:23 | c |
| A.cpp:48:20:48:20 | c | A.cpp:48:12:48:18 | call to make [c] |
| A.cpp:49:10:49:10 | b [c] | A.cpp:49:13:49:13 | c |
| A.cpp:49:13:49:13 | c | A.cpp:173:26:173:26 | o |
| A.cpp:55:5:55:5 | ref arg b [c] | A.cpp:56:10:56:10 | b [c] |
| A.cpp:55:12:55:19 | new | A.cpp:27:17:27:17 | c |
| A.cpp:55:12:55:19 | new | A.cpp:55:5:55:5 | ref arg b [c] |
| A.cpp:56:10:56:10 | b [c] | A.cpp:28:8:28:10 | this [c] |
| A.cpp:56:10:56:10 | b [c] | A.cpp:56:13:56:15 | call to get |
| A.cpp:56:13:56:15 | call to get | A.cpp:173:26:173:26 | o |
| A.cpp:57:11:57:24 | call to B [c] | A.cpp:57:11:57:24 | new [c] |
| A.cpp:57:11:57:24 | new [c] | A.cpp:28:8:28:10 | this [c] |
| A.cpp:57:11:57:24 | new [c] | A.cpp:57:28:57:30 | call to get |
| A.cpp:57:17:57:23 | new | A.cpp:23:10:23:10 | c |
| A.cpp:57:17:57:23 | new | A.cpp:57:11:57:24 | call to B [c] |
| A.cpp:57:28:57:30 | call to get | A.cpp:173:26:173:26 | o |
| A.cpp:64:10:64:15 | call to setOnB [c] | A.cpp:66:10:66:11 | b2 [c] |
| A.cpp:64:21:64:28 | new | A.cpp:64:10:64:15 | call to setOnB [c] |
| A.cpp:64:21:64:28 | new | A.cpp:85:26:85:26 | c |
| A.cpp:66:10:66:11 | b2 [c] | A.cpp:66:14:66:14 | c |
| A.cpp:66:14:66:14 | c | A.cpp:173:26:173:26 | o |
| A.cpp:73:10:73:19 | call to setOnBWrap [c] | A.cpp:75:10:75:11 | b2 [c] |
| A.cpp:73:25:73:32 | new | A.cpp:73:10:73:19 | call to setOnBWrap [c] |
| A.cpp:73:25:73:32 | new | A.cpp:78:27:78:27 | c |
| A.cpp:75:10:75:11 | b2 [c] | A.cpp:75:14:75:14 | c |
| A.cpp:75:14:75:14 | c | A.cpp:173:26:173:26 | o |
| A.cpp:78:27:78:27 | c | A.cpp:81:21:81:21 | c |
| A.cpp:81:10:81:15 | call to setOnB [c] | A.cpp:82:12:82:24 | ... ? ... : ... [c] |
| A.cpp:81:21:81:21 | c | A.cpp:81:10:81:15 | call to setOnB [c] |
@@ -48,13 +54,20 @@ edges
| A.cpp:103:14:103:14 | c [a] | A.cpp:107:12:107:13 | c1 [a] |
| A.cpp:103:14:103:14 | c [a] | A.cpp:120:12:120:13 | c1 [a] |
| A.cpp:107:12:107:13 | c1 [a] | A.cpp:107:16:107:16 | a |
| A.cpp:107:16:107:16 | a | A.cpp:173:26:173:26 | o |
| A.cpp:120:12:120:13 | c1 [a] | A.cpp:120:16:120:16 | a |
| A.cpp:120:16:120:16 | a | A.cpp:173:26:173:26 | o |
| A.cpp:124:14:124:14 | b [c] | A.cpp:131:8:131:8 | ref arg b [c] |
| A.cpp:126:5:126:5 | ref arg b [c] | A.cpp:124:14:124:14 | b [c] |
| A.cpp:126:5:126:5 | ref arg b [c] | A.cpp:131:8:131:8 | ref arg b [c] |
| A.cpp:126:12:126:18 | new | A.cpp:27:17:27:17 | c |
| A.cpp:126:12:126:18 | new | A.cpp:126:5:126:5 | ref arg b [c] |
| A.cpp:131:8:131:8 | ref arg b [c] | A.cpp:132:10:132:10 | b [c] |
| A.cpp:132:10:132:10 | b [c] | A.cpp:132:13:132:13 | c |
| A.cpp:132:13:132:13 | c | A.cpp:173:26:173:26 | o |
| A.cpp:140:13:140:13 | b | A.cpp:143:7:143:31 | ... = ... |
| A.cpp:140:13:140:13 | b [c] | A.cpp:151:18:151:18 | ref arg b [c] |
| A.cpp:142:7:142:7 | b [post update] [c] | A.cpp:140:13:140:13 | b [c] |
| A.cpp:142:7:142:7 | b [post update] [c] | A.cpp:143:7:143:31 | ... = ... [c] |
| A.cpp:142:7:142:7 | b [post update] [c] | A.cpp:151:18:151:18 | ref arg b [c] |
| A.cpp:142:7:142:20 | ... = ... | A.cpp:142:7:142:7 | b [post update] [c] |
@@ -66,15 +79,24 @@ edges
| A.cpp:143:7:143:31 | ... = ... [c] | A.cpp:143:7:143:10 | this [post update] [b, c] |
| A.cpp:143:25:143:31 | new | A.cpp:143:7:143:31 | ... = ... |
| A.cpp:150:12:150:18 | new | A.cpp:151:18:151:18 | b |
| A.cpp:151:12:151:24 | call to D [b, c] | A.cpp:152:10:152:10 | d [b, c] |
| A.cpp:151:12:151:24 | call to D [b, c] | A.cpp:153:10:153:10 | d [b, c] |
| A.cpp:151:12:151:24 | call to D [b] | A.cpp:152:10:152:10 | d [b] |
| A.cpp:151:18:151:18 | b | A.cpp:140:13:140:13 | b |
| A.cpp:151:18:151:18 | b | A.cpp:151:12:151:24 | call to D [b] |
| A.cpp:151:18:151:18 | ref arg b [c] | A.cpp:154:10:154:10 | b [c] |
| A.cpp:152:10:152:10 | d [b, c] | A.cpp:152:13:152:13 | b [c] |
| A.cpp:152:10:152:10 | d [b] | A.cpp:152:13:152:13 | b |
| A.cpp:152:10:152:10 | d [post update] [b, c] | A.cpp:153:10:153:10 | d [b, c] |
| A.cpp:152:13:152:13 | b | A.cpp:173:26:173:26 | o |
| A.cpp:152:13:152:13 | b [c] | A.cpp:152:13:152:13 | ref arg b [c] |
| A.cpp:152:13:152:13 | b [c] | A.cpp:173:26:173:26 | o [c] |
| A.cpp:152:13:152:13 | ref arg b [c] | A.cpp:152:10:152:10 | d [post update] [b, c] |
| A.cpp:153:10:153:10 | d [b, c] | A.cpp:153:13:153:13 | b [c] |
| A.cpp:153:13:153:13 | b [c] | A.cpp:153:16:153:16 | c |
| A.cpp:153:16:153:16 | c | A.cpp:173:26:173:26 | o |
| A.cpp:154:10:154:10 | b [c] | A.cpp:154:13:154:13 | c |
| A.cpp:154:13:154:13 | c | A.cpp:173:26:173:26 | o |
| A.cpp:159:12:159:18 | new | A.cpp:160:29:160:29 | b |
| A.cpp:160:18:160:60 | call to MyList [head] | A.cpp:161:38:161:39 | l1 [head] |
| A.cpp:160:29:160:29 | b | A.cpp:160:18:160:60 | call to MyList [head] |
@@ -87,13 +109,23 @@ edges
| A.cpp:162:38:162:39 | l2 [next, head] | A.cpp:162:18:162:40 | call to MyList [next, next, head] |
| A.cpp:162:38:162:39 | l2 [next, head] | A.cpp:181:32:181:35 | next [next, head] |
| A.cpp:165:10:165:11 | l3 [next, next, head] | A.cpp:165:14:165:17 | next [next, head] |
| A.cpp:165:10:165:11 | l3 [post update] [next, next, head] | A.cpp:167:44:167:44 | l [next, next, head] |
| A.cpp:165:14:165:17 | next [next, head] | A.cpp:165:20:165:23 | next [head] |
| A.cpp:165:14:165:17 | next [post update] [next, head] | A.cpp:165:10:165:11 | l3 [post update] [next, next, head] |
| A.cpp:165:20:165:23 | next [head] | A.cpp:165:26:165:29 | head |
| A.cpp:165:20:165:23 | next [head] | A.cpp:165:26:165:29 | head |
| A.cpp:165:20:165:23 | next [post update] [head] | A.cpp:165:14:165:17 | next [post update] [next, head] |
| A.cpp:165:26:165:29 | head | A.cpp:165:26:165:29 | ref arg head |
| A.cpp:165:26:165:29 | head | A.cpp:173:26:173:26 | o |
| A.cpp:165:26:165:29 | ref arg head | A.cpp:165:20:165:23 | next [post update] [head] |
| A.cpp:167:44:167:44 | l [next, head] | A.cpp:167:47:167:50 | next [head] |
| A.cpp:167:44:167:44 | l [next, next, head] | A.cpp:167:47:167:50 | next [next, head] |
| A.cpp:167:47:167:50 | next [head] | A.cpp:169:12:169:12 | l [head] |
| A.cpp:167:47:167:50 | next [next, head] | A.cpp:167:44:167:44 | l [next, head] |
| A.cpp:169:12:169:12 | l [head] | A.cpp:169:15:169:18 | head |
| A.cpp:169:15:169:18 | head | A.cpp:173:26:173:26 | o |
| A.cpp:173:26:173:26 | o | A.cpp:173:26:173:26 | o |
| A.cpp:173:26:173:26 | o [c] | A.cpp:173:26:173:26 | o [c] |
| A.cpp:181:15:181:21 | newHead | A.cpp:183:7:183:20 | ... = ... |
| A.cpp:181:32:181:35 | next [head] | A.cpp:184:7:184:23 | ... = ... [head] |
| A.cpp:181:32:181:35 | next [next, head] | A.cpp:184:7:184:23 | ... = ... [next, head] |
@@ -150,6 +182,7 @@ edges
| D.cpp:22:10:22:11 | b2 [box, elem] | D.cpp:22:14:22:20 | call to getBox1 [elem] |
| D.cpp:22:14:22:20 | call to getBox1 [elem] | D.cpp:10:11:10:17 | this [elem] |
| D.cpp:22:14:22:20 | call to getBox1 [elem] | D.cpp:22:25:22:31 | call to getElem |
| D.cpp:22:25:22:31 | call to getElem | realistic.cpp:41:17:41:17 | o |
| D.cpp:28:15:28:24 | new | D.cpp:30:5:30:20 | ... = ... |
| D.cpp:30:5:30:5 | b [post update] [box, elem] | D.cpp:31:14:31:14 | b [box, elem] |
| D.cpp:30:5:30:20 | ... = ... | D.cpp:30:8:30:10 | box [post update] [elem] |
@@ -182,17 +215,23 @@ edges
| D.cpp:64:10:64:17 | boxfield [box, elem] | D.cpp:64:20:64:22 | box [elem] |
| D.cpp:64:10:64:17 | this [boxfield, box, elem] | D.cpp:64:10:64:17 | boxfield [box, elem] |
| D.cpp:64:20:64:22 | box [elem] | D.cpp:64:25:64:28 | elem |
| D.cpp:64:25:64:28 | elem | realistic.cpp:41:17:41:17 | o |
| E.cpp:19:27:19:27 | p [data, buffer] | E.cpp:21:10:21:10 | p [data, buffer] |
| E.cpp:21:10:21:10 | p [data, buffer] | E.cpp:21:13:21:16 | data [buffer] |
| E.cpp:21:13:21:16 | data [buffer] | E.cpp:21:18:21:23 | buffer |
| E.cpp:21:18:21:23 | buffer | realistic.cpp:41:17:41:17 | o |
| E.cpp:28:21:28:23 | ref arg raw | E.cpp:31:10:31:12 | raw |
| E.cpp:29:21:29:21 | b [post update] [buffer] | E.cpp:32:10:32:10 | b [buffer] |
| E.cpp:29:24:29:29 | ref arg buffer | E.cpp:29:21:29:21 | b [post update] [buffer] |
| E.cpp:30:21:30:21 | p [post update] [data, buffer] | E.cpp:33:18:33:19 | & ... [data, buffer] |
| E.cpp:30:23:30:26 | data [post update] [buffer] | E.cpp:30:21:30:21 | p [post update] [data, buffer] |
| E.cpp:30:28:30:33 | ref arg buffer | E.cpp:30:23:30:26 | data [post update] [buffer] |
| E.cpp:31:10:31:12 | raw | realistic.cpp:41:17:41:17 | o |
| E.cpp:32:10:32:10 | b [buffer] | E.cpp:32:13:32:18 | buffer |
| E.cpp:32:13:32:18 | buffer | realistic.cpp:41:17:41:17 | o |
| E.cpp:33:18:33:19 | & ... [data, buffer] | E.cpp:19:27:19:27 | p [data, buffer] |
| aliasing.cpp:8:23:8:23 | s [m1] | aliasing.cpp:25:17:25:19 | ref arg & ... [m1] |
| aliasing.cpp:9:3:9:3 | s [post update] [m1] | aliasing.cpp:8:23:8:23 | s [m1] |
| aliasing.cpp:9:3:9:3 | s [post update] [m1] | aliasing.cpp:25:17:25:19 | ref arg & ... [m1] |
| aliasing.cpp:9:3:9:22 | ... = ... | aliasing.cpp:9:3:9:3 | s [post update] [m1] |
| aliasing.cpp:9:11:9:20 | call to user_input | aliasing.cpp:9:3:9:22 | ... = ... |
@@ -204,30 +243,42 @@ edges
| aliasing.cpp:25:17:25:19 | ref arg & ... [m1] | aliasing.cpp:29:8:29:9 | s1 [m1] |
| aliasing.cpp:26:19:26:20 | ref arg s2 [m1] | aliasing.cpp:30:8:30:9 | s2 [m1] |
| aliasing.cpp:29:8:29:9 | s1 [m1] | aliasing.cpp:29:11:29:12 | m1 |
| aliasing.cpp:29:11:29:12 | m1 | realistic.cpp:41:17:41:17 | o |
| aliasing.cpp:30:8:30:9 | s2 [m1] | aliasing.cpp:30:11:30:12 | m1 |
| aliasing.cpp:30:11:30:12 | m1 | realistic.cpp:41:17:41:17 | o |
| aliasing.cpp:60:3:60:4 | s2 [post update] [m1] | aliasing.cpp:62:8:62:12 | copy2 [m1] |
| aliasing.cpp:60:3:60:22 | ... = ... | aliasing.cpp:60:3:60:4 | s2 [post update] [m1] |
| aliasing.cpp:60:11:60:20 | call to user_input | aliasing.cpp:60:3:60:22 | ... = ... |
| aliasing.cpp:62:8:62:12 | copy2 [m1] | aliasing.cpp:62:14:62:15 | m1 |
| aliasing.cpp:62:14:62:15 | m1 | realistic.cpp:41:17:41:17 | o |
| aliasing.cpp:92:3:92:3 | w [post update] [s, m1] | aliasing.cpp:93:8:93:8 | w [s, m1] |
| aliasing.cpp:92:3:92:23 | ... = ... | aliasing.cpp:92:5:92:5 | s [post update] [m1] |
| aliasing.cpp:92:5:92:5 | s [post update] [m1] | aliasing.cpp:92:3:92:3 | w [post update] [s, m1] |
| aliasing.cpp:92:12:92:21 | call to user_input | aliasing.cpp:92:3:92:23 | ... = ... |
| aliasing.cpp:93:8:93:8 | w [s, m1] | aliasing.cpp:93:10:93:10 | s [m1] |
| aliasing.cpp:93:10:93:10 | s [m1] | aliasing.cpp:93:12:93:13 | m1 |
| aliasing.cpp:93:12:93:13 | m1 | realistic.cpp:41:17:41:17 | o |
| aliasing.cpp:105:23:105:24 | pa | aliasing.cpp:158:17:158:20 | ref arg data |
| aliasing.cpp:105:23:105:24 | pa | aliasing.cpp:164:17:164:20 | ref arg data |
| aliasing.cpp:105:23:105:24 | pa | aliasing.cpp:175:15:175:22 | ref arg & ... |
| aliasing.cpp:105:23:105:24 | pa | aliasing.cpp:187:15:187:22 | ref arg & ... |
| aliasing.cpp:105:23:105:24 | pa | aliasing.cpp:200:15:200:24 | ref arg & ... |
| aliasing.cpp:106:4:106:5 | pa [inner post update] | aliasing.cpp:158:17:158:20 | ref arg data |
| aliasing.cpp:106:4:106:5 | pa [inner post update] | aliasing.cpp:164:17:164:20 | ref arg data |
| aliasing.cpp:106:4:106:5 | pa [inner post update] | aliasing.cpp:175:15:175:22 | ref arg & ... |
| aliasing.cpp:106:4:106:5 | pa [inner post update] | aliasing.cpp:187:15:187:22 | ref arg & ... |
| aliasing.cpp:106:4:106:5 | pa [inner post update] | aliasing.cpp:200:15:200:24 | ref arg & ... |
| aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:105:23:105:24 | pa |
| aliasing.cpp:106:9:106:18 | call to user_input | aliasing.cpp:106:4:106:5 | pa [inner post update] |
| aliasing.cpp:158:15:158:15 | s [post update] [data] | aliasing.cpp:159:9:159:9 | s [data] |
| aliasing.cpp:158:17:158:20 | ref arg data | aliasing.cpp:158:15:158:15 | s [post update] [data] |
| aliasing.cpp:159:8:159:14 | * ... | realistic.cpp:41:17:41:17 | o |
| aliasing.cpp:159:9:159:9 | s [data] | aliasing.cpp:159:11:159:14 | data |
| aliasing.cpp:159:11:159:14 | data | aliasing.cpp:159:8:159:14 | * ... |
| aliasing.cpp:164:15:164:15 | s [post update] [data] | aliasing.cpp:165:8:165:8 | s [data] |
| aliasing.cpp:164:17:164:20 | ref arg data | aliasing.cpp:164:15:164:15 | s [post update] [data] |
| aliasing.cpp:165:8:165:8 | s [data] | aliasing.cpp:165:10:165:13 | data |
| aliasing.cpp:165:8:165:16 | access to array | realistic.cpp:41:17:41:17 | o |
| aliasing.cpp:165:10:165:13 | data | aliasing.cpp:165:8:165:16 | access to array |
| aliasing.cpp:175:15:175:22 | ref arg & ... | aliasing.cpp:175:21:175:22 | m1 [inner post update] |
| aliasing.cpp:175:16:175:17 | s2 [post update] [s, m1] | aliasing.cpp:176:8:176:9 | s2 [s, m1] |
@@ -235,24 +286,33 @@ edges
| aliasing.cpp:175:21:175:22 | m1 [inner post update] | aliasing.cpp:175:19:175:19 | s [post update] [m1] |
| aliasing.cpp:176:8:176:9 | s2 [s, m1] | aliasing.cpp:176:11:176:11 | s [m1] |
| aliasing.cpp:176:11:176:11 | s [m1] | aliasing.cpp:176:13:176:14 | m1 |
| aliasing.cpp:176:13:176:14 | m1 | realistic.cpp:41:17:41:17 | o |
| aliasing.cpp:187:15:187:22 | ref arg & ... | aliasing.cpp:187:21:187:22 | m1 [inner post update] |
| aliasing.cpp:187:16:187:17 | s2 [post update] [s, m1] | aliasing.cpp:189:8:189:11 | s2_2 [s, m1] |
| aliasing.cpp:187:19:187:19 | s [post update] [m1] | aliasing.cpp:187:16:187:17 | s2 [post update] [s, m1] |
| aliasing.cpp:187:21:187:22 | m1 [inner post update] | aliasing.cpp:187:19:187:19 | s [post update] [m1] |
| aliasing.cpp:189:8:189:11 | s2_2 [s, m1] | aliasing.cpp:189:13:189:13 | s [m1] |
| aliasing.cpp:189:13:189:13 | s [m1] | aliasing.cpp:189:15:189:16 | m1 |
| aliasing.cpp:189:15:189:16 | m1 | realistic.cpp:41:17:41:17 | o |
| aliasing.cpp:200:15:200:24 | ref arg & ... | aliasing.cpp:200:23:200:24 | m1 [inner post update] |
| aliasing.cpp:200:16:200:18 | ps2 [post update] [s, m1] | aliasing.cpp:201:8:201:10 | ps2 [s, m1] |
| aliasing.cpp:200:21:200:21 | s [post update] [m1] | aliasing.cpp:200:16:200:18 | ps2 [post update] [s, m1] |
| aliasing.cpp:200:23:200:24 | m1 [inner post update] | aliasing.cpp:200:21:200:21 | s [post update] [m1] |
| aliasing.cpp:201:8:201:10 | ps2 [s, m1] | aliasing.cpp:201:13:201:13 | s [m1] |
| aliasing.cpp:201:13:201:13 | s [m1] | aliasing.cpp:201:15:201:16 | m1 |
| aliasing.cpp:201:15:201:16 | m1 | realistic.cpp:41:17:41:17 | o |
| arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:7:8:7:13 | access to array |
| arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:8:8:8:13 | access to array |
| arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:9:8:9:11 | * ... |
| arrays.cpp:6:12:6:21 | call to user_input | arrays.cpp:10:8:10:15 | * ... |
| arrays.cpp:7:8:7:13 | access to array | realistic.cpp:41:17:41:17 | o |
| arrays.cpp:8:8:8:13 | access to array | realistic.cpp:41:17:41:17 | o |
| arrays.cpp:9:8:9:11 | * ... | realistic.cpp:41:17:41:17 | o |
| arrays.cpp:10:8:10:15 | * ... | realistic.cpp:41:17:41:17 | o |
| arrays.cpp:15:14:15:23 | call to user_input | arrays.cpp:16:8:16:13 | access to array |
| arrays.cpp:15:14:15:23 | call to user_input | arrays.cpp:17:8:17:13 | access to array |
| arrays.cpp:16:8:16:13 | access to array | realistic.cpp:41:17:41:17 | o |
| arrays.cpp:17:8:17:13 | access to array | realistic.cpp:41:17:41:17 | o |
| arrays.cpp:36:3:36:3 | o [post update] [nested, arr, data] | arrays.cpp:37:8:37:8 | o [nested, arr, data] |
| arrays.cpp:36:3:36:3 | o [post update] [nested, arr, data] | arrays.cpp:38:8:38:8 | o [nested, arr, data] |
| arrays.cpp:36:3:36:17 | access to array [post update] [data] | arrays.cpp:36:12:36:14 | arr [inner post update] [data] |
@@ -261,13 +321,22 @@ edges
| arrays.cpp:36:12:36:14 | arr [inner post update] [data] | arrays.cpp:36:5:36:10 | nested [post update] [arr, data] |
| arrays.cpp:36:26:36:35 | call to user_input | arrays.cpp:36:3:36:37 | ... = ... |
| arrays.cpp:37:8:37:8 | o [nested, arr, data] | arrays.cpp:37:10:37:15 | nested [arr, data] |
| arrays.cpp:37:8:37:8 | o [post update] [nested, arr, data] | arrays.cpp:38:8:38:8 | o [nested, arr, data] |
| arrays.cpp:37:8:37:22 | access to array [data] | arrays.cpp:37:24:37:27 | data |
| arrays.cpp:37:8:37:22 | access to array [data] | arrays.cpp:37:24:37:27 | data |
| arrays.cpp:37:8:37:22 | access to array [post update] [data] | arrays.cpp:37:17:37:19 | arr [inner post update] [data] |
| arrays.cpp:37:10:37:15 | nested [arr, data] | arrays.cpp:37:17:37:19 | arr [data] |
| arrays.cpp:37:10:37:15 | nested [post update] [arr, data] | arrays.cpp:37:8:37:8 | o [post update] [nested, arr, data] |
| arrays.cpp:37:17:37:19 | arr [data] | arrays.cpp:37:8:37:22 | access to array [data] |
| arrays.cpp:37:17:37:19 | arr [inner post update] [data] | arrays.cpp:37:10:37:15 | nested [post update] [arr, data] |
| arrays.cpp:37:24:37:27 | data | arrays.cpp:37:24:37:27 | ref arg data |
| arrays.cpp:37:24:37:27 | data | realistic.cpp:41:17:41:17 | o |
| arrays.cpp:37:24:37:27 | ref arg data | arrays.cpp:37:8:37:22 | access to array [post update] [data] |
| arrays.cpp:38:8:38:8 | o [nested, arr, data] | arrays.cpp:38:10:38:15 | nested [arr, data] |
| arrays.cpp:38:8:38:22 | access to array [data] | arrays.cpp:38:24:38:27 | data |
| arrays.cpp:38:10:38:15 | nested [arr, data] | arrays.cpp:38:17:38:19 | arr [data] |
| arrays.cpp:38:17:38:19 | arr [data] | arrays.cpp:38:8:38:22 | access to array [data] |
| arrays.cpp:38:24:38:27 | data | realistic.cpp:41:17:41:17 | o |
| arrays.cpp:42:3:42:3 | o [post update] [indirect, arr, data] | arrays.cpp:43:8:43:8 | o [indirect, arr, data] |
| arrays.cpp:42:3:42:3 | o [post update] [indirect, arr, data] | arrays.cpp:44:8:44:8 | o [indirect, arr, data] |
| arrays.cpp:42:3:42:20 | access to array [post update] [data] | arrays.cpp:42:15:42:17 | arr [inner post update] [data] |
@@ -276,14 +345,24 @@ edges
| arrays.cpp:42:15:42:17 | arr [inner post update] [data] | arrays.cpp:42:5:42:12 | indirect [post update] [arr, data] |
| arrays.cpp:42:29:42:38 | call to user_input | arrays.cpp:42:3:42:40 | ... = ... |
| arrays.cpp:43:8:43:8 | o [indirect, arr, data] | arrays.cpp:43:10:43:17 | indirect [arr, data] |
| arrays.cpp:43:8:43:8 | o [post update] [indirect, arr, data] | arrays.cpp:44:8:44:8 | o [indirect, arr, data] |
| arrays.cpp:43:8:43:25 | access to array [data] | arrays.cpp:43:27:43:30 | data |
| arrays.cpp:43:8:43:25 | access to array [data] | arrays.cpp:43:27:43:30 | data |
| arrays.cpp:43:8:43:25 | access to array [post update] [data] | arrays.cpp:43:20:43:22 | arr [inner post update] [data] |
| arrays.cpp:43:10:43:17 | indirect [arr, data] | arrays.cpp:43:20:43:22 | arr [data] |
| arrays.cpp:43:10:43:17 | indirect [post update] [arr, data] | arrays.cpp:43:8:43:8 | o [post update] [indirect, arr, data] |
| arrays.cpp:43:20:43:22 | arr [data] | arrays.cpp:43:8:43:25 | access to array [data] |
| arrays.cpp:43:20:43:22 | arr [inner post update] [data] | arrays.cpp:43:10:43:17 | indirect [post update] [arr, data] |
| arrays.cpp:43:27:43:30 | data | arrays.cpp:43:27:43:30 | ref arg data |
| arrays.cpp:43:27:43:30 | data | realistic.cpp:41:17:41:17 | o |
| arrays.cpp:43:27:43:30 | ref arg data | arrays.cpp:43:8:43:25 | access to array [post update] [data] |
| arrays.cpp:44:8:44:8 | o [indirect, arr, data] | arrays.cpp:44:10:44:17 | indirect [arr, data] |
| arrays.cpp:44:8:44:25 | access to array [data] | arrays.cpp:44:27:44:30 | data |
| arrays.cpp:44:10:44:17 | indirect [arr, data] | arrays.cpp:44:20:44:22 | arr [data] |
| arrays.cpp:44:20:44:22 | arr [data] | arrays.cpp:44:8:44:25 | access to array [data] |
| arrays.cpp:44:27:44:30 | data | realistic.cpp:41:17:41:17 | o |
| by_reference.cpp:11:48:11:52 | value | by_reference.cpp:12:5:12:16 | ... = ... |
| by_reference.cpp:12:5:12:5 | s [post update] [a] | by_reference.cpp:11:39:11:39 | s [a] |
| by_reference.cpp:12:5:12:16 | ... = ... | by_reference.cpp:12:5:12:5 | s [post update] [a] |
| by_reference.cpp:15:26:15:30 | value | by_reference.cpp:16:5:16:19 | ... = ... |
| by_reference.cpp:16:5:16:19 | ... = ... | by_reference.cpp:16:5:16:8 | this [post update] [a] |
@@ -308,21 +387,30 @@ edges
| by_reference.cpp:50:17:50:26 | call to user_input | by_reference.cpp:50:3:50:3 | ref arg s [a] |
| by_reference.cpp:51:8:51:8 | s [a] | by_reference.cpp:35:9:35:19 | this [a] |
| by_reference.cpp:51:8:51:8 | s [a] | by_reference.cpp:51:10:51:20 | call to getDirectly |
| by_reference.cpp:51:10:51:20 | call to getDirectly | realistic.cpp:41:17:41:17 | o |
| by_reference.cpp:56:3:56:3 | ref arg s [a] | by_reference.cpp:57:8:57:8 | s [a] |
| by_reference.cpp:56:19:56:28 | call to user_input | by_reference.cpp:19:28:19:32 | value |
| by_reference.cpp:56:19:56:28 | call to user_input | by_reference.cpp:56:3:56:3 | ref arg s [a] |
| by_reference.cpp:57:8:57:8 | s [a] | by_reference.cpp:39:9:39:21 | this [a] |
| by_reference.cpp:57:8:57:8 | s [a] | by_reference.cpp:57:10:57:22 | call to getIndirectly |
| by_reference.cpp:57:10:57:22 | call to getIndirectly | realistic.cpp:41:17:41:17 | o |
| by_reference.cpp:62:3:62:3 | ref arg s [a] | by_reference.cpp:63:8:63:8 | s [a] |
| by_reference.cpp:62:25:62:34 | call to user_input | by_reference.cpp:23:34:23:38 | value |
| by_reference.cpp:62:25:62:34 | call to user_input | by_reference.cpp:62:3:62:3 | ref arg s [a] |
| by_reference.cpp:63:8:63:8 | s [a] | by_reference.cpp:43:9:43:27 | this [a] |
| by_reference.cpp:63:8:63:8 | s [a] | by_reference.cpp:63:10:63:28 | call to getThroughNonMember |
| by_reference.cpp:63:10:63:28 | call to getThroughNonMember | realistic.cpp:41:17:41:17 | o |
| by_reference.cpp:68:17:68:18 | ref arg & ... [a] | by_reference.cpp:69:22:69:23 | & ... [a] |
| by_reference.cpp:68:21:68:30 | call to user_input | by_reference.cpp:11:48:11:52 | value |
| by_reference.cpp:68:21:68:30 | call to user_input | by_reference.cpp:68:17:68:18 | ref arg & ... [a] |
| by_reference.cpp:69:8:69:20 | call to nonMemberGetA | realistic.cpp:41:17:41:17 | o |
| by_reference.cpp:69:22:69:23 | & ... [a] | by_reference.cpp:31:46:31:46 | s [a] |
| by_reference.cpp:69:22:69:23 | & ... [a] | by_reference.cpp:69:8:69:20 | call to nonMemberGetA |
| by_reference.cpp:83:31:83:35 | inner [a] | by_reference.cpp:102:21:102:39 | ref arg & ... [a] |
| by_reference.cpp:83:31:83:35 | inner [a] | by_reference.cpp:103:27:103:35 | ref arg inner_ptr [a] |
| by_reference.cpp:83:31:83:35 | inner [a] | by_reference.cpp:106:21:106:41 | ref arg & ... [a] |
| by_reference.cpp:83:31:83:35 | inner [a] | by_reference.cpp:107:29:107:37 | ref arg inner_ptr [a] |
| by_reference.cpp:84:3:84:7 | inner [post update] [a] | by_reference.cpp:83:31:83:35 | inner [a] |
| by_reference.cpp:84:3:84:7 | inner [post update] [a] | by_reference.cpp:102:21:102:39 | ref arg & ... [a] |
| by_reference.cpp:84:3:84:7 | inner [post update] [a] | by_reference.cpp:103:27:103:35 | ref arg inner_ptr [a] |
| by_reference.cpp:84:3:84:7 | inner [post update] [a] | by_reference.cpp:106:21:106:41 | ref arg & ... [a] |
@@ -340,8 +428,11 @@ edges
| by_reference.cpp:88:3:88:7 | inner [post update] [a] | by_reference.cpp:127:21:127:38 | ref arg * ... [a] |
| by_reference.cpp:88:3:88:24 | ... = ... | by_reference.cpp:88:3:88:7 | inner [post update] [a] |
| by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:88:3:88:24 | ... = ... |
| by_reference.cpp:91:25:91:26 | pa | by_reference.cpp:104:15:104:22 | ref arg & ... |
| by_reference.cpp:91:25:91:26 | pa | by_reference.cpp:108:15:108:24 | ref arg & ... |
| by_reference.cpp:92:4:92:5 | pa [inner post update] | by_reference.cpp:104:15:104:22 | ref arg & ... |
| by_reference.cpp:92:4:92:5 | pa [inner post update] | by_reference.cpp:108:15:108:24 | ref arg & ... |
| by_reference.cpp:92:9:92:18 | call to user_input | by_reference.cpp:91:25:91:26 | pa |
| by_reference.cpp:92:9:92:18 | call to user_input | by_reference.cpp:92:4:92:5 | pa [inner post update] |
| by_reference.cpp:95:25:95:26 | pa | by_reference.cpp:124:21:124:21 | ref arg a |
| by_reference.cpp:95:25:95:26 | pa | by_reference.cpp:128:23:128:23 | ref arg a |
@@ -364,14 +455,20 @@ edges
| by_reference.cpp:108:24:108:24 | a [inner post update] | by_reference.cpp:108:16:108:21 | pouter [post update] [a] |
| by_reference.cpp:110:8:110:12 | outer [inner_nested, a] | by_reference.cpp:110:14:110:25 | inner_nested [a] |
| by_reference.cpp:110:14:110:25 | inner_nested [a] | by_reference.cpp:110:27:110:27 | a |
| by_reference.cpp:110:27:110:27 | a | realistic.cpp:41:17:41:17 | o |
| by_reference.cpp:111:8:111:12 | outer [inner_ptr, a] | by_reference.cpp:111:14:111:22 | inner_ptr [a] |
| by_reference.cpp:111:14:111:22 | inner_ptr [a] | by_reference.cpp:111:25:111:25 | a |
| by_reference.cpp:111:25:111:25 | a | realistic.cpp:41:17:41:17 | o |
| by_reference.cpp:112:8:112:12 | outer [a] | by_reference.cpp:112:14:112:14 | a |
| by_reference.cpp:112:14:112:14 | a | realistic.cpp:41:17:41:17 | o |
| by_reference.cpp:114:8:114:13 | pouter [inner_nested, a] | by_reference.cpp:114:16:114:27 | inner_nested [a] |
| by_reference.cpp:114:16:114:27 | inner_nested [a] | by_reference.cpp:114:29:114:29 | a |
| by_reference.cpp:114:29:114:29 | a | realistic.cpp:41:17:41:17 | o |
| by_reference.cpp:115:8:115:13 | pouter [inner_ptr, a] | by_reference.cpp:115:16:115:24 | inner_ptr [a] |
| by_reference.cpp:115:16:115:24 | inner_ptr [a] | by_reference.cpp:115:27:115:27 | a |
| by_reference.cpp:115:27:115:27 | a | realistic.cpp:41:17:41:17 | o |
| by_reference.cpp:116:8:116:13 | pouter [a] | by_reference.cpp:116:16:116:16 | a |
| by_reference.cpp:116:16:116:16 | a | realistic.cpp:41:17:41:17 | o |
| by_reference.cpp:122:21:122:25 | outer [post update] [inner_nested, a] | by_reference.cpp:130:8:130:12 | outer [inner_nested, a] |
| by_reference.cpp:122:27:122:38 | ref arg inner_nested [a] | by_reference.cpp:122:21:122:25 | outer [post update] [inner_nested, a] |
| by_reference.cpp:123:21:123:36 | ref arg * ... [a] | by_reference.cpp:123:28:123:36 | inner_ptr [inner post update] [a] |
@@ -388,14 +485,20 @@ edges
| by_reference.cpp:128:23:128:23 | ref arg a | by_reference.cpp:128:15:128:20 | pouter [post update] [a] |
| by_reference.cpp:130:8:130:12 | outer [inner_nested, a] | by_reference.cpp:130:14:130:25 | inner_nested [a] |
| by_reference.cpp:130:14:130:25 | inner_nested [a] | by_reference.cpp:130:27:130:27 | a |
| by_reference.cpp:130:27:130:27 | a | realistic.cpp:41:17:41:17 | o |
| by_reference.cpp:131:8:131:12 | outer [inner_ptr, a] | by_reference.cpp:131:14:131:22 | inner_ptr [a] |
| by_reference.cpp:131:14:131:22 | inner_ptr [a] | by_reference.cpp:131:25:131:25 | a |
| by_reference.cpp:131:25:131:25 | a | realistic.cpp:41:17:41:17 | o |
| by_reference.cpp:132:8:132:12 | outer [a] | by_reference.cpp:132:14:132:14 | a |
| by_reference.cpp:132:14:132:14 | a | realistic.cpp:41:17:41:17 | o |
| by_reference.cpp:134:8:134:13 | pouter [inner_nested, a] | by_reference.cpp:134:16:134:27 | inner_nested [a] |
| by_reference.cpp:134:16:134:27 | inner_nested [a] | by_reference.cpp:134:29:134:29 | a |
| by_reference.cpp:134:29:134:29 | a | realistic.cpp:41:17:41:17 | o |
| by_reference.cpp:135:8:135:13 | pouter [inner_ptr, a] | by_reference.cpp:135:16:135:24 | inner_ptr [a] |
| by_reference.cpp:135:16:135:24 | inner_ptr [a] | by_reference.cpp:135:27:135:27 | a |
| by_reference.cpp:135:27:135:27 | a | realistic.cpp:41:17:41:17 | o |
| by_reference.cpp:136:8:136:13 | pouter [a] | by_reference.cpp:136:16:136:16 | a |
| by_reference.cpp:136:16:136:16 | a | realistic.cpp:41:17:41:17 | o |
| complex.cpp:9:7:9:7 | this [a_] | complex.cpp:9:20:9:21 | this [a_] |
| complex.cpp:9:20:9:21 | this [a_] | complex.cpp:9:20:9:21 | a_ |
| complex.cpp:10:7:10:7 | this [b_] | complex.cpp:10:20:10:21 | this [b_] |
@@ -439,26 +542,31 @@ edges
| complex.cpp:65:7:65:8 | b3 [inner, f, a_] | complex.cpp:40:17:40:17 | b [inner, f, a_] |
| complex.cpp:65:7:65:8 | b3 [inner, f, b_] | complex.cpp:40:17:40:17 | b [inner, f, b_] |
| conflated.cpp:19:19:19:21 | ref arg raw | conflated.cpp:20:8:20:10 | raw |
| conflated.cpp:20:8:20:10 | raw | realistic.cpp:41:17:41:17 | o |
| conflated.cpp:29:3:29:4 | pa [post update] [x] | conflated.cpp:30:8:30:9 | pa [x] |
| conflated.cpp:29:3:29:22 | ... = ... | conflated.cpp:29:3:29:4 | pa [post update] [x] |
| conflated.cpp:29:11:29:20 | call to user_input | conflated.cpp:29:3:29:22 | ... = ... |
| conflated.cpp:30:8:30:9 | pa [x] | conflated.cpp:30:12:30:12 | x |
| conflated.cpp:30:12:30:12 | x | realistic.cpp:41:17:41:17 | o |
| conflated.cpp:36:3:36:4 | pa [post update] [x] | conflated.cpp:37:8:37:9 | pa [x] |
| conflated.cpp:36:3:36:22 | ... = ... | conflated.cpp:36:3:36:4 | pa [post update] [x] |
| conflated.cpp:36:11:36:20 | call to user_input | conflated.cpp:36:3:36:22 | ... = ... |
| conflated.cpp:37:8:37:9 | pa [x] | conflated.cpp:37:12:37:12 | x |
| conflated.cpp:37:12:37:12 | x | realistic.cpp:41:17:41:17 | o |
| conflated.cpp:54:3:54:4 | ll [post update] [next, y] | conflated.cpp:55:8:55:9 | ll [next, y] |
| conflated.cpp:54:3:54:28 | ... = ... | conflated.cpp:54:7:54:10 | next [post update] [y] |
| conflated.cpp:54:7:54:10 | next [post update] [y] | conflated.cpp:54:3:54:4 | ll [post update] [next, y] |
| conflated.cpp:54:17:54:26 | call to user_input | conflated.cpp:54:3:54:28 | ... = ... |
| conflated.cpp:55:8:55:9 | ll [next, y] | conflated.cpp:55:12:55:15 | next [y] |
| conflated.cpp:55:12:55:15 | next [y] | conflated.cpp:55:18:55:18 | y |
| conflated.cpp:55:18:55:18 | y | realistic.cpp:41:17:41:17 | o |
| conflated.cpp:60:3:60:4 | ll [post update] [next, y] | conflated.cpp:61:8:61:9 | ll [next, y] |
| conflated.cpp:60:3:60:28 | ... = ... | conflated.cpp:60:7:60:10 | next [post update] [y] |
| conflated.cpp:60:7:60:10 | next [post update] [y] | conflated.cpp:60:3:60:4 | ll [post update] [next, y] |
| conflated.cpp:60:17:60:26 | call to user_input | conflated.cpp:60:3:60:28 | ... = ... |
| conflated.cpp:61:8:61:9 | ll [next, y] | conflated.cpp:61:12:61:15 | next [y] |
| conflated.cpp:61:12:61:15 | next [y] | conflated.cpp:61:18:61:18 | y |
| conflated.cpp:61:18:61:18 | y | realistic.cpp:41:17:41:17 | o |
| constructors.cpp:18:9:18:9 | this [a_] | constructors.cpp:18:22:18:23 | this [a_] |
| constructors.cpp:18:22:18:23 | this [a_] | constructors.cpp:18:22:18:23 | a_ |
| constructors.cpp:19:9:19:9 | this [b_] | constructors.cpp:19:22:19:23 | this [b_] |
@@ -492,6 +600,7 @@ edges
| qualifiers.cpp:9:21:9:25 | value | qualifiers.cpp:9:30:9:44 | ... = ... |
| qualifiers.cpp:9:30:9:44 | ... = ... | qualifiers.cpp:9:30:9:33 | this [post update] [a] |
| qualifiers.cpp:12:40:12:44 | value | qualifiers.cpp:12:49:12:64 | ... = ... |
| qualifiers.cpp:12:49:12:53 | inner [post update] [a] | qualifiers.cpp:12:27:12:31 | inner [a] |
| qualifiers.cpp:12:49:12:64 | ... = ... | qualifiers.cpp:12:49:12:53 | inner [post update] [a] |
| qualifiers.cpp:13:42:13:46 | value | qualifiers.cpp:13:51:13:65 | ... = ... |
| qualifiers.cpp:13:51:13:55 | inner [post update] [a] | qualifiers.cpp:13:29:13:33 | inner [a] |
@@ -502,18 +611,21 @@ edges
| qualifiers.cpp:22:27:22:36 | call to user_input | qualifiers.cpp:22:5:22:38 | ... = ... |
| qualifiers.cpp:23:10:23:14 | outer [inner, a] | qualifiers.cpp:23:16:23:20 | inner [a] |
| qualifiers.cpp:23:16:23:20 | inner [a] | qualifiers.cpp:23:23:23:23 | a |
| qualifiers.cpp:23:23:23:23 | a | realistic.cpp:41:17:41:17 | o |
| qualifiers.cpp:27:5:27:9 | ref arg outer [inner, a] | qualifiers.cpp:28:10:28:14 | outer [inner, a] |
| qualifiers.cpp:27:11:27:18 | ref arg call to getInner [a] | qualifiers.cpp:27:5:27:9 | ref arg outer [inner, a] |
| qualifiers.cpp:27:28:27:37 | call to user_input | qualifiers.cpp:9:21:9:25 | value |
| qualifiers.cpp:27:28:27:37 | call to user_input | qualifiers.cpp:27:11:27:18 | ref arg call to getInner [a] |
| qualifiers.cpp:28:10:28:14 | outer [inner, a] | qualifiers.cpp:28:16:28:20 | inner [a] |
| qualifiers.cpp:28:16:28:20 | inner [a] | qualifiers.cpp:28:23:28:23 | a |
| qualifiers.cpp:28:23:28:23 | a | realistic.cpp:41:17:41:17 | o |
| qualifiers.cpp:32:17:32:21 | ref arg outer [inner, a] | qualifiers.cpp:33:10:33:14 | outer [inner, a] |
| qualifiers.cpp:32:23:32:30 | ref arg call to getInner [a] | qualifiers.cpp:32:17:32:21 | ref arg outer [inner, a] |
| qualifiers.cpp:32:35:32:44 | call to user_input | qualifiers.cpp:12:40:12:44 | value |
| qualifiers.cpp:32:35:32:44 | call to user_input | qualifiers.cpp:32:23:32:30 | ref arg call to getInner [a] |
| qualifiers.cpp:33:10:33:14 | outer [inner, a] | qualifiers.cpp:33:16:33:20 | inner [a] |
| qualifiers.cpp:33:16:33:20 | inner [a] | qualifiers.cpp:33:23:33:23 | a |
| qualifiers.cpp:33:23:33:23 | a | realistic.cpp:41:17:41:17 | o |
| qualifiers.cpp:37:19:37:35 | ref arg * ... [a] | qualifiers.cpp:37:26:37:33 | call to getInner [inner post update] [a] |
| qualifiers.cpp:37:20:37:24 | ref arg outer [inner, a] | qualifiers.cpp:38:10:38:14 | outer [inner, a] |
| qualifiers.cpp:37:26:37:33 | call to getInner [inner post update] [a] | qualifiers.cpp:37:20:37:24 | ref arg outer [inner, a] |
@@ -521,6 +633,7 @@ edges
| qualifiers.cpp:37:38:37:47 | call to user_input | qualifiers.cpp:37:19:37:35 | ref arg * ... [a] |
| qualifiers.cpp:38:10:38:14 | outer [inner, a] | qualifiers.cpp:38:16:38:20 | inner [a] |
| qualifiers.cpp:38:16:38:20 | inner [a] | qualifiers.cpp:38:23:38:23 | a |
| qualifiers.cpp:38:23:38:23 | a | realistic.cpp:41:17:41:17 | o |
| qualifiers.cpp:42:5:42:40 | ... = ... | qualifiers.cpp:42:6:42:22 | * ... [post update] [a] |
| qualifiers.cpp:42:6:42:22 | * ... [post update] [a] | qualifiers.cpp:42:13:42:20 | call to getInner [inner post update] [a] |
| qualifiers.cpp:42:7:42:11 | ref arg outer [inner, a] | qualifiers.cpp:43:10:43:14 | outer [inner, a] |
@@ -528,12 +641,15 @@ edges
| qualifiers.cpp:42:29:42:38 | call to user_input | qualifiers.cpp:42:5:42:40 | ... = ... |
| qualifiers.cpp:43:10:43:14 | outer [inner, a] | qualifiers.cpp:43:16:43:20 | inner [a] |
| qualifiers.cpp:43:16:43:20 | inner [a] | qualifiers.cpp:43:23:43:23 | a |
| qualifiers.cpp:43:23:43:23 | a | realistic.cpp:41:17:41:17 | o |
| qualifiers.cpp:47:5:47:42 | ... = ... | qualifiers.cpp:47:15:47:22 | call to getInner [post update] [a] |
| qualifiers.cpp:47:6:47:11 | ref arg & ... [inner, a] | qualifiers.cpp:48:10:48:14 | outer [inner, a] |
| qualifiers.cpp:47:15:47:22 | call to getInner [post update] [a] | qualifiers.cpp:47:6:47:11 | ref arg & ... [inner, a] |
| qualifiers.cpp:47:31:47:40 | call to user_input | qualifiers.cpp:47:5:47:42 | ... = ... |
| qualifiers.cpp:48:10:48:14 | outer [inner, a] | qualifiers.cpp:48:16:48:20 | inner [a] |
| qualifiers.cpp:48:16:48:20 | inner [a] | qualifiers.cpp:48:23:48:23 | a |
| qualifiers.cpp:48:23:48:23 | a | realistic.cpp:41:17:41:17 | o |
| realistic.cpp:41:17:41:17 | o | realistic.cpp:41:17:41:17 | o |
| realistic.cpp:53:9:53:11 | foo [post update] [bar, baz, userInput, bufferLen] | realistic.cpp:61:21:61:23 | foo [bar, baz, userInput, bufferLen] |
| realistic.cpp:53:9:53:18 | access to array [post update] [baz, userInput, bufferLen] | realistic.cpp:53:13:53:15 | bar [inner post update] [baz, userInput, bufferLen] |
| realistic.cpp:53:9:53:66 | ... = ... | realistic.cpp:53:25:53:33 | userInput [post update] [bufferLen] |
@@ -542,10 +658,19 @@ edges
| realistic.cpp:53:25:53:33 | userInput [post update] [bufferLen] | realistic.cpp:53:20:53:22 | baz [post update] [userInput, bufferLen] |
| realistic.cpp:53:55:53:64 | call to user_input | realistic.cpp:53:9:53:66 | ... = ... |
| realistic.cpp:61:21:61:23 | foo [bar, baz, userInput, bufferLen] | realistic.cpp:61:25:61:27 | bar [baz, userInput, bufferLen] |
| realistic.cpp:61:21:61:23 | foo [post update] [bar, baz, userInput, bufferLen] | realistic.cpp:61:21:61:23 | foo [bar, baz, userInput, bufferLen] |
| realistic.cpp:61:21:61:30 | access to array [baz, userInput, bufferLen] | realistic.cpp:61:32:61:34 | baz [userInput, bufferLen] |
| realistic.cpp:61:21:61:30 | access to array [post update] [baz, userInput, bufferLen] | realistic.cpp:61:25:61:27 | bar [inner post update] [baz, userInput, bufferLen] |
| realistic.cpp:61:25:61:27 | bar [baz, userInput, bufferLen] | realistic.cpp:61:21:61:30 | access to array [baz, userInput, bufferLen] |
| realistic.cpp:61:25:61:27 | bar [inner post update] [baz, userInput, bufferLen] | realistic.cpp:61:21:61:23 | foo [post update] [bar, baz, userInput, bufferLen] |
| realistic.cpp:61:32:61:34 | baz [post update] [userInput, bufferLen] | realistic.cpp:61:21:61:30 | access to array [post update] [baz, userInput, bufferLen] |
| realistic.cpp:61:32:61:34 | baz [userInput, bufferLen] | realistic.cpp:61:37:61:45 | userInput [bufferLen] |
| realistic.cpp:61:37:61:45 | userInput [bufferLen] | realistic.cpp:61:47:61:55 | bufferLen |
| realistic.cpp:61:37:61:45 | userInput [bufferLen] | realistic.cpp:61:47:61:55 | bufferLen |
| realistic.cpp:61:37:61:45 | userInput [post update] [bufferLen] | realistic.cpp:61:32:61:34 | baz [post update] [userInput, bufferLen] |
| realistic.cpp:61:47:61:55 | bufferLen | realistic.cpp:41:17:41:17 | o |
| realistic.cpp:61:47:61:55 | bufferLen | realistic.cpp:61:47:61:55 | ref arg bufferLen |
| realistic.cpp:61:47:61:55 | ref arg bufferLen | realistic.cpp:61:37:61:45 | userInput [post update] [bufferLen] |
| simple.cpp:18:9:18:9 | this [a_] | simple.cpp:18:22:18:23 | this [a_] |
| simple.cpp:18:22:18:23 | this [a_] | simple.cpp:18:22:18:23 | a_ |
| simple.cpp:19:9:19:9 | this [b_] | simple.cpp:19:22:19:23 | this [b_] |
@@ -593,14 +718,28 @@ edges
| simple.cpp:92:5:92:22 | ... = ... | simple.cpp:92:5:92:5 | a [post update] [i] |
| simple.cpp:92:11:92:20 | call to user_input | simple.cpp:92:5:92:22 | ... = ... |
| simple.cpp:94:10:94:11 | a2 [i] | simple.cpp:94:13:94:13 | i |
| struct_init.c:14:24:14:25 | ab [a] | struct_init.c:14:24:14:25 | ab [a] |
| struct_init.c:14:24:14:25 | ab [a] | struct_init.c:15:8:15:9 | ab [a] |
| struct_init.c:15:8:15:9 | ab [a] | struct_init.c:15:12:15:12 | a |
| struct_init.c:15:8:15:9 | ab [a] | struct_init.c:15:12:15:12 | a |
| struct_init.c:15:8:15:9 | ab [post update] [a] | struct_init.c:14:24:14:25 | ab [a] |
| struct_init.c:15:12:15:12 | a | realistic.cpp:41:17:41:17 | o |
| struct_init.c:15:12:15:12 | a | struct_init.c:15:12:15:12 | ref arg a |
| struct_init.c:15:12:15:12 | ref arg a | struct_init.c:15:8:15:9 | ab [post update] [a] |
| struct_init.c:20:17:20:36 | {...} [a] | struct_init.c:22:8:22:9 | ab [a] |
| struct_init.c:20:17:20:36 | {...} [a] | struct_init.c:24:10:24:12 | & ... [a] |
| struct_init.c:20:17:20:36 | {...} [a] | struct_init.c:28:5:28:7 | & ... [a] |
| struct_init.c:20:20:20:29 | call to user_input | struct_init.c:20:17:20:36 | {...} [a] |
| struct_init.c:22:8:22:9 | ab [a] | struct_init.c:22:11:22:11 | a |
| struct_init.c:22:8:22:9 | ab [a] | struct_init.c:22:11:22:11 | a |
| struct_init.c:22:8:22:9 | ab [post update] [a] | struct_init.c:24:10:24:12 | & ... [a] |
| struct_init.c:22:8:22:9 | ab [post update] [a] | struct_init.c:28:5:28:7 | & ... [a] |
| struct_init.c:22:11:22:11 | a | realistic.cpp:41:17:41:17 | o |
| struct_init.c:22:11:22:11 | a | struct_init.c:22:11:22:11 | ref arg a |
| struct_init.c:22:11:22:11 | ref arg a | struct_init.c:22:8:22:9 | ab [post update] [a] |
| struct_init.c:24:10:24:12 | & ... [a] | struct_init.c:14:24:14:25 | ab [a] |
| struct_init.c:24:10:24:12 | & ... [a] | struct_init.c:24:10:24:12 | ref arg & ... [a] |
| struct_init.c:24:10:24:12 | ref arg & ... [a] | struct_init.c:28:5:28:7 | & ... [a] |
| struct_init.c:26:23:29:3 | {...} [nestedAB, a] | struct_init.c:31:8:31:12 | outer [nestedAB, a] |
| struct_init.c:26:23:29:3 | {...} [nestedAB, a] | struct_init.c:36:11:36:15 | outer [nestedAB, a] |
| struct_init.c:26:23:29:3 | {...} [pointerAB, a] | struct_init.c:33:8:33:12 | outer [pointerAB, a] |
@@ -608,9 +747,16 @@ edges
| struct_init.c:27:7:27:16 | call to user_input | struct_init.c:27:5:27:23 | {...} [a] |
| struct_init.c:28:5:28:7 | & ... [a] | struct_init.c:26:23:29:3 | {...} [pointerAB, a] |
| struct_init.c:31:8:31:12 | outer [nestedAB, a] | struct_init.c:31:14:31:21 | nestedAB [a] |
| struct_init.c:31:8:31:12 | outer [post update] [nestedAB, a] | struct_init.c:36:11:36:15 | outer [nestedAB, a] |
| struct_init.c:31:14:31:21 | nestedAB [a] | struct_init.c:31:23:31:23 | a |
| struct_init.c:31:14:31:21 | nestedAB [a] | struct_init.c:31:23:31:23 | a |
| struct_init.c:31:14:31:21 | nestedAB [post update] [a] | struct_init.c:31:8:31:12 | outer [post update] [nestedAB, a] |
| struct_init.c:31:23:31:23 | a | realistic.cpp:41:17:41:17 | o |
| struct_init.c:31:23:31:23 | a | struct_init.c:31:23:31:23 | ref arg a |
| struct_init.c:31:23:31:23 | ref arg a | struct_init.c:31:14:31:21 | nestedAB [post update] [a] |
| struct_init.c:33:8:33:12 | outer [pointerAB, a] | struct_init.c:33:14:33:22 | pointerAB [a] |
| struct_init.c:33:14:33:22 | pointerAB [a] | struct_init.c:33:25:33:25 | a |
| struct_init.c:33:25:33:25 | a | realistic.cpp:41:17:41:17 | o |
| struct_init.c:36:10:36:24 | & ... [a] | struct_init.c:14:24:14:25 | ab [a] |
| struct_init.c:36:11:36:15 | outer [nestedAB, a] | struct_init.c:36:17:36:24 | nestedAB [a] |
| struct_init.c:36:17:36:24 | nestedAB [a] | struct_init.c:36:10:36:24 | & ... [a] |
@@ -674,12 +820,14 @@ nodes
| A.cpp:107:16:107:16 | a | semmle.label | a |
| A.cpp:120:12:120:13 | c1 [a] | semmle.label | c1 [a] |
| A.cpp:120:16:120:16 | a | semmle.label | a |
| A.cpp:124:14:124:14 | b [c] | semmle.label | b [c] |
| A.cpp:126:5:126:5 | ref arg b [c] | semmle.label | ref arg b [c] |
| A.cpp:126:12:126:18 | new | semmle.label | new |
| A.cpp:131:8:131:8 | ref arg b [c] | semmle.label | ref arg b [c] |
| A.cpp:132:10:132:10 | b [c] | semmle.label | b [c] |
| A.cpp:132:13:132:13 | c | semmle.label | c |
| A.cpp:140:13:140:13 | b | semmle.label | b |
| A.cpp:140:13:140:13 | b [c] | semmle.label | b [c] |
| A.cpp:142:7:142:7 | b [post update] [c] | semmle.label | b [post update] [c] |
| A.cpp:142:7:142:20 | ... = ... | semmle.label | ... = ... |
| A.cpp:142:14:142:20 | new | semmle.label | new |
@@ -695,8 +843,12 @@ nodes
| A.cpp:151:12:151:24 | call to D [b] | semmle.label | call to D [b] |
| A.cpp:151:18:151:18 | b | semmle.label | b |
| A.cpp:151:18:151:18 | ref arg b [c] | semmle.label | ref arg b [c] |
| A.cpp:152:10:152:10 | d [b, c] | semmle.label | d [b, c] |
| A.cpp:152:10:152:10 | d [b] | semmle.label | d [b] |
| A.cpp:152:10:152:10 | d [post update] [b, c] | semmle.label | d [post update] [b, c] |
| A.cpp:152:13:152:13 | b | semmle.label | b |
| A.cpp:152:13:152:13 | b [c] | semmle.label | b [c] |
| A.cpp:152:13:152:13 | ref arg b [c] | semmle.label | ref arg b [c] |
| A.cpp:153:10:153:10 | d [b, c] | semmle.label | d [b, c] |
| A.cpp:153:13:153:13 | b [c] | semmle.label | b [c] |
| A.cpp:153:16:153:16 | c | semmle.label | c |
@@ -710,15 +862,24 @@ nodes
| A.cpp:162:18:162:40 | call to MyList [next, next, head] | semmle.label | call to MyList [next, next, head] |
| A.cpp:162:38:162:39 | l2 [next, head] | semmle.label | l2 [next, head] |
| A.cpp:165:10:165:11 | l3 [next, next, head] | semmle.label | l3 [next, next, head] |
| A.cpp:165:10:165:11 | l3 [post update] [next, next, head] | semmle.label | l3 [post update] [next, next, head] |
| A.cpp:165:14:165:17 | next [next, head] | semmle.label | next [next, head] |
| A.cpp:165:14:165:17 | next [post update] [next, head] | semmle.label | next [post update] [next, head] |
| A.cpp:165:20:165:23 | next [head] | semmle.label | next [head] |
| A.cpp:165:20:165:23 | next [post update] [head] | semmle.label | next [post update] [head] |
| A.cpp:165:26:165:29 | head | semmle.label | head |
| A.cpp:165:26:165:29 | head | semmle.label | head |
| A.cpp:165:26:165:29 | ref arg head | semmle.label | ref arg head |
| A.cpp:167:44:167:44 | l [next, head] | semmle.label | l [next, head] |
| A.cpp:167:44:167:44 | l [next, next, head] | semmle.label | l [next, next, head] |
| A.cpp:167:47:167:50 | next [head] | semmle.label | next [head] |
| A.cpp:167:47:167:50 | next [next, head] | semmle.label | next [next, head] |
| A.cpp:169:12:169:12 | l [head] | semmle.label | l [head] |
| A.cpp:169:15:169:18 | head | semmle.label | head |
| A.cpp:173:26:173:26 | o | semmle.label | o |
| A.cpp:173:26:173:26 | o | semmle.label | o |
| A.cpp:173:26:173:26 | o [c] | semmle.label | o [c] |
| A.cpp:173:26:173:26 | o [c] | semmle.label | o [c] |
| A.cpp:181:15:181:21 | newHead | semmle.label | newHead |
| A.cpp:181:32:181:35 | next [head] | semmle.label | next [head] |
| A.cpp:181:32:181:35 | next [next, head] | semmle.label | next [next, head] |
@@ -829,6 +990,7 @@ nodes
| E.cpp:32:10:32:10 | b [buffer] | semmle.label | b [buffer] |
| E.cpp:32:13:32:18 | buffer | semmle.label | buffer |
| E.cpp:33:18:33:19 | & ... [data, buffer] | semmle.label | & ... [data, buffer] |
| aliasing.cpp:8:23:8:23 | s [m1] | semmle.label | s [m1] |
| aliasing.cpp:9:3:9:3 | s [post update] [m1] | semmle.label | s [post update] [m1] |
| aliasing.cpp:9:3:9:22 | ... = ... | semmle.label | ... = ... |
| aliasing.cpp:9:11:9:20 | call to user_input | semmle.label | call to user_input |
@@ -854,6 +1016,7 @@ nodes
| aliasing.cpp:93:8:93:8 | w [s, m1] | semmle.label | w [s, m1] |
| aliasing.cpp:93:10:93:10 | s [m1] | semmle.label | s [m1] |
| aliasing.cpp:93:12:93:13 | m1 | semmle.label | m1 |
| aliasing.cpp:105:23:105:24 | pa | semmle.label | pa |
| aliasing.cpp:106:4:106:5 | pa [inner post update] | semmle.label | pa [inner post update] |
| aliasing.cpp:106:9:106:18 | call to user_input | semmle.label | call to user_input |
| aliasing.cpp:158:15:158:15 | s [post update] [data] | semmle.label | s [post update] [data] |
@@ -902,10 +1065,16 @@ nodes
| arrays.cpp:36:12:36:14 | arr [inner post update] [data] | semmle.label | arr [inner post update] [data] |
| arrays.cpp:36:26:36:35 | call to user_input | semmle.label | call to user_input |
| arrays.cpp:37:8:37:8 | o [nested, arr, data] | semmle.label | o [nested, arr, data] |
| arrays.cpp:37:8:37:8 | o [post update] [nested, arr, data] | semmle.label | o [post update] [nested, arr, data] |
| arrays.cpp:37:8:37:22 | access to array [data] | semmle.label | access to array [data] |
| arrays.cpp:37:8:37:22 | access to array [post update] [data] | semmle.label | access to array [post update] [data] |
| arrays.cpp:37:10:37:15 | nested [arr, data] | semmle.label | nested [arr, data] |
| arrays.cpp:37:10:37:15 | nested [post update] [arr, data] | semmle.label | nested [post update] [arr, data] |
| arrays.cpp:37:17:37:19 | arr [data] | semmle.label | arr [data] |
| arrays.cpp:37:17:37:19 | arr [inner post update] [data] | semmle.label | arr [inner post update] [data] |
| arrays.cpp:37:24:37:27 | data | semmle.label | data |
| arrays.cpp:37:24:37:27 | data | semmle.label | data |
| arrays.cpp:37:24:37:27 | ref arg data | semmle.label | ref arg data |
| arrays.cpp:38:8:38:8 | o [nested, arr, data] | semmle.label | o [nested, arr, data] |
| arrays.cpp:38:8:38:22 | access to array [data] | semmle.label | access to array [data] |
| arrays.cpp:38:10:38:15 | nested [arr, data] | semmle.label | nested [arr, data] |
@@ -918,15 +1087,22 @@ nodes
| arrays.cpp:42:15:42:17 | arr [inner post update] [data] | semmle.label | arr [inner post update] [data] |
| arrays.cpp:42:29:42:38 | call to user_input | semmle.label | call to user_input |
| arrays.cpp:43:8:43:8 | o [indirect, arr, data] | semmle.label | o [indirect, arr, data] |
| arrays.cpp:43:8:43:8 | o [post update] [indirect, arr, data] | semmle.label | o [post update] [indirect, arr, data] |
| arrays.cpp:43:8:43:25 | access to array [data] | semmle.label | access to array [data] |
| arrays.cpp:43:8:43:25 | access to array [post update] [data] | semmle.label | access to array [post update] [data] |
| arrays.cpp:43:10:43:17 | indirect [arr, data] | semmle.label | indirect [arr, data] |
| arrays.cpp:43:10:43:17 | indirect [post update] [arr, data] | semmle.label | indirect [post update] [arr, data] |
| arrays.cpp:43:20:43:22 | arr [data] | semmle.label | arr [data] |
| arrays.cpp:43:20:43:22 | arr [inner post update] [data] | semmle.label | arr [inner post update] [data] |
| arrays.cpp:43:27:43:30 | data | semmle.label | data |
| arrays.cpp:43:27:43:30 | data | semmle.label | data |
| arrays.cpp:43:27:43:30 | ref arg data | semmle.label | ref arg data |
| arrays.cpp:44:8:44:8 | o [indirect, arr, data] | semmle.label | o [indirect, arr, data] |
| arrays.cpp:44:8:44:25 | access to array [data] | semmle.label | access to array [data] |
| arrays.cpp:44:10:44:17 | indirect [arr, data] | semmle.label | indirect [arr, data] |
| arrays.cpp:44:20:44:22 | arr [data] | semmle.label | arr [data] |
| arrays.cpp:44:27:44:30 | data | semmle.label | data |
| by_reference.cpp:11:39:11:39 | s [a] | semmle.label | s [a] |
| by_reference.cpp:11:48:11:52 | value | semmle.label | value |
| by_reference.cpp:12:5:12:5 | s [post update] [a] | semmle.label | s [post update] [a] |
| by_reference.cpp:12:5:12:16 | ... = ... | semmle.label | ... = ... |
@@ -967,6 +1143,7 @@ nodes
| by_reference.cpp:68:21:68:30 | call to user_input | semmle.label | call to user_input |
| by_reference.cpp:69:8:69:20 | call to nonMemberGetA | semmle.label | call to nonMemberGetA |
| by_reference.cpp:69:22:69:23 | & ... [a] | semmle.label | & ... [a] |
| by_reference.cpp:83:31:83:35 | inner [a] | semmle.label | inner [a] |
| by_reference.cpp:84:3:84:7 | inner [post update] [a] | semmle.label | inner [post update] [a] |
| by_reference.cpp:84:3:84:25 | ... = ... | semmle.label | ... = ... |
| by_reference.cpp:84:14:84:23 | call to user_input | semmle.label | call to user_input |
@@ -974,6 +1151,7 @@ nodes
| by_reference.cpp:88:3:88:7 | inner [post update] [a] | semmle.label | inner [post update] [a] |
| by_reference.cpp:88:3:88:24 | ... = ... | semmle.label | ... = ... |
| by_reference.cpp:88:13:88:22 | call to user_input | semmle.label | call to user_input |
| by_reference.cpp:91:25:91:26 | pa | semmle.label | pa |
| by_reference.cpp:92:4:92:5 | pa [inner post update] | semmle.label | pa [inner post update] |
| by_reference.cpp:92:9:92:18 | call to user_input | semmle.label | call to user_input |
| by_reference.cpp:95:25:95:26 | pa | semmle.label | pa |
@@ -1141,6 +1319,7 @@ nodes
| qualifiers.cpp:9:21:9:25 | value | semmle.label | value |
| qualifiers.cpp:9:30:9:33 | this [post update] [a] | semmle.label | this [post update] [a] |
| qualifiers.cpp:9:30:9:44 | ... = ... | semmle.label | ... = ... |
| qualifiers.cpp:12:27:12:31 | inner [a] | semmle.label | inner [a] |
| qualifiers.cpp:12:40:12:44 | value | semmle.label | value |
| qualifiers.cpp:12:49:12:53 | inner [post update] [a] | semmle.label | inner [post update] [a] |
| qualifiers.cpp:12:49:12:64 | ... = ... | semmle.label | ... = ... |
@@ -1189,6 +1368,8 @@ nodes
| qualifiers.cpp:48:10:48:14 | outer [inner, a] | semmle.label | outer [inner, a] |
| qualifiers.cpp:48:16:48:20 | inner [a] | semmle.label | inner [a] |
| qualifiers.cpp:48:23:48:23 | a | semmle.label | a |
| realistic.cpp:41:17:41:17 | o | semmle.label | o |
| realistic.cpp:41:17:41:17 | o | semmle.label | o |
| realistic.cpp:53:9:53:11 | foo [post update] [bar, baz, userInput, bufferLen] | semmle.label | foo [post update] [bar, baz, userInput, bufferLen] |
| realistic.cpp:53:9:53:18 | access to array [post update] [baz, userInput, bufferLen] | semmle.label | access to array [post update] [baz, userInput, bufferLen] |
| realistic.cpp:53:9:53:66 | ... = ... | semmle.label | ... = ... |
@@ -1197,11 +1378,18 @@ nodes
| realistic.cpp:53:25:53:33 | userInput [post update] [bufferLen] | semmle.label | userInput [post update] [bufferLen] |
| realistic.cpp:53:55:53:64 | call to user_input | semmle.label | call to user_input |
| realistic.cpp:61:21:61:23 | foo [bar, baz, userInput, bufferLen] | semmle.label | foo [bar, baz, userInput, bufferLen] |
| realistic.cpp:61:21:61:23 | foo [post update] [bar, baz, userInput, bufferLen] | semmle.label | foo [post update] [bar, baz, userInput, bufferLen] |
| realistic.cpp:61:21:61:30 | access to array [baz, userInput, bufferLen] | semmle.label | access to array [baz, userInput, bufferLen] |
| realistic.cpp:61:21:61:30 | access to array [post update] [baz, userInput, bufferLen] | semmle.label | access to array [post update] [baz, userInput, bufferLen] |
| realistic.cpp:61:25:61:27 | bar [baz, userInput, bufferLen] | semmle.label | bar [baz, userInput, bufferLen] |
| realistic.cpp:61:25:61:27 | bar [inner post update] [baz, userInput, bufferLen] | semmle.label | bar [inner post update] [baz, userInput, bufferLen] |
| realistic.cpp:61:32:61:34 | baz [post update] [userInput, bufferLen] | semmle.label | baz [post update] [userInput, bufferLen] |
| realistic.cpp:61:32:61:34 | baz [userInput, bufferLen] | semmle.label | baz [userInput, bufferLen] |
| realistic.cpp:61:37:61:45 | userInput [bufferLen] | semmle.label | userInput [bufferLen] |
| realistic.cpp:61:37:61:45 | userInput [post update] [bufferLen] | semmle.label | userInput [post update] [bufferLen] |
| realistic.cpp:61:47:61:55 | bufferLen | semmle.label | bufferLen |
| realistic.cpp:61:47:61:55 | bufferLen | semmle.label | bufferLen |
| realistic.cpp:61:47:61:55 | ref arg bufferLen | semmle.label | ref arg bufferLen |
| simple.cpp:18:9:18:9 | this [a_] | semmle.label | this [a_] |
| simple.cpp:18:22:18:23 | a_ | semmle.label | a_ |
| simple.cpp:18:22:18:23 | this [a_] | semmle.label | this [a_] |
@@ -1253,21 +1441,33 @@ nodes
| simple.cpp:94:10:94:11 | a2 [i] | semmle.label | a2 [i] |
| simple.cpp:94:13:94:13 | i | semmle.label | i |
| struct_init.c:14:24:14:25 | ab [a] | semmle.label | ab [a] |
| struct_init.c:14:24:14:25 | ab [a] | semmle.label | ab [a] |
| struct_init.c:15:8:15:9 | ab [a] | semmle.label | ab [a] |
| struct_init.c:15:8:15:9 | ab [post update] [a] | semmle.label | ab [post update] [a] |
| struct_init.c:15:12:15:12 | a | semmle.label | a |
| struct_init.c:15:12:15:12 | a | semmle.label | a |
| struct_init.c:15:12:15:12 | ref arg a | semmle.label | ref arg a |
| struct_init.c:20:17:20:36 | {...} [a] | semmle.label | {...} [a] |
| struct_init.c:20:20:20:29 | call to user_input | semmle.label | call to user_input |
| struct_init.c:22:8:22:9 | ab [a] | semmle.label | ab [a] |
| struct_init.c:22:8:22:9 | ab [post update] [a] | semmle.label | ab [post update] [a] |
| struct_init.c:22:11:22:11 | a | semmle.label | a |
| struct_init.c:22:11:22:11 | a | semmle.label | a |
| struct_init.c:22:11:22:11 | ref arg a | semmle.label | ref arg a |
| struct_init.c:24:10:24:12 | & ... [a] | semmle.label | & ... [a] |
| struct_init.c:24:10:24:12 | ref arg & ... [a] | semmle.label | ref arg & ... [a] |
| struct_init.c:26:23:29:3 | {...} [nestedAB, a] | semmle.label | {...} [nestedAB, a] |
| struct_init.c:26:23:29:3 | {...} [pointerAB, a] | semmle.label | {...} [pointerAB, a] |
| struct_init.c:27:5:27:23 | {...} [a] | semmle.label | {...} [a] |
| struct_init.c:27:7:27:16 | call to user_input | semmle.label | call to user_input |
| struct_init.c:28:5:28:7 | & ... [a] | semmle.label | & ... [a] |
| struct_init.c:31:8:31:12 | outer [nestedAB, a] | semmle.label | outer [nestedAB, a] |
| struct_init.c:31:8:31:12 | outer [post update] [nestedAB, a] | semmle.label | outer [post update] [nestedAB, a] |
| struct_init.c:31:14:31:21 | nestedAB [a] | semmle.label | nestedAB [a] |
| struct_init.c:31:14:31:21 | nestedAB [post update] [a] | semmle.label | nestedAB [post update] [a] |
| struct_init.c:31:23:31:23 | a | semmle.label | a |
| struct_init.c:31:23:31:23 | a | semmle.label | a |
| struct_init.c:31:23:31:23 | ref arg a | semmle.label | ref arg a |
| struct_init.c:33:8:33:12 | outer [pointerAB, a] | semmle.label | outer [pointerAB, a] |
| struct_init.c:33:14:33:22 | pointerAB [a] | semmle.label | pointerAB [a] |
| struct_init.c:33:25:33:25 | a | semmle.label | a |
@@ -1293,9 +1493,11 @@ subpaths
| A.cpp:90:15:90:15 | c | A.cpp:27:17:27:17 | c | A.cpp:27:22:27:25 | this [post update] [c] | A.cpp:90:7:90:8 | ref arg b2 [c] |
| A.cpp:126:12:126:18 | new | A.cpp:27:17:27:17 | c | A.cpp:27:22:27:25 | this [post update] [c] | A.cpp:126:5:126:5 | ref arg b [c] |
| A.cpp:151:18:151:18 | b | A.cpp:140:13:140:13 | b | A.cpp:143:7:143:10 | this [post update] [b] | A.cpp:151:12:151:24 | call to D [b] |
| A.cpp:152:13:152:13 | b [c] | A.cpp:173:26:173:26 | o [c] | A.cpp:173:26:173:26 | o [c] | A.cpp:152:13:152:13 | ref arg b [c] |
| A.cpp:160:29:160:29 | b | A.cpp:181:15:181:21 | newHead | A.cpp:183:7:183:10 | this [post update] [head] | A.cpp:160:18:160:60 | call to MyList [head] |
| A.cpp:161:38:161:39 | l1 [head] | A.cpp:181:32:181:35 | next [head] | A.cpp:184:7:184:10 | this [post update] [next, head] | A.cpp:161:18:161:40 | call to MyList [next, head] |
| A.cpp:162:38:162:39 | l2 [next, head] | A.cpp:181:32:181:35 | next [next, head] | A.cpp:184:7:184:10 | this [post update] [next, next, head] | A.cpp:162:18:162:40 | call to MyList [next, next, head] |
| A.cpp:165:26:165:29 | head | A.cpp:173:26:173:26 | o | A.cpp:173:26:173:26 | o | A.cpp:165:26:165:29 | ref arg head |
| B.cpp:7:25:7:25 | e | B.cpp:33:16:33:17 | e1 | B.cpp:35:7:35:10 | this [post update] [elem1] | B.cpp:7:16:7:35 | call to Box1 [elem1] |
| B.cpp:8:25:8:26 | b1 [elem1] | B.cpp:44:16:44:17 | b1 [elem1] | B.cpp:46:7:46:10 | this [post update] [box1, elem1] | B.cpp:8:16:8:27 | call to Box2 [box1, elem1] |
| B.cpp:16:37:16:37 | e | B.cpp:33:26:33:27 | e2 | B.cpp:36:7:36:10 | this [post update] [elem2] | B.cpp:16:16:16:38 | call to Box1 [elem2] |
@@ -1304,7 +1506,10 @@ subpaths
| D.cpp:22:14:22:20 | call to getBox1 [elem] | D.cpp:10:11:10:17 | this [elem] | D.cpp:10:30:10:33 | elem | D.cpp:22:25:22:31 | call to getElem |
| D.cpp:37:21:37:21 | e | D.cpp:11:24:11:24 | e | D.cpp:11:29:11:32 | this [post update] [elem] | D.cpp:37:8:37:10 | ref arg box [elem] |
| D.cpp:51:27:51:27 | e | D.cpp:11:24:11:24 | e | D.cpp:11:29:11:32 | this [post update] [elem] | D.cpp:51:8:51:14 | ref arg call to getBox1 [elem] |
| arrays.cpp:37:24:37:27 | data | realistic.cpp:41:17:41:17 | o | realistic.cpp:41:17:41:17 | o | arrays.cpp:37:24:37:27 | ref arg data |
| arrays.cpp:43:27:43:30 | data | realistic.cpp:41:17:41:17 | o | realistic.cpp:41:17:41:17 | o | arrays.cpp:43:27:43:30 | ref arg data |
| by_reference.cpp:20:23:20:27 | value | by_reference.cpp:15:26:15:30 | value | by_reference.cpp:16:5:16:8 | this [post update] [a] | by_reference.cpp:20:5:20:8 | ref arg this [a] |
| by_reference.cpp:24:25:24:29 | value | by_reference.cpp:11:48:11:52 | value | by_reference.cpp:11:39:11:39 | s [a] | by_reference.cpp:24:19:24:22 | ref arg this [a] |
| by_reference.cpp:24:25:24:29 | value | by_reference.cpp:11:48:11:52 | value | by_reference.cpp:12:5:12:5 | s [post update] [a] | by_reference.cpp:24:19:24:22 | ref arg this [a] |
| by_reference.cpp:40:12:40:15 | this [a] | by_reference.cpp:35:9:35:19 | this [a] | by_reference.cpp:36:18:36:18 | a | by_reference.cpp:40:18:40:28 | call to getDirectly |
| by_reference.cpp:44:26:44:29 | this [a] | by_reference.cpp:31:46:31:46 | s [a] | by_reference.cpp:32:15:32:15 | a | by_reference.cpp:44:12:44:24 | call to nonMemberGetA |
@@ -1314,6 +1519,7 @@ subpaths
| by_reference.cpp:57:8:57:8 | s [a] | by_reference.cpp:39:9:39:21 | this [a] | by_reference.cpp:40:18:40:28 | call to getDirectly | by_reference.cpp:57:10:57:22 | call to getIndirectly |
| by_reference.cpp:62:25:62:34 | call to user_input | by_reference.cpp:23:34:23:38 | value | by_reference.cpp:24:19:24:22 | ref arg this [a] | by_reference.cpp:62:3:62:3 | ref arg s [a] |
| by_reference.cpp:63:8:63:8 | s [a] | by_reference.cpp:43:9:43:27 | this [a] | by_reference.cpp:44:12:44:24 | call to nonMemberGetA | by_reference.cpp:63:10:63:28 | call to getThroughNonMember |
| by_reference.cpp:68:21:68:30 | call to user_input | by_reference.cpp:11:48:11:52 | value | by_reference.cpp:11:39:11:39 | s [a] | by_reference.cpp:68:17:68:18 | ref arg & ... [a] |
| by_reference.cpp:68:21:68:30 | call to user_input | by_reference.cpp:11:48:11:52 | value | by_reference.cpp:12:5:12:5 | s [post update] [a] | by_reference.cpp:68:17:68:18 | ref arg & ... [a] |
| by_reference.cpp:69:22:69:23 | & ... [a] | by_reference.cpp:31:46:31:46 | s [a] | by_reference.cpp:32:15:32:15 | a | by_reference.cpp:69:8:69:20 | call to nonMemberGetA |
| complex.cpp:42:16:42:16 | f [a_] | complex.cpp:9:7:9:7 | this [a_] | complex.cpp:9:20:9:21 | a_ | complex.cpp:42:18:42:18 | call to a |
@@ -1329,9 +1535,11 @@ subpaths
| constructors.cpp:36:11:36:20 | call to user_input | constructors.cpp:23:13:23:13 | a | constructors.cpp:23:25:23:29 | constructor init of field a_ [post-this] [a_] | constructors.cpp:36:11:36:37 | call to Foo [a_] |
| constructors.cpp:36:25:36:34 | call to user_input | constructors.cpp:23:20:23:20 | b | constructors.cpp:23:32:23:36 | constructor init of field b_ [post-this] [b_] | constructors.cpp:36:11:36:37 | call to Foo [b_] |
| qualifiers.cpp:27:28:27:37 | call to user_input | qualifiers.cpp:9:21:9:25 | value | qualifiers.cpp:9:30:9:33 | this [post update] [a] | qualifiers.cpp:27:11:27:18 | ref arg call to getInner [a] |
| qualifiers.cpp:32:35:32:44 | call to user_input | qualifiers.cpp:12:40:12:44 | value | qualifiers.cpp:12:27:12:31 | inner [a] | qualifiers.cpp:32:23:32:30 | ref arg call to getInner [a] |
| qualifiers.cpp:32:35:32:44 | call to user_input | qualifiers.cpp:12:40:12:44 | value | qualifiers.cpp:12:49:12:53 | inner [post update] [a] | qualifiers.cpp:32:23:32:30 | ref arg call to getInner [a] |
| qualifiers.cpp:37:38:37:47 | call to user_input | qualifiers.cpp:13:42:13:46 | value | qualifiers.cpp:13:29:13:33 | inner [a] | qualifiers.cpp:37:19:37:35 | ref arg * ... [a] |
| qualifiers.cpp:37:38:37:47 | call to user_input | qualifiers.cpp:13:42:13:46 | value | qualifiers.cpp:13:51:13:55 | inner [post update] [a] | qualifiers.cpp:37:19:37:35 | ref arg * ... [a] |
| realistic.cpp:61:47:61:55 | bufferLen | realistic.cpp:41:17:41:17 | o | realistic.cpp:41:17:41:17 | o | realistic.cpp:61:47:61:55 | ref arg bufferLen |
| simple.cpp:28:10:28:10 | f [a_] | simple.cpp:18:9:18:9 | this [a_] | simple.cpp:18:22:18:23 | a_ | simple.cpp:28:12:28:12 | call to a |
| simple.cpp:29:10:29:10 | f [b_] | simple.cpp:19:9:19:9 | this [b_] | simple.cpp:19:22:19:23 | b_ | simple.cpp:29:12:29:12 | call to b |
| simple.cpp:39:12:39:21 | call to user_input | simple.cpp:20:19:20:19 | a | simple.cpp:20:24:20:25 | this [post update] [a_] | simple.cpp:39:5:39:5 | ref arg f [a_] |
@@ -1339,6 +1547,10 @@ subpaths
| simple.cpp:41:12:41:21 | call to user_input | simple.cpp:20:19:20:19 | a | simple.cpp:20:24:20:25 | this [post update] [a_] | simple.cpp:41:5:41:5 | ref arg h [a_] |
| simple.cpp:42:12:42:21 | call to user_input | simple.cpp:21:19:21:19 | b | simple.cpp:21:24:21:25 | this [post update] [b_] | simple.cpp:42:5:42:5 | ref arg h [b_] |
| simple.cpp:84:14:84:20 | this [f2, f1] | simple.cpp:78:9:78:15 | this [f2, f1] | simple.cpp:79:19:79:20 | f1 | simple.cpp:84:14:84:20 | call to getf2f1 |
| struct_init.c:15:12:15:12 | a | realistic.cpp:41:17:41:17 | o | realistic.cpp:41:17:41:17 | o | struct_init.c:15:12:15:12 | ref arg a |
| struct_init.c:22:11:22:11 | a | realistic.cpp:41:17:41:17 | o | realistic.cpp:41:17:41:17 | o | struct_init.c:22:11:22:11 | ref arg a |
| struct_init.c:24:10:24:12 | & ... [a] | struct_init.c:14:24:14:25 | ab [a] | struct_init.c:14:24:14:25 | ab [a] | struct_init.c:24:10:24:12 | ref arg & ... [a] |
| struct_init.c:31:23:31:23 | a | realistic.cpp:41:17:41:17 | o | realistic.cpp:41:17:41:17 | o | struct_init.c:31:23:31:23 | ref arg a |
#select
| A.cpp:43:10:43:12 | & ... | A.cpp:41:15:41:21 | new | A.cpp:43:10:43:12 | & ... | & ... flows from $@ | A.cpp:41:15:41:21 | new | new |
| A.cpp:49:13:49:13 | c | A.cpp:47:12:47:18 | new | A.cpp:49:13:49:13 | c | c flows from $@ | A.cpp:47:12:47:18 | new | new |

View File

@@ -299,12 +299,15 @@
| file://:0:0:0:0 | (unnamed parameter 0) | file://:0:0:0:0 | (unnamed parameter 0) | |
| file://:0:0:0:0 | (unnamed parameter 0) | file://:0:0:0:0 | (unnamed parameter 0) | |
| file://:0:0:0:0 | (unnamed parameter 0) | file://:0:0:0:0 | (unnamed parameter 0) | |
| format.cpp:16:21:16:21 | s | format.cpp:16:21:16:21 | s | |
| format.cpp:16:21:16:21 | s | format.cpp:22:22:22:22 | s | |
| format.cpp:16:31:16:31 | n | format.cpp:22:25:22:25 | n | |
| format.cpp:16:46:16:51 | format | format.cpp:16:46:16:51 | format | |
| format.cpp:16:46:16:51 | format | format.cpp:22:28:22:33 | format | |
| format.cpp:20:10:20:13 | args | format.cpp:22:36:22:39 | args | |
| format.cpp:22:12:22:20 | call to vsnprintf | format.cpp:22:3:22:40 | ... = ... | |
| format.cpp:22:12:22:20 | call to vsnprintf | format.cpp:25:9:25:14 | result | |
| format.cpp:22:22:22:22 | ref arg s | format.cpp:16:21:16:21 | s | |
| format.cpp:50:21:50:24 | {...} | format.cpp:51:17:51:22 | buffer | |
| format.cpp:50:21:50:24 | {...} | format.cpp:52:8:52:13 | buffer | |
| format.cpp:50:23:50:23 | 0 | format.cpp:50:21:50:24 | {...} | TAINT |
@@ -3344,7 +3347,9 @@
| smart_pointer.cpp:93:8:93:8 | ref arg q | smart_pointer.cpp:94:8:94:8 | q | |
| smart_pointer.cpp:94:8:94:8 | q | smart_pointer.cpp:94:9:94:9 | call to operator-> | |
| smart_pointer.cpp:94:8:94:8 | ref arg q | smart_pointer.cpp:86:67:86:67 | q | |
| smart_pointer.cpp:97:17:97:18 | pa | smart_pointer.cpp:97:17:97:18 | pa | |
| smart_pointer.cpp:97:17:97:18 | pa | smart_pointer.cpp:98:5:98:6 | pa | |
| smart_pointer.cpp:98:5:98:6 | pa [post update] | smart_pointer.cpp:97:17:97:18 | pa | |
| smart_pointer.cpp:98:5:98:20 | ... = ... | smart_pointer.cpp:98:9:98:9 | x [post update] | |
| smart_pointer.cpp:98:13:98:18 | call to source | smart_pointer.cpp:98:5:98:20 | ... = ... | |
| smart_pointer.cpp:102:25:102:50 | call to unique_ptr | smart_pointer.cpp:103:11:103:11 | p | |
@@ -5923,11 +5928,13 @@
| taint.cpp:172:10:172:15 | buffer | taint.cpp:172:10:172:15 | ref arg buffer | TAINT |
| taint.cpp:172:10:172:15 | ref arg buffer | taint.cpp:173:8:173:13 | buffer | |
| taint.cpp:172:18:172:24 | tainted | taint.cpp:172:10:172:15 | ref arg buffer | TAINT |
| taint.cpp:180:19:180:19 | p | taint.cpp:180:19:180:19 | p | |
| taint.cpp:180:19:180:19 | p | taint.cpp:181:9:181:9 | p | |
| taint.cpp:181:9:181:9 | p | taint.cpp:181:8:181:9 | * ... | TAINT |
| taint.cpp:185:11:185:16 | call to source | taint.cpp:186:11:186:11 | x | |
| taint.cpp:186:10:186:11 | ref arg & ... | taint.cpp:186:11:186:11 | x [inner post update] | |
| taint.cpp:186:11:186:11 | x | taint.cpp:186:10:186:11 | & ... | |
| taint.cpp:192:23:192:28 | source | taint.cpp:192:23:192:28 | source | |
| taint.cpp:192:23:192:28 | source | taint.cpp:194:13:194:18 | source | |
| taint.cpp:193:6:193:6 | x | taint.cpp:194:10:194:10 | x | |
| taint.cpp:193:6:193:6 | x | taint.cpp:195:7:195:7 | x | |
@@ -5935,6 +5942,7 @@
| taint.cpp:194:9:194:10 | ref arg & ... | taint.cpp:194:10:194:10 | x [inner post update] | |
| taint.cpp:194:9:194:10 | ref arg & ... | taint.cpp:195:7:195:7 | x | |
| taint.cpp:194:10:194:10 | x | taint.cpp:194:9:194:10 | & ... | |
| taint.cpp:194:13:194:18 | ref arg source | taint.cpp:192:23:192:28 | source | |
| taint.cpp:194:13:194:18 | source | taint.cpp:194:2:194:7 | call to memcpy | TAINT |
| taint.cpp:194:13:194:18 | source | taint.cpp:194:9:194:10 | ref arg & ... | TAINT |
| taint.cpp:207:6:207:11 | call to source | taint.cpp:207:2:207:13 | ... = ... | |
@@ -6040,18 +6048,22 @@
| taint.cpp:304:2:304:6 | ... = ... | taint.cpp:304:2:304:2 | a [post update] | |
| taint.cpp:304:6:304:6 | b | taint.cpp:302:21:302:21 | a | |
| taint.cpp:304:6:304:6 | b | taint.cpp:304:2:304:6 | ... = ... | |
| taint.cpp:307:21:307:21 | a | taint.cpp:307:21:307:21 | a | |
| taint.cpp:307:21:307:21 | a | taint.cpp:309:3:309:3 | a | |
| taint.cpp:307:28:307:28 | b | taint.cpp:309:7:309:7 | b | |
| taint.cpp:309:2:309:3 | * ... [post update] | taint.cpp:307:21:307:21 | a | |
| taint.cpp:309:2:309:3 | * ... [post update] | taint.cpp:309:3:309:3 | a [inner post update] | |
| taint.cpp:309:2:309:7 | ... = ... | taint.cpp:309:2:309:3 | * ... [post update] | |
| taint.cpp:309:3:309:3 | a | taint.cpp:309:2:309:3 | * ... | TAINT |
| taint.cpp:309:7:309:7 | b | taint.cpp:309:2:309:7 | ... = ... | |
| taint.cpp:312:21:312:21 | a | taint.cpp:312:21:312:21 | a | |
| taint.cpp:312:21:312:21 | a | taint.cpp:317:3:317:3 | a | |
| taint.cpp:312:28:312:28 | b | taint.cpp:316:6:316:6 | b | |
| taint.cpp:316:6:316:6 | b | taint.cpp:316:6:316:10 | ... + ... | TAINT |
| taint.cpp:316:6:316:10 | ... + ... | taint.cpp:316:2:316:10 | ... = ... | |
| taint.cpp:316:6:316:10 | ... + ... | taint.cpp:317:7:317:7 | c | |
| taint.cpp:316:10:316:10 | 1 | taint.cpp:316:6:316:10 | ... + ... | TAINT |
| taint.cpp:317:2:317:3 | * ... [post update] | taint.cpp:312:21:312:21 | a | |
| taint.cpp:317:2:317:3 | * ... [post update] | taint.cpp:317:3:317:3 | a [inner post update] | |
| taint.cpp:317:2:317:7 | ... = ... | taint.cpp:317:2:317:3 | * ... [post update] | |
| taint.cpp:317:3:317:3 | a | taint.cpp:317:2:317:3 | * ... | TAINT |
@@ -6113,6 +6125,7 @@
| taint.cpp:347:13:347:13 | d | taint.cpp:347:12:347:13 | & ... | |
| taint.cpp:348:14:348:14 | ref arg e | taint.cpp:355:7:355:7 | e | |
| taint.cpp:348:17:348:17 | ref arg t | taint.cpp:350:7:350:7 | t | |
| taint.cpp:367:24:367:29 | source | taint.cpp:367:24:367:29 | source | |
| taint.cpp:367:24:367:29 | source | taint.cpp:371:13:371:18 | source | |
| taint.cpp:367:24:367:29 | source | taint.cpp:373:14:373:19 | source | |
| taint.cpp:371:6:371:11 | call to strdup | taint.cpp:371:2:371:19 | ... = ... | |
@@ -6130,6 +6143,7 @@
| taint.cpp:383:6:383:12 | call to strndup | taint.cpp:384:7:384:7 | a | |
| taint.cpp:383:14:383:27 | hello, world | taint.cpp:383:6:383:12 | call to strndup | TAINT |
| taint.cpp:383:30:383:35 | source | taint.cpp:383:6:383:12 | call to strndup | TAINT |
| taint.cpp:387:27:387:32 | source | taint.cpp:387:27:387:32 | source | |
| taint.cpp:387:27:387:32 | source | taint.cpp:391:13:391:18 | source | |
| taint.cpp:391:6:391:11 | call to wcsdup | taint.cpp:391:2:391:19 | ... = ... | |
| taint.cpp:391:6:391:11 | call to wcsdup | taint.cpp:393:7:393:7 | a | |
@@ -6137,6 +6151,7 @@
| taint.cpp:392:6:392:11 | call to wcsdup | taint.cpp:392:2:392:28 | ... = ... | |
| taint.cpp:392:6:392:11 | call to wcsdup | taint.cpp:394:7:394:7 | b | |
| taint.cpp:392:13:392:27 | hello, world | taint.cpp:392:6:392:11 | call to wcsdup | TAINT |
| taint.cpp:397:25:397:30 | source | taint.cpp:397:25:397:30 | source | |
| taint.cpp:397:25:397:30 | source | taint.cpp:401:14:401:19 | source | |
| taint.cpp:397:25:397:30 | source | taint.cpp:403:15:403:20 | source | |
| taint.cpp:401:6:401:12 | call to strdupa | taint.cpp:401:2:401:20 | ... = ... | |
@@ -6239,6 +6254,7 @@
| taint.cpp:485:6:485:6 | 0 | taint.cpp:493:7:493:7 | y | |
| taint.cpp:490:7:490:7 | ref arg x | taint.cpp:492:7:492:7 | x | |
| taint.cpp:490:10:490:10 | ref arg y | taint.cpp:493:7:493:7 | y | |
| taint.cpp:502:26:502:32 | source1 | taint.cpp:502:26:502:32 | source1 | |
| taint.cpp:502:26:502:32 | source1 | taint.cpp:505:28:505:34 | source1 | |
| taint.cpp:503:15:503:21 | 0 | taint.cpp:505:12:505:15 | line | |
| taint.cpp:503:15:503:21 | 0 | taint.cpp:507:7:507:10 | line | |
@@ -6248,29 +6264,39 @@
| taint.cpp:505:12:505:15 | line | taint.cpp:505:11:505:15 | & ... | |
| taint.cpp:505:18:505:19 | ref arg & ... | taint.cpp:505:19:505:19 | n [inner post update] | |
| taint.cpp:505:19:505:19 | n | taint.cpp:505:18:505:19 | & ... | |
| taint.cpp:505:28:505:34 | ref arg source1 | taint.cpp:502:26:502:32 | source1 | |
| taint.cpp:505:28:505:34 | source1 | taint.cpp:505:11:505:15 | ref arg & ... | TAINT |
| taint.cpp:514:24:514:29 | source | taint.cpp:514:24:514:29 | source | |
| taint.cpp:514:24:514:29 | source | taint.cpp:516:27:516:32 | source | |
| taint.cpp:515:22:515:29 | ,.-;:_ | taint.cpp:516:35:516:39 | delim | |
| taint.cpp:515:22:515:29 | ,.-;:_ | taint.cpp:518:7:518:11 | delim | |
| taint.cpp:516:20:516:25 | call to strtok | taint.cpp:517:7:517:15 | tokenized | |
| taint.cpp:516:27:516:32 | ref arg source | taint.cpp:514:24:514:29 | source | |
| taint.cpp:516:27:516:32 | source | taint.cpp:516:20:516:25 | call to strtok | TAINT |
| taint.cpp:525:26:525:28 | ptr | taint.cpp:525:26:525:28 | ptr | |
| taint.cpp:525:26:525:28 | ptr | taint.cpp:526:10:526:12 | ptr | |
| taint.cpp:525:26:525:28 | ptr | taint.cpp:527:7:527:9 | ptr | |
| taint.cpp:525:26:525:28 | ptr | taint.cpp:528:8:528:10 | ptr | |
| taint.cpp:525:36:525:41 | source | taint.cpp:526:15:526:20 | source | |
| taint.cpp:526:10:526:12 | ptr | taint.cpp:526:2:526:8 | call to _strset | |
| taint.cpp:526:10:526:12 | ref arg ptr | taint.cpp:525:26:525:28 | ptr | |
| taint.cpp:526:10:526:12 | ref arg ptr | taint.cpp:527:7:527:9 | ptr | |
| taint.cpp:526:10:526:12 | ref arg ptr | taint.cpp:528:8:528:10 | ptr | |
| taint.cpp:526:15:526:20 | source | taint.cpp:526:2:526:8 | call to _strset | TAINT |
| taint.cpp:526:15:526:20 | source | taint.cpp:526:10:526:12 | ref arg ptr | |
| taint.cpp:527:7:527:9 | ref arg ptr | taint.cpp:525:26:525:28 | ptr | |
| taint.cpp:527:7:527:9 | ref arg ptr | taint.cpp:528:8:528:10 | ptr | |
| taint.cpp:528:8:528:10 | ptr | taint.cpp:528:7:528:10 | * ... | TAINT |
| taint.cpp:531:26:531:31 | source | taint.cpp:531:26:531:31 | source | |
| taint.cpp:531:26:531:31 | source | taint.cpp:532:10:532:15 | source | |
| taint.cpp:531:26:531:31 | source | taint.cpp:533:7:533:12 | source | |
| taint.cpp:532:10:532:15 | ref arg source | taint.cpp:531:26:531:31 | source | |
| taint.cpp:532:10:532:15 | ref arg source | taint.cpp:533:7:533:12 | source | |
| taint.cpp:532:10:532:15 | source | taint.cpp:532:2:532:8 | call to _strset | |
| taint.cpp:532:18:532:18 | 0 | taint.cpp:532:2:532:8 | call to _strset | TAINT |
| taint.cpp:532:18:532:18 | 0 | taint.cpp:532:10:532:15 | ref arg source | |
| taint.cpp:533:7:533:12 | ref arg source | taint.cpp:531:26:531:31 | source | |
| taint.cpp:540:24:540:29 | source | taint.cpp:540:24:540:29 | source | |
| taint.cpp:540:24:540:29 | source | taint.cpp:542:14:542:19 | source | |
| taint.cpp:541:6:541:6 | x | taint.cpp:542:11:542:11 | x | |
| taint.cpp:541:6:541:6 | x | taint.cpp:543:7:543:7 | x | |
@@ -6280,6 +6306,7 @@
| taint.cpp:542:11:542:11 | x | taint.cpp:542:10:542:11 | & ... | |
| taint.cpp:542:14:542:19 | source | taint.cpp:542:2:542:8 | call to mempcpy | TAINT |
| taint.cpp:542:14:542:19 | source | taint.cpp:542:10:542:11 | ref arg & ... | TAINT |
| taint.cpp:550:24:550:29 | source | taint.cpp:550:24:550:29 | source | |
| taint.cpp:550:24:550:29 | source | taint.cpp:552:16:552:21 | source | |
| taint.cpp:551:6:551:9 | dest | taint.cpp:552:10:552:13 | dest | |
| taint.cpp:551:6:551:9 | dest | taint.cpp:552:35:552:38 | dest | |
@@ -6288,29 +6315,42 @@
| taint.cpp:552:10:552:13 | ref arg dest | taint.cpp:553:7:553:10 | dest | |
| taint.cpp:552:16:552:21 | source | taint.cpp:552:2:552:8 | call to memccpy | TAINT |
| taint.cpp:552:16:552:21 | source | taint.cpp:552:10:552:13 | ref arg dest | TAINT |
| taint.cpp:560:24:560:28 | dest1 | taint.cpp:560:24:560:28 | dest1 | |
| taint.cpp:560:24:560:28 | dest1 | taint.cpp:561:9:561:13 | dest1 | |
| taint.cpp:560:24:560:28 | dest1 | taint.cpp:562:7:562:11 | dest1 | |
| taint.cpp:560:37:560:41 | dest2 | taint.cpp:560:37:560:41 | dest2 | |
| taint.cpp:560:37:560:41 | dest2 | taint.cpp:564:9:564:13 | dest2 | |
| taint.cpp:560:37:560:41 | dest2 | taint.cpp:565:7:565:11 | dest2 | |
| taint.cpp:560:50:560:54 | clean | taint.cpp:560:50:560:54 | clean | |
| taint.cpp:560:50:560:54 | clean | taint.cpp:564:16:564:20 | clean | |
| taint.cpp:560:63:560:68 | source | taint.cpp:560:63:560:68 | source | |
| taint.cpp:560:63:560:68 | source | taint.cpp:561:16:561:21 | source | |
| taint.cpp:561:9:561:13 | dest1 | taint.cpp:561:2:561:7 | call to strcat | |
| taint.cpp:561:9:561:13 | dest1 | taint.cpp:561:9:561:13 | ref arg dest1 | TAINT |
| taint.cpp:561:9:561:13 | ref arg dest1 | taint.cpp:560:24:560:28 | dest1 | |
| taint.cpp:561:9:561:13 | ref arg dest1 | taint.cpp:562:7:562:11 | dest1 | |
| taint.cpp:561:16:561:21 | source | taint.cpp:561:9:561:13 | ref arg dest1 | TAINT |
| taint.cpp:562:7:562:11 | ref arg dest1 | taint.cpp:560:24:560:28 | dest1 | |
| taint.cpp:564:9:564:13 | dest2 | taint.cpp:564:2:564:7 | call to strcat | |
| taint.cpp:564:9:564:13 | dest2 | taint.cpp:564:9:564:13 | ref arg dest2 | TAINT |
| taint.cpp:564:9:564:13 | ref arg dest2 | taint.cpp:560:37:560:41 | dest2 | |
| taint.cpp:564:9:564:13 | ref arg dest2 | taint.cpp:565:7:565:11 | dest2 | |
| taint.cpp:564:16:564:20 | clean | taint.cpp:564:9:564:13 | ref arg dest2 | TAINT |
| taint.cpp:565:7:565:11 | ref arg dest2 | taint.cpp:560:37:560:41 | dest2 | |
| taint.cpp:572:37:572:41 | dest1 | taint.cpp:572:37:572:41 | dest1 | |
| taint.cpp:572:37:572:41 | dest1 | taint.cpp:574:36:574:40 | dest1 | |
| taint.cpp:572:37:572:41 | dest1 | taint.cpp:575:7:575:11 | dest1 | |
| taint.cpp:572:37:572:41 | dest1 | taint.cpp:576:8:576:12 | dest1 | |
| taint.cpp:572:65:572:67 | ptr | taint.cpp:572:65:572:67 | ptr | |
| taint.cpp:572:65:572:67 | ptr | taint.cpp:574:43:574:45 | ptr | |
| taint.cpp:572:65:572:67 | ptr | taint.cpp:580:43:580:45 | ptr | |
| taint.cpp:572:85:572:89 | dest3 | taint.cpp:572:85:572:89 | dest3 | |
| taint.cpp:572:85:572:89 | dest3 | taint.cpp:580:36:580:40 | dest3 | |
| taint.cpp:572:85:572:89 | dest3 | taint.cpp:581:7:581:11 | dest3 | |
| taint.cpp:572:85:572:89 | dest3 | taint.cpp:582:8:582:12 | dest3 | |
| taint.cpp:573:32:573:36 | clean | taint.cpp:573:32:573:36 | clean | |
| taint.cpp:573:32:573:36 | clean | taint.cpp:580:51:580:55 | clean | |
| taint.cpp:573:49:573:54 | source | taint.cpp:573:49:573:54 | source | |
| taint.cpp:573:49:573:54 | source | taint.cpp:574:51:574:56 | source | |
| taint.cpp:573:61:573:61 | n | taint.cpp:574:48:574:48 | n | |
| taint.cpp:573:61:573:61 | n | taint.cpp:580:48:580:48 | n | |
@@ -6318,11 +6358,14 @@
| taint.cpp:574:25:574:34 | call to _mbsncat_l | taint.cpp:578:8:578:12 | dest2 | |
| taint.cpp:574:36:574:40 | dest1 | taint.cpp:574:25:574:34 | call to _mbsncat_l | |
| taint.cpp:574:36:574:40 | dest1 | taint.cpp:574:36:574:40 | ref arg dest1 | TAINT |
| taint.cpp:574:36:574:40 | ref arg dest1 | taint.cpp:572:37:572:41 | dest1 | |
| taint.cpp:574:36:574:40 | ref arg dest1 | taint.cpp:575:7:575:11 | dest1 | |
| taint.cpp:574:36:574:40 | ref arg dest1 | taint.cpp:576:8:576:12 | dest1 | |
| taint.cpp:574:43:574:45 | ptr | taint.cpp:574:36:574:40 | ref arg dest1 | TAINT |
| taint.cpp:574:48:574:48 | n | taint.cpp:574:36:574:40 | ref arg dest1 | TAINT |
| taint.cpp:574:51:574:56 | ref arg source | taint.cpp:573:49:573:54 | source | |
| taint.cpp:574:51:574:56 | source | taint.cpp:574:36:574:40 | ref arg dest1 | TAINT |
| taint.cpp:575:7:575:11 | ref arg dest1 | taint.cpp:572:37:572:41 | dest1 | |
| taint.cpp:575:7:575:11 | ref arg dest1 | taint.cpp:576:8:576:12 | dest1 | |
| taint.cpp:576:8:576:12 | dest1 | taint.cpp:576:7:576:12 | * ... | TAINT |
| taint.cpp:577:7:577:11 | ref arg dest2 | taint.cpp:578:8:578:12 | dest2 | |
@@ -6331,15 +6374,19 @@
| taint.cpp:580:25:580:34 | call to _mbsncat_l | taint.cpp:584:8:584:12 | dest4 | |
| taint.cpp:580:36:580:40 | dest3 | taint.cpp:580:25:580:34 | call to _mbsncat_l | |
| taint.cpp:580:36:580:40 | dest3 | taint.cpp:580:36:580:40 | ref arg dest3 | TAINT |
| taint.cpp:580:36:580:40 | ref arg dest3 | taint.cpp:572:85:572:89 | dest3 | |
| taint.cpp:580:36:580:40 | ref arg dest3 | taint.cpp:581:7:581:11 | dest3 | |
| taint.cpp:580:36:580:40 | ref arg dest3 | taint.cpp:582:8:582:12 | dest3 | |
| taint.cpp:580:43:580:45 | ptr | taint.cpp:580:36:580:40 | ref arg dest3 | TAINT |
| taint.cpp:580:48:580:48 | n | taint.cpp:580:36:580:40 | ref arg dest3 | TAINT |
| taint.cpp:580:51:580:55 | clean | taint.cpp:580:36:580:40 | ref arg dest3 | TAINT |
| taint.cpp:580:51:580:55 | ref arg clean | taint.cpp:573:32:573:36 | clean | |
| taint.cpp:581:7:581:11 | ref arg dest3 | taint.cpp:572:85:572:89 | dest3 | |
| taint.cpp:581:7:581:11 | ref arg dest3 | taint.cpp:582:8:582:12 | dest3 | |
| taint.cpp:582:8:582:12 | dest3 | taint.cpp:582:7:582:12 | * ... | TAINT |
| taint.cpp:583:7:583:11 | ref arg dest4 | taint.cpp:584:8:584:12 | dest4 | |
| taint.cpp:584:8:584:12 | dest4 | taint.cpp:584:7:584:12 | * ... | TAINT |
| taint.cpp:591:24:591:29 | source | taint.cpp:591:24:591:29 | source | |
| taint.cpp:591:24:591:29 | source | taint.cpp:594:29:594:34 | source | |
| taint.cpp:592:23:592:30 | ,.-;:_ | taint.cpp:594:37:594:41 | delim | |
| taint.cpp:594:9:594:17 | tokenized | taint.cpp:594:9:594:42 | ... = ... | |
@@ -6347,6 +6394,7 @@
| taint.cpp:594:21:594:26 | call to strsep | taint.cpp:595:10:595:18 | tokenized | |
| taint.cpp:594:21:594:26 | call to strsep | taint.cpp:596:11:596:19 | tokenized | |
| taint.cpp:594:28:594:34 | & ... | taint.cpp:594:21:594:26 | call to strsep | TAINT |
| taint.cpp:594:28:594:34 | ref arg & ... | taint.cpp:591:24:591:29 | source | |
| taint.cpp:594:28:594:34 | ref arg & ... | taint.cpp:594:29:594:34 | source | |
| taint.cpp:594:28:594:34 | ref arg & ... | taint.cpp:594:29:594:34 | source [inner post update] | |
| taint.cpp:594:29:594:34 | source | taint.cpp:594:21:594:26 | call to strsep | TAINT |
@@ -6354,45 +6402,63 @@
| taint.cpp:594:37:594:41 | delim | taint.cpp:594:21:594:26 | call to strsep | TAINT |
| taint.cpp:595:10:595:18 | ref arg tokenized | taint.cpp:596:11:596:19 | tokenized | |
| taint.cpp:596:11:596:19 | tokenized | taint.cpp:596:10:596:19 | * ... | TAINT |
| taint.cpp:606:25:606:30 | source | taint.cpp:606:25:606:30 | source | |
| taint.cpp:606:25:606:30 | source | taint.cpp:607:18:607:23 | source | |
| taint.cpp:606:39:606:43 | clean | taint.cpp:606:39:606:43 | clean | |
| taint.cpp:606:39:606:43 | clean | taint.cpp:611:18:611:22 | clean | |
| taint.cpp:606:82:606:87 | locale | taint.cpp:606:82:606:87 | locale | |
| taint.cpp:606:82:606:87 | locale | taint.cpp:607:26:607:31 | locale | |
| taint.cpp:606:82:606:87 | locale | taint.cpp:611:25:611:30 | locale | |
| taint.cpp:607:10:607:16 | call to _strinc | taint.cpp:606:52:606:56 | dest1 | |
| taint.cpp:607:10:607:16 | call to _strinc | taint.cpp:607:2:607:32 | ... = ... | |
| taint.cpp:607:10:607:16 | call to _strinc | taint.cpp:608:7:608:11 | dest1 | |
| taint.cpp:607:10:607:16 | call to _strinc | taint.cpp:609:8:609:12 | dest1 | |
| taint.cpp:607:18:607:23 | source | taint.cpp:607:10:607:16 | call to _strinc | TAINT |
| taint.cpp:607:26:607:31 | locale | taint.cpp:607:10:607:16 | call to _strinc | TAINT |
| taint.cpp:607:26:607:31 | ref arg locale | taint.cpp:606:82:606:87 | locale | |
| taint.cpp:607:26:607:31 | ref arg locale | taint.cpp:611:25:611:30 | locale | |
| taint.cpp:608:7:608:11 | ref arg dest1 | taint.cpp:606:52:606:56 | dest1 | |
| taint.cpp:608:7:608:11 | ref arg dest1 | taint.cpp:609:8:609:12 | dest1 | |
| taint.cpp:609:8:609:12 | dest1 | taint.cpp:609:7:609:12 | * ... | TAINT |
| taint.cpp:611:10:611:16 | call to _strinc | taint.cpp:606:65:606:69 | dest2 | |
| taint.cpp:611:10:611:16 | call to _strinc | taint.cpp:611:2:611:31 | ... = ... | |
| taint.cpp:611:10:611:16 | call to _strinc | taint.cpp:612:7:612:11 | dest2 | |
| taint.cpp:611:10:611:16 | call to _strinc | taint.cpp:613:8:613:12 | dest2 | |
| taint.cpp:611:18:611:22 | clean | taint.cpp:611:10:611:16 | call to _strinc | TAINT |
| taint.cpp:611:25:611:30 | locale | taint.cpp:611:10:611:16 | call to _strinc | TAINT |
| taint.cpp:611:25:611:30 | ref arg locale | taint.cpp:606:82:606:87 | locale | |
| taint.cpp:612:7:612:11 | ref arg dest2 | taint.cpp:606:65:606:69 | dest2 | |
| taint.cpp:612:7:612:11 | ref arg dest2 | taint.cpp:613:8:613:12 | dest2 | |
| taint.cpp:613:8:613:12 | dest2 | taint.cpp:613:7:613:12 | * ... | TAINT |
| taint.cpp:616:34:616:48 | source_unsigned | taint.cpp:616:34:616:48 | source_unsigned | |
| taint.cpp:616:34:616:48 | source_unsigned | taint.cpp:617:26:617:40 | source_unsigned | |
| taint.cpp:616:57:616:62 | source | taint.cpp:616:57:616:62 | source | |
| taint.cpp:616:57:616:62 | source | taint.cpp:621:40:621:45 | source | |
| taint.cpp:617:18:617:24 | call to _mbsinc | taint.cpp:616:80:616:92 | dest_unsigned | |
| taint.cpp:617:18:617:24 | call to _mbsinc | taint.cpp:617:2:617:41 | ... = ... | |
| taint.cpp:617:18:617:24 | call to _mbsinc | taint.cpp:618:7:618:19 | dest_unsigned | |
| taint.cpp:617:18:617:24 | call to _mbsinc | taint.cpp:619:8:619:20 | dest_unsigned | |
| taint.cpp:617:26:617:40 | source_unsigned | taint.cpp:617:18:617:24 | call to _mbsinc | TAINT |
| taint.cpp:618:7:618:19 | ref arg dest_unsigned | taint.cpp:616:80:616:92 | dest_unsigned | |
| taint.cpp:618:7:618:19 | ref arg dest_unsigned | taint.cpp:619:8:619:20 | dest_unsigned | |
| taint.cpp:619:8:619:20 | dest_unsigned | taint.cpp:619:7:619:20 | * ... | TAINT |
| taint.cpp:621:16:621:22 | call to _mbsinc | taint.cpp:616:101:616:104 | dest | |
| taint.cpp:621:16:621:22 | call to _mbsinc | taint.cpp:621:2:621:46 | ... = ... | |
| taint.cpp:621:16:621:22 | call to _mbsinc | taint.cpp:622:7:622:10 | dest | |
| taint.cpp:621:16:621:22 | call to _mbsinc | taint.cpp:623:8:623:11 | dest | |
| taint.cpp:621:40:621:45 | source | taint.cpp:621:16:621:22 | call to _mbsinc | TAINT |
| taint.cpp:622:7:622:10 | ref arg dest | taint.cpp:616:101:616:104 | dest | |
| taint.cpp:622:7:622:10 | ref arg dest | taint.cpp:623:8:623:11 | dest | |
| taint.cpp:623:8:623:11 | dest | taint.cpp:623:7:623:11 | * ... | TAINT |
| taint.cpp:626:40:626:45 | source | taint.cpp:626:40:626:45 | source | |
| taint.cpp:626:40:626:45 | source | taint.cpp:627:18:627:23 | source | |
| taint.cpp:626:40:626:45 | source | taint.cpp:627:31:627:36 | source | |
| taint.cpp:626:40:626:45 | source | taint.cpp:633:25:633:30 | source | |
| taint.cpp:626:40:626:45 | source | taint.cpp:638:18:638:23 | source | |
| taint.cpp:626:63:626:67 | clean | taint.cpp:626:63:626:67 | clean | |
| taint.cpp:626:63:626:67 | clean | taint.cpp:633:18:633:22 | clean | |
| taint.cpp:626:63:626:67 | clean | taint.cpp:638:26:638:30 | clean | |
| taint.cpp:627:10:627:16 | call to _strdec | taint.cpp:626:85:626:89 | dest1 | |
| taint.cpp:627:10:627:16 | call to _strdec | taint.cpp:627:2:627:37 | ... = ... | |
| taint.cpp:627:10:627:16 | call to _strdec | taint.cpp:628:7:628:11 | dest1 | |
| taint.cpp:627:10:627:16 | call to _strdec | taint.cpp:629:8:629:12 | dest1 | |
@@ -6400,20 +6466,25 @@
| taint.cpp:627:18:627:28 | ... + ... | taint.cpp:627:10:627:16 | call to _strdec | TAINT |
| taint.cpp:627:27:627:28 | 12 | taint.cpp:627:18:627:28 | ... + ... | TAINT |
| taint.cpp:627:31:627:36 | source | taint.cpp:627:10:627:16 | call to _strdec | TAINT |
| taint.cpp:628:7:628:11 | ref arg dest1 | taint.cpp:626:85:626:89 | dest1 | |
| taint.cpp:628:7:628:11 | ref arg dest1 | taint.cpp:629:8:629:12 | dest1 | |
| taint.cpp:629:8:629:12 | dest1 | taint.cpp:629:7:629:12 | * ... | TAINT |
| taint.cpp:633:10:633:16 | call to _strdec | taint.cpp:626:107:626:111 | dest2 | |
| taint.cpp:633:10:633:16 | call to _strdec | taint.cpp:633:2:633:31 | ... = ... | |
| taint.cpp:633:10:633:16 | call to _strdec | taint.cpp:634:7:634:11 | dest2 | |
| taint.cpp:633:10:633:16 | call to _strdec | taint.cpp:635:8:635:12 | dest2 | |
| taint.cpp:633:18:633:22 | clean | taint.cpp:633:10:633:16 | call to _strdec | TAINT |
| taint.cpp:633:25:633:30 | source | taint.cpp:633:10:633:16 | call to _strdec | TAINT |
| taint.cpp:634:7:634:11 | ref arg dest2 | taint.cpp:626:107:626:111 | dest2 | |
| taint.cpp:634:7:634:11 | ref arg dest2 | taint.cpp:635:8:635:12 | dest2 | |
| taint.cpp:635:8:635:12 | dest2 | taint.cpp:635:7:635:12 | * ... | TAINT |
| taint.cpp:638:10:638:16 | call to _strdec | taint.cpp:626:129:626:133 | dest3 | |
| taint.cpp:638:10:638:16 | call to _strdec | taint.cpp:638:2:638:31 | ... = ... | |
| taint.cpp:638:10:638:16 | call to _strdec | taint.cpp:639:7:639:11 | dest3 | |
| taint.cpp:638:10:638:16 | call to _strdec | taint.cpp:640:8:640:12 | dest3 | |
| taint.cpp:638:18:638:23 | source | taint.cpp:638:10:638:16 | call to _strdec | TAINT |
| taint.cpp:638:26:638:30 | clean | taint.cpp:638:10:638:16 | call to _strdec | TAINT |
| taint.cpp:639:7:639:11 | ref arg dest3 | taint.cpp:626:129:626:133 | dest3 | |
| taint.cpp:639:7:639:11 | ref arg dest3 | taint.cpp:640:8:640:12 | dest3 | |
| taint.cpp:640:8:640:12 | dest3 | taint.cpp:640:7:640:12 | * ... | TAINT |
| taint.cpp:647:33:647:38 | source | taint.cpp:650:17:650:22 | source | |
@@ -6427,19 +6498,23 @@
| taint.cpp:653:6:653:14 | call to _strnextc | taint.cpp:654:7:654:7 | c | |
| taint.cpp:653:16:653:17 | | taint.cpp:653:6:653:14 | call to _strnextc | TAINT |
| taint.cpp:662:9:662:12 | this | taint.cpp:662:25:662:29 | this | |
| taint.cpp:665:33:665:38 | source | taint.cpp:665:33:665:38 | source | |
| taint.cpp:665:33:665:38 | source | taint.cpp:667:20:667:25 | source | |
| taint.cpp:666:30:666:30 | c | taint.cpp:667:10:667:10 | c | |
| taint.cpp:666:30:666:30 | c | taint.cpp:668:8:668:8 | c | |
| taint.cpp:667:10:667:10 | ref arg c | taint.cpp:668:8:668:8 | c | |
| taint.cpp:667:12:667:15 | call to data | taint.cpp:667:3:667:8 | call to memcpy | |
| taint.cpp:667:20:667:25 | ref arg source | taint.cpp:665:33:665:38 | source | |
| taint.cpp:667:20:667:25 | source | taint.cpp:667:3:667:8 | call to memcpy | TAINT |
| taint.cpp:667:20:667:25 | source | taint.cpp:667:12:667:15 | ref arg call to data | TAINT |
| taint.cpp:674:9:674:12 | this | taint.cpp:674:31:674:35 | this | |
| taint.cpp:677:35:677:40 | source | taint.cpp:677:35:677:40 | source | |
| taint.cpp:677:35:677:40 | source | taint.cpp:679:20:679:25 | source | |
| taint.cpp:678:27:678:27 | c | taint.cpp:679:10:679:10 | c | |
| taint.cpp:678:27:678:27 | c | taint.cpp:680:8:680:8 | c | |
| taint.cpp:679:10:679:10 | ref arg c | taint.cpp:680:8:680:8 | c | |
| taint.cpp:679:12:679:15 | call to data | taint.cpp:679:3:679:8 | call to memcpy | |
| taint.cpp:679:20:679:25 | ref arg source | taint.cpp:677:35:677:40 | source | |
| taint.cpp:679:20:679:25 | source | taint.cpp:679:3:679:8 | call to memcpy | TAINT |
| taint.cpp:679:20:679:25 | source | taint.cpp:679:12:679:15 | ref arg call to data | TAINT |
| taint.cpp:690:14:690:14 | s | taint.cpp:691:18:691:18 | s | |
@@ -7782,6 +7857,7 @@
| vector.cpp:413:11:413:16 | call to source | vector.cpp:413:2:413:2 | call to operator* [post update] | TAINT |
| vector.cpp:413:11:413:16 | call to source | vector.cpp:413:2:413:18 | ... = ... | |
| vector.cpp:414:7:414:9 | ref arg v14 | vector.cpp:415:1:415:1 | v14 | |
| vector.cpp:417:33:417:45 | source_string | vector.cpp:417:33:417:45 | source_string | |
| vector.cpp:417:33:417:45 | source_string | vector.cpp:421:23:421:35 | source_string | |
| vector.cpp:417:33:417:45 | source_string | vector.cpp:428:23:428:35 | source_string | |
| vector.cpp:417:33:417:45 | source_string | vector.cpp:442:23:442:35 | source_string | |

View File

@@ -1,3 +1,3 @@
| file://:0:0:0:0 | __wchar_t * | PointerType | Wchar_t, WideCharType |
| file://:0:0:0:0 | __wchar_t * | IteratorByPointer, PointerType | Wchar_t, WideCharType |
| file://:0:0:0:0 | const __wchar_t | SpecifiedType | Wchar_t, WideCharType |
| file://:0:0:0:0 | wchar_t | Wchar_t, WideCharType | |

View File

@@ -1,3 +1,3 @@
| file://:0:0:0:0 | wchar_t | Wchar_t, WideCharType | |
| file://:0:0:0:0 | wchar_t * | PointerType | CTypedefType, Wchar_t |
| file://:0:0:0:0 | wchar_t * | IteratorByPointer, PointerType | CTypedefType, Wchar_t |
| ms.c:2:24:2:30 | wchar_t | CTypedefType, Wchar_t | |

View File

@@ -30,7 +30,7 @@
| char8_t | Char8Type | | | | |
| char16_t | Char16Type | | | | |
| char32_t | Char32Type | | | | |
| char * | CharPointerType | | char | | |
| char * | CharPointerType, IteratorByPointer | | char | | |
| char *[3] | ArrayType | char * | char * | | |
| char *[32] | ArrayType | char * | char * | | |
| char *[] | ArrayType | char * | char * | | |
@@ -48,7 +48,7 @@
| const address | SpecifiedType | | address | | |
| const address & | LValueReferenceType | | const address | | |
| const char | SpecifiedType | | char | | |
| const char * | PointerType | | const char | | |
| const char * | IteratorByPointer, PointerType | | const char | | |
| const char *[3] | ArrayType | const char * | const char * | | |
| const char *[] | ArrayType | const char * | const char * | | |
| const char[5] | ArrayType | const char | const char | | |
@@ -65,7 +65,7 @@
| float | FloatType | | | | |
| float[3] | ArrayType | float | float | | |
| int | IntType, MicrosoftInt32Type | | | | |
| int * | IntPointerType | | int | | |
| int * | IntPointerType, IteratorByPointer | | int | | |
| int[4] | ArrayType | int | int | | |
| int[8] | ArrayType | int | int | | |
| int[10] | ArrayType | int | int | | |
@@ -90,5 +90,5 @@
| unsigned long long | LongLongType | | | | unsigned integral |
| unsigned short | ShortType | | | | unsigned integral |
| void | VoidType | | | | |
| void * | VoidPointerType | | void | | |
| void * | IteratorByPointer, VoidPointerType | | void | | |
| wchar_t | Wchar_t, WideCharType | | | | |

View File

@@ -1,47 +1,137 @@
edges
| test.cpp:26:29:26:29 | b | test.cpp:26:29:26:29 | b |
| test.cpp:26:29:26:29 | b | test.cpp:27:2:27:2 | b |
| test.cpp:30:34:30:34 | b | test.cpp:31:2:31:2 | b |
| test.cpp:34:31:34:31 | b | test.cpp:34:31:34:31 | b |
| test.cpp:34:31:34:31 | b | test.cpp:35:2:35:2 | b |
| test.cpp:38:35:38:35 | d | test.cpp:38:35:38:35 | d |
| test.cpp:38:35:38:35 | d | test.cpp:39:2:39:2 | d |
| test.cpp:42:40:42:40 | d | test.cpp:43:2:43:2 | d |
| test.cpp:46:37:46:37 | d | test.cpp:46:37:46:37 | d |
| test.cpp:46:37:46:37 | d | test.cpp:47:2:47:2 | d |
| test.cpp:50:31:50:31 | b | test.cpp:51:11:51:11 | b |
| test.cpp:57:19:57:19 | d | test.cpp:26:29:26:29 | b |
| test.cpp:57:19:57:19 | d | test.cpp:57:19:57:19 | ref arg d |
| test.cpp:57:19:57:19 | ref arg d | test.cpp:58:25:58:25 | d |
| test.cpp:57:19:57:19 | ref arg d | test.cpp:59:21:59:21 | d |
| test.cpp:57:19:57:19 | ref arg d | test.cpp:61:22:61:22 | d |
| test.cpp:57:19:57:19 | ref arg d | test.cpp:62:28:62:28 | d |
| test.cpp:57:19:57:19 | ref arg d | test.cpp:63:24:63:24 | d |
| test.cpp:57:19:57:19 | ref arg d | test.cpp:95:21:95:21 | d |
| test.cpp:58:25:58:25 | d | test.cpp:30:34:30:34 | b |
| test.cpp:59:21:59:21 | d | test.cpp:34:31:34:31 | b |
| test.cpp:59:21:59:21 | d | test.cpp:59:21:59:21 | ref arg d |
| test.cpp:59:21:59:21 | ref arg d | test.cpp:61:22:61:22 | d |
| test.cpp:59:21:59:21 | ref arg d | test.cpp:62:28:62:28 | d |
| test.cpp:59:21:59:21 | ref arg d | test.cpp:63:24:63:24 | d |
| test.cpp:59:21:59:21 | ref arg d | test.cpp:95:21:95:21 | d |
| test.cpp:61:22:61:22 | d | test.cpp:38:35:38:35 | d |
| test.cpp:61:22:61:22 | d | test.cpp:61:22:61:22 | ref arg d |
| test.cpp:61:22:61:22 | ref arg d | test.cpp:62:28:62:28 | d |
| test.cpp:61:22:61:22 | ref arg d | test.cpp:63:24:63:24 | d |
| test.cpp:61:22:61:22 | ref arg d | test.cpp:95:21:95:21 | d |
| test.cpp:62:28:62:28 | d | test.cpp:42:40:42:40 | d |
| test.cpp:63:24:63:24 | d | test.cpp:46:37:46:37 | d |
| test.cpp:63:24:63:24 | d | test.cpp:63:24:63:24 | ref arg d |
| test.cpp:63:24:63:24 | ref arg d | test.cpp:95:21:95:21 | d |
| test.cpp:74:19:74:21 | dss | test.cpp:26:29:26:29 | b |
| test.cpp:74:19:74:21 | dss | test.cpp:74:19:74:21 | ref arg dss |
| test.cpp:74:19:74:21 | ref arg dss | test.cpp:75:25:75:27 | dss |
| test.cpp:74:19:74:21 | ref arg dss | test.cpp:76:21:76:23 | dss |
| test.cpp:74:19:74:21 | ref arg dss | test.cpp:96:21:96:23 | dss |
| test.cpp:75:25:75:27 | dss | test.cpp:30:34:30:34 | b |
| test.cpp:76:21:76:23 | dss | test.cpp:34:31:34:31 | b |
| test.cpp:76:21:76:23 | dss | test.cpp:76:21:76:23 | ref arg dss |
| test.cpp:76:21:76:23 | ref arg dss | test.cpp:96:21:96:23 | dss |
| test.cpp:86:19:86:20 | d2 | test.cpp:26:29:26:29 | b |
| test.cpp:86:19:86:20 | d2 | test.cpp:86:19:86:20 | ref arg d2 |
| test.cpp:86:19:86:20 | ref arg d2 | test.cpp:87:25:87:26 | d2 |
| test.cpp:86:19:86:20 | ref arg d2 | test.cpp:88:21:88:22 | d2 |
| test.cpp:86:19:86:20 | ref arg d2 | test.cpp:90:22:90:23 | d2 |
| test.cpp:86:19:86:20 | ref arg d2 | test.cpp:91:28:91:29 | d2 |
| test.cpp:86:19:86:20 | ref arg d2 | test.cpp:92:24:92:25 | d2 |
| test.cpp:87:25:87:26 | d2 | test.cpp:30:34:30:34 | b |
| test.cpp:88:21:88:22 | d2 | test.cpp:34:31:34:31 | b |
| test.cpp:88:21:88:22 | d2 | test.cpp:88:21:88:22 | ref arg d2 |
| test.cpp:88:21:88:22 | ref arg d2 | test.cpp:90:22:90:23 | d2 |
| test.cpp:88:21:88:22 | ref arg d2 | test.cpp:91:28:91:29 | d2 |
| test.cpp:88:21:88:22 | ref arg d2 | test.cpp:92:24:92:25 | d2 |
| test.cpp:90:22:90:23 | d2 | test.cpp:38:35:38:35 | d |
| test.cpp:90:22:90:23 | d2 | test.cpp:90:22:90:23 | ref arg d2 |
| test.cpp:90:22:90:23 | ref arg d2 | test.cpp:91:28:91:29 | d2 |
| test.cpp:90:22:90:23 | ref arg d2 | test.cpp:92:24:92:25 | d2 |
| test.cpp:91:28:91:29 | d2 | test.cpp:42:40:42:40 | d |
| test.cpp:92:24:92:25 | d2 | test.cpp:46:37:46:37 | d |
| test.cpp:95:21:95:21 | d | test.cpp:50:31:50:31 | b |
| test.cpp:96:21:96:23 | dss | test.cpp:50:31:50:31 | b |
nodes
| test.cpp:26:29:26:29 | b | semmle.label | b |
| test.cpp:26:29:26:29 | b | semmle.label | b |
| test.cpp:27:2:27:2 | b | semmle.label | b |
| test.cpp:30:34:30:34 | b | semmle.label | b |
| test.cpp:31:2:31:2 | b | semmle.label | b |
| test.cpp:34:31:34:31 | b | semmle.label | b |
| test.cpp:34:31:34:31 | b | semmle.label | b |
| test.cpp:35:2:35:2 | b | semmle.label | b |
| test.cpp:38:35:38:35 | d | semmle.label | d |
| test.cpp:38:35:38:35 | d | semmle.label | d |
| test.cpp:39:2:39:2 | d | semmle.label | d |
| test.cpp:42:40:42:40 | d | semmle.label | d |
| test.cpp:43:2:43:2 | d | semmle.label | d |
| test.cpp:46:37:46:37 | d | semmle.label | d |
| test.cpp:46:37:46:37 | d | semmle.label | d |
| test.cpp:47:2:47:2 | d | semmle.label | d |
| test.cpp:50:31:50:31 | b | semmle.label | b |
| test.cpp:51:11:51:11 | b | semmle.label | b |
| test.cpp:57:19:57:19 | d | semmle.label | d |
| test.cpp:57:19:57:19 | ref arg d | semmle.label | ref arg d |
| test.cpp:58:25:58:25 | d | semmle.label | d |
| test.cpp:59:21:59:21 | d | semmle.label | d |
| test.cpp:59:21:59:21 | ref arg d | semmle.label | ref arg d |
| test.cpp:61:22:61:22 | d | semmle.label | d |
| test.cpp:61:22:61:22 | ref arg d | semmle.label | ref arg d |
| test.cpp:62:28:62:28 | d | semmle.label | d |
| test.cpp:63:24:63:24 | d | semmle.label | d |
| test.cpp:63:24:63:24 | ref arg d | semmle.label | ref arg d |
| test.cpp:74:19:74:21 | dss | semmle.label | dss |
| test.cpp:74:19:74:21 | ref arg dss | semmle.label | ref arg dss |
| test.cpp:75:25:75:27 | dss | semmle.label | dss |
| test.cpp:76:21:76:23 | dss | semmle.label | dss |
| test.cpp:76:21:76:23 | ref arg dss | semmle.label | ref arg dss |
| test.cpp:86:19:86:20 | d2 | semmle.label | d2 |
| test.cpp:86:19:86:20 | ref arg d2 | semmle.label | ref arg d2 |
| test.cpp:87:25:87:26 | d2 | semmle.label | d2 |
| test.cpp:88:21:88:22 | d2 | semmle.label | d2 |
| test.cpp:88:21:88:22 | ref arg d2 | semmle.label | ref arg d2 |
| test.cpp:90:22:90:23 | d2 | semmle.label | d2 |
| test.cpp:90:22:90:23 | ref arg d2 | semmle.label | ref arg d2 |
| test.cpp:91:28:91:29 | d2 | semmle.label | d2 |
| test.cpp:92:24:92:25 | d2 | semmle.label | d2 |
| test.cpp:95:21:95:21 | d | semmle.label | d |
| test.cpp:96:21:96:23 | dss | semmle.label | dss |
subpaths
| test.cpp:57:19:57:19 | d | test.cpp:26:29:26:29 | b | test.cpp:26:29:26:29 | b | test.cpp:57:19:57:19 | ref arg d |
| test.cpp:59:21:59:21 | d | test.cpp:34:31:34:31 | b | test.cpp:34:31:34:31 | b | test.cpp:59:21:59:21 | ref arg d |
| test.cpp:61:22:61:22 | d | test.cpp:38:35:38:35 | d | test.cpp:38:35:38:35 | d | test.cpp:61:22:61:22 | ref arg d |
| test.cpp:63:24:63:24 | d | test.cpp:46:37:46:37 | d | test.cpp:46:37:46:37 | d | test.cpp:63:24:63:24 | ref arg d |
| test.cpp:74:19:74:21 | dss | test.cpp:26:29:26:29 | b | test.cpp:26:29:26:29 | b | test.cpp:74:19:74:21 | ref arg dss |
| test.cpp:76:21:76:23 | dss | test.cpp:34:31:34:31 | b | test.cpp:34:31:34:31 | b | test.cpp:76:21:76:23 | ref arg dss |
| test.cpp:86:19:86:20 | d2 | test.cpp:26:29:26:29 | b | test.cpp:26:29:26:29 | b | test.cpp:86:19:86:20 | ref arg d2 |
| test.cpp:88:21:88:22 | d2 | test.cpp:34:31:34:31 | b | test.cpp:34:31:34:31 | b | test.cpp:88:21:88:22 | ref arg d2 |
| test.cpp:90:22:90:23 | d2 | test.cpp:38:35:38:35 | d | test.cpp:38:35:38:35 | d | test.cpp:90:22:90:23 | ref arg d2 |
#select
| test.cpp:27:2:27:2 | b | test.cpp:57:19:57:19 | d | test.cpp:27:2:27:2 | b | Pointer arithmetic here may be done with the wrong type because of the cast $@. | test.cpp:57:19:57:19 | d | here |
| test.cpp:27:2:27:2 | b | test.cpp:74:19:74:21 | dss | test.cpp:27:2:27:2 | b | Pointer arithmetic here may be done with the wrong type because of the cast $@. | test.cpp:74:19:74:21 | dss | here |
| test.cpp:27:2:27:2 | b | test.cpp:86:19:86:20 | d2 | test.cpp:27:2:27:2 | b | Pointer arithmetic here may be done with the wrong type because of the cast $@. | test.cpp:86:19:86:20 | d2 | here |
| test.cpp:31:2:31:2 | b | test.cpp:57:19:57:19 | d | test.cpp:31:2:31:2 | b | Pointer arithmetic here may be done with the wrong type because of the cast $@. | test.cpp:57:19:57:19 | d | here |
| test.cpp:31:2:31:2 | b | test.cpp:58:25:58:25 | d | test.cpp:31:2:31:2 | b | Pointer arithmetic here may be done with the wrong type because of the cast $@. | test.cpp:58:25:58:25 | d | here |
| test.cpp:31:2:31:2 | b | test.cpp:74:19:74:21 | dss | test.cpp:31:2:31:2 | b | Pointer arithmetic here may be done with the wrong type because of the cast $@. | test.cpp:74:19:74:21 | dss | here |
| test.cpp:31:2:31:2 | b | test.cpp:75:25:75:27 | dss | test.cpp:31:2:31:2 | b | Pointer arithmetic here may be done with the wrong type because of the cast $@. | test.cpp:75:25:75:27 | dss | here |
| test.cpp:31:2:31:2 | b | test.cpp:86:19:86:20 | d2 | test.cpp:31:2:31:2 | b | Pointer arithmetic here may be done with the wrong type because of the cast $@. | test.cpp:86:19:86:20 | d2 | here |
| test.cpp:31:2:31:2 | b | test.cpp:87:25:87:26 | d2 | test.cpp:31:2:31:2 | b | Pointer arithmetic here may be done with the wrong type because of the cast $@. | test.cpp:87:25:87:26 | d2 | here |
| test.cpp:35:2:35:2 | b | test.cpp:57:19:57:19 | d | test.cpp:35:2:35:2 | b | Pointer arithmetic here may be done with the wrong type because of the cast $@. | test.cpp:57:19:57:19 | d | here |
| test.cpp:35:2:35:2 | b | test.cpp:59:21:59:21 | d | test.cpp:35:2:35:2 | b | Pointer arithmetic here may be done with the wrong type because of the cast $@. | test.cpp:59:21:59:21 | d | here |
| test.cpp:35:2:35:2 | b | test.cpp:74:19:74:21 | dss | test.cpp:35:2:35:2 | b | Pointer arithmetic here may be done with the wrong type because of the cast $@. | test.cpp:74:19:74:21 | dss | here |
| test.cpp:35:2:35:2 | b | test.cpp:76:21:76:23 | dss | test.cpp:35:2:35:2 | b | Pointer arithmetic here may be done with the wrong type because of the cast $@. | test.cpp:76:21:76:23 | dss | here |
| test.cpp:35:2:35:2 | b | test.cpp:86:19:86:20 | d2 | test.cpp:35:2:35:2 | b | Pointer arithmetic here may be done with the wrong type because of the cast $@. | test.cpp:86:19:86:20 | d2 | here |
| test.cpp:35:2:35:2 | b | test.cpp:88:21:88:22 | d2 | test.cpp:35:2:35:2 | b | Pointer arithmetic here may be done with the wrong type because of the cast $@. | test.cpp:88:21:88:22 | d2 | here |

View File

@@ -85,6 +85,9 @@ module Public {
/** Holds if this stack contains summary component `c`. */
predicate contains(SummaryComponent c) { c = this.drop(_).head() }
/** Gets the bottom element of this stack. */
SummaryComponent bottom() { result = this.drop(this.length() - 1).head() }
/** Gets a textual representation of this stack. */
string toString() {
exists(SummaryComponent head, SummaryComponentStack tail |
@@ -197,6 +200,8 @@ module Private {
or
tail.(RequiredSummaryComponentStack).required(TParameterSummaryComponent(_)) and
head = thisParam()
or
derivedFluentFlowPush(_, _, _, head, tail, _)
}
pragma[nomagic]
@@ -210,7 +215,7 @@ module Private {
c.propagatesFlow(output, input, preservesValue) and
preservesValue = true and
isCallbackParameter(input) and
isContentOfArgument(output)
isContentOfArgument(output, _)
or
// flow from the receiver of a callback into the instance-parameter
exists(SummaryComponentStack s, SummaryComponentStack callbackRef |
@@ -222,16 +227,81 @@ module Private {
output = TConsSummaryComponentStack(thisParam(), input) and
preservesValue = true
)
or
exists(SummaryComponentStack arg, SummaryComponentStack return |
derivedFluentFlow(c, input, arg, return, preservesValue)
|
arg.length() = 1 and
output = return
or
exists(SummaryComponent head, SummaryComponentStack tail |
derivedFluentFlowPush(c, input, arg, head, tail, 0) and
output = SummaryComponentStack::push(head, tail)
)
)
or
// Chain together summaries where values get passed into callbacks along the way
exists(SummaryComponentStack mid, boolean preservesValue1, boolean preservesValue2 |
c.propagatesFlow(input, mid, preservesValue1) and
c.propagatesFlow(mid, output, preservesValue2) and
mid.drop(mid.length() - 2) =
SummaryComponentStack::push(TParameterSummaryComponent(_),
SummaryComponentStack::singleton(TArgumentSummaryComponent(_))) and
preservesValue = preservesValue1.booleanAnd(preservesValue2)
)
}
/**
* Holds if `c` has a flow summary from `input` to `arg`, where `arg`
* writes to (contents of) the `i`th argument, and `c` has a
* value-preserving flow summary from the `i`th argument to a return value
* (`return`).
*
* In such a case, we derive flow from `input` to (contents of) the return
* value.
*
* As an example, this simplifies modeling of fluent methods:
* for `StringBuilder.append(x)` with a specified value flow from qualifier to
* return value and taint flow from argument 0 to the qualifier, then this
* allows us to infer taint flow from argument 0 to the return value.
*/
pragma[nomagic]
private predicate derivedFluentFlow(
SummarizedCallable c, SummaryComponentStack input, SummaryComponentStack arg,
SummaryComponentStack return, boolean preservesValue
) {
exists(int i |
summary(c, input, arg, preservesValue) and
isContentOfArgument(arg, i) and
summary(c, SummaryComponentStack::singleton(TArgumentSummaryComponent(i)), return, true) and
return.bottom() = TReturnSummaryComponent(_)
)
}
pragma[nomagic]
private predicate derivedFluentFlowPush(
SummarizedCallable c, SummaryComponentStack input, SummaryComponentStack arg,
SummaryComponent head, SummaryComponentStack tail, int i
) {
derivedFluentFlow(c, input, arg, tail, _) and
head = arg.drop(i).head() and
i = arg.length() - 2
or
exists(SummaryComponent head0, SummaryComponentStack tail0 |
derivedFluentFlowPush(c, input, arg, head0, tail0, i + 1) and
head = arg.drop(i).head() and
tail = SummaryComponentStack::push(head0, tail0)
)
}
private predicate isCallbackParameter(SummaryComponentStack s) {
s.head() = TParameterSummaryComponent(_) and exists(s.tail())
}
private predicate isContentOfArgument(SummaryComponentStack s) {
s.head() = TContentSummaryComponent(_) and isContentOfArgument(s.tail())
private predicate isContentOfArgument(SummaryComponentStack s, int i) {
s.head() = TContentSummaryComponent(_) and isContentOfArgument(s.tail(), i)
or
s = TSingletonSummaryComponentStack(TArgumentSummaryComponent(_))
s = TSingletonSummaryComponentStack(TArgumentSummaryComponent(i))
}
private predicate outputState(SummarizedCallable c, SummaryComponentStack s) {
@@ -508,9 +578,14 @@ module Private {
* node, and back out to `p`.
*/
predicate summaryAllowParameterReturnInSelf(ParamNode p) {
exists(SummarizedCallable c, int i |
c.clearsContent(i, _) and
p.isParameterOf(c, i)
exists(SummarizedCallable c, int i | p.isParameterOf(c, i) |
c.clearsContent(i, _)
or
exists(SummaryComponentStack inputContents, SummaryComponentStack outputContents |
summary(c, inputContents, outputContents, _) and
inputContents.bottom() = pragma[only_bind_into](TArgumentSummaryComponent(i)) and
outputContents.bottom() = pragma[only_bind_into](TArgumentSummaryComponent(i))
)
)
}
@@ -534,22 +609,6 @@ module Private {
preservesValue = false and not summary(c, inputContents, outputContents, true)
)
or
// If flow through a method updates a parameter from some input A, and that
// parameter also is returned through B, then we'd like a combined flow from A
// to B as well. As an example, this simplifies modeling of fluent methods:
// for `StringBuilder.append(x)` with a specified value flow from qualifier to
// return value and taint flow from argument 0 to the qualifier, then this
// allows us to infer taint flow from argument 0 to the return value.
succ instanceof ParamNode and
summaryPostUpdateNode(pred, succ) and
preservesValue = true
or
// Similarly we would like to chain together summaries where values get passed
// into callbacks along the way.
pred instanceof ArgNode and
summaryPostUpdateNode(succ, pred) and
preservesValue = true
or
exists(SummarizedCallable c, int i |
pred.(ParamNode).isParameterOf(c, i) and
succ = summaryNode(c, TSummaryNodeClearsContentState(i, _)) and

View File

@@ -174,32 +174,34 @@ module EntityFramework {
}
}
private class RawSqlStringSummarizedCallable extends EFSummarizedCallable {
private SummaryComponentStack input_;
private SummaryComponentStack output_;
private boolean preservesValue_;
RawSqlStringSummarizedCallable() {
private class RawSqlStringConstructorSummarizedCallable extends EFSummarizedCallable {
RawSqlStringConstructorSummarizedCallable() {
exists(RawSqlStringStruct s |
this = s.getAConstructor() and
input_ = SummaryComponentStack::argument(0) and
this.getNumberOfParameters() > 0 and
output_ = SummaryComponentStack::return() and
preservesValue_ = false
or
this = s.getAConversionTo() and
input_ = SummaryComponentStack::argument(0) and
output_ = SummaryComponentStack::return() and
preservesValue_ = false
this.getNumberOfParameters() > 0
)
}
override predicate propagatesFlow(
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
) {
input = input_ and
output = output_ and
preservesValue = preservesValue_
input = SummaryComponentStack::argument(0) and
output = SummaryComponentStack::return() and
preservesValue = false
}
}
private class RawSqlStringConversionSummarizedCallable extends EFSummarizedCallable {
RawSqlStringConversionSummarizedCallable() {
exists(RawSqlStringStruct s | this = s.getAConversionTo())
}
override predicate propagatesFlow(
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
) {
input = SummaryComponentStack::argument(0) and
output = SummaryComponentStack::return() and
preservesValue = false
}
}

View File

@@ -33,7 +33,7 @@ namespace My.Qltest
void M5()
{
this.StepFieldSetter(new object());
Sink(((D)this.StepFieldSetter(new object()).Field2).Field);
Sink(this.Field);
}
@@ -102,7 +102,7 @@ namespace My.Qltest
Sink(d);
}, d1, d2);
Sink(d1.Field);
Sink(d2.Field2); // SPURIOUS FLOW
Sink(d2.Field2);
}
object StepArgRes(object x) { return null; }
@@ -120,7 +120,7 @@ namespace My.Qltest
object StepFieldGetter() => throw null;
void StepFieldSetter(object value) => throw null;
D StepFieldSetter(object value) => throw null;
object Property { get; set; }

View File

@@ -11,8 +11,12 @@ edges
| ExternalFlow.cs:30:13:30:16 | [post] this access [field Field] : Object | ExternalFlow.cs:31:18:31:21 | this access [field Field] : Object |
| ExternalFlow.cs:30:26:30:37 | object creation of type Object : Object | ExternalFlow.cs:30:13:30:16 | [post] this access [field Field] : Object |
| ExternalFlow.cs:31:18:31:21 | this access [field Field] : Object | ExternalFlow.cs:31:18:31:39 | call to method StepFieldGetter |
| ExternalFlow.cs:36:13:36:16 | [post] this access [field Field] : Object | ExternalFlow.cs:37:18:37:21 | this access [field Field] : Object |
| ExternalFlow.cs:36:34:36:45 | object creation of type Object : Object | ExternalFlow.cs:36:13:36:16 | [post] this access [field Field] : Object |
| ExternalFlow.cs:36:19:36:62 | (...) ... [field Field] : Object | ExternalFlow.cs:36:18:36:69 | access to field Field |
| ExternalFlow.cs:36:22:36:25 | [post] this access [field Field] : Object | ExternalFlow.cs:37:18:37:21 | this access [field Field] : Object |
| ExternalFlow.cs:36:22:36:55 | call to method StepFieldSetter [field Field2, field Field] : Object | ExternalFlow.cs:36:22:36:62 | access to field Field2 [field Field] : Object |
| ExternalFlow.cs:36:22:36:62 | access to field Field2 [field Field] : Object | ExternalFlow.cs:36:19:36:62 | (...) ... [field Field] : Object |
| ExternalFlow.cs:36:43:36:54 | object creation of type Object : Object | ExternalFlow.cs:36:22:36:25 | [post] this access [field Field] : Object |
| ExternalFlow.cs:36:43:36:54 | object creation of type Object : Object | ExternalFlow.cs:36:22:36:55 | call to method StepFieldSetter [field Field2, field Field] : Object |
| ExternalFlow.cs:37:18:37:21 | this access [field Field] : Object | ExternalFlow.cs:37:18:37:27 | access to field Field |
| ExternalFlow.cs:42:13:42:16 | [post] this access [property Property] : Object | ExternalFlow.cs:43:18:43:21 | this access [property Property] : Object |
| ExternalFlow.cs:42:29:42:40 | object creation of type Object : Object | ExternalFlow.cs:42:13:42:16 | [post] this access [property Property] : Object |
@@ -48,10 +52,7 @@ edges
| ExternalFlow.cs:98:24:98:35 | object creation of type Object : Object | ExternalFlow.cs:98:13:98:14 | [post] access to local variable d1 [field Field] : Object |
| ExternalFlow.cs:100:20:100:20 | d : Object | ExternalFlow.cs:102:22:102:22 | access to parameter d |
| ExternalFlow.cs:103:16:103:17 | access to local variable d1 [field Field] : Object | ExternalFlow.cs:100:20:100:20 | d : Object |
| ExternalFlow.cs:103:16:103:17 | access to local variable d1 [field Field] : Object | ExternalFlow.cs:103:20:103:21 | [post] access to local variable d2 [field Field2] : Object |
| ExternalFlow.cs:103:20:103:21 | [post] access to local variable d2 [field Field2] : Object | ExternalFlow.cs:105:18:105:19 | access to local variable d2 [field Field2] : Object |
| ExternalFlow.cs:104:18:104:19 | access to local variable d1 [field Field] : Object | ExternalFlow.cs:104:18:104:25 | access to field Field |
| ExternalFlow.cs:105:18:105:19 | access to local variable d2 [field Field2] : Object | ExternalFlow.cs:105:18:105:26 | access to field Field2 |
nodes
| ExternalFlow.cs:9:27:9:38 | object creation of type Object : Object | semmle.label | object creation of type Object : Object |
| ExternalFlow.cs:10:18:10:33 | call to method StepArgRes | semmle.label | call to method StepArgRes |
@@ -69,8 +70,12 @@ nodes
| ExternalFlow.cs:30:26:30:37 | object creation of type Object : Object | semmle.label | object creation of type Object : Object |
| ExternalFlow.cs:31:18:31:21 | this access [field Field] : Object | semmle.label | this access [field Field] : Object |
| ExternalFlow.cs:31:18:31:39 | call to method StepFieldGetter | semmle.label | call to method StepFieldGetter |
| ExternalFlow.cs:36:13:36:16 | [post] this access [field Field] : Object | semmle.label | [post] this access [field Field] : Object |
| ExternalFlow.cs:36:34:36:45 | object creation of type Object : Object | semmle.label | object creation of type Object : Object |
| ExternalFlow.cs:36:18:36:69 | access to field Field | semmle.label | access to field Field |
| ExternalFlow.cs:36:19:36:62 | (...) ... [field Field] : Object | semmle.label | (...) ... [field Field] : Object |
| ExternalFlow.cs:36:22:36:25 | [post] this access [field Field] : Object | semmle.label | [post] this access [field Field] : Object |
| ExternalFlow.cs:36:22:36:55 | call to method StepFieldSetter [field Field2, field Field] : Object | semmle.label | call to method StepFieldSetter [field Field2, field Field] : Object |
| ExternalFlow.cs:36:22:36:62 | access to field Field2 [field Field] : Object | semmle.label | access to field Field2 [field Field] : Object |
| ExternalFlow.cs:36:43:36:54 | object creation of type Object : Object | semmle.label | object creation of type Object : Object |
| ExternalFlow.cs:37:18:37:21 | this access [field Field] : Object | semmle.label | this access [field Field] : Object |
| ExternalFlow.cs:37:18:37:27 | access to field Field | semmle.label | access to field Field |
| ExternalFlow.cs:42:13:42:16 | [post] this access [property Property] : Object | semmle.label | [post] this access [property Property] : Object |
@@ -116,11 +121,8 @@ nodes
| ExternalFlow.cs:100:20:100:20 | d : Object | semmle.label | d : Object |
| ExternalFlow.cs:102:22:102:22 | access to parameter d | semmle.label | access to parameter d |
| ExternalFlow.cs:103:16:103:17 | access to local variable d1 [field Field] : Object | semmle.label | access to local variable d1 [field Field] : Object |
| ExternalFlow.cs:103:20:103:21 | [post] access to local variable d2 [field Field2] : Object | semmle.label | [post] access to local variable d2 [field Field2] : Object |
| ExternalFlow.cs:104:18:104:19 | access to local variable d1 [field Field] : Object | semmle.label | access to local variable d1 [field Field] : Object |
| ExternalFlow.cs:104:18:104:25 | access to field Field | semmle.label | access to field Field |
| ExternalFlow.cs:105:18:105:19 | access to local variable d2 [field Field2] : Object | semmle.label | access to local variable d2 [field Field2] : Object |
| ExternalFlow.cs:105:18:105:26 | access to field Field2 | semmle.label | access to field Field2 |
subpaths
invalidModelRow
#select
@@ -129,7 +131,8 @@ invalidModelRow
| ExternalFlow.cs:18:18:18:24 | access to local variable argOut1 | ExternalFlow.cs:16:30:16:41 | object creation of type Object : Object | ExternalFlow.cs:18:18:18:24 | access to local variable argOut1 | $@ | ExternalFlow.cs:16:30:16:41 | object creation of type Object : Object | object creation of type Object : Object |
| ExternalFlow.cs:25:18:25:21 | this access | ExternalFlow.cs:23:27:23:38 | object creation of type Object : Object | ExternalFlow.cs:25:18:25:21 | this access | $@ | ExternalFlow.cs:23:27:23:38 | object creation of type Object : Object | object creation of type Object : Object |
| ExternalFlow.cs:31:18:31:39 | call to method StepFieldGetter | ExternalFlow.cs:30:26:30:37 | object creation of type Object : Object | ExternalFlow.cs:31:18:31:39 | call to method StepFieldGetter | $@ | ExternalFlow.cs:30:26:30:37 | object creation of type Object : Object | object creation of type Object : Object |
| ExternalFlow.cs:37:18:37:27 | access to field Field | ExternalFlow.cs:36:34:36:45 | object creation of type Object : Object | ExternalFlow.cs:37:18:37:27 | access to field Field | $@ | ExternalFlow.cs:36:34:36:45 | object creation of type Object : Object | object creation of type Object : Object |
| ExternalFlow.cs:36:18:36:69 | access to field Field | ExternalFlow.cs:36:43:36:54 | object creation of type Object : Object | ExternalFlow.cs:36:18:36:69 | access to field Field | $@ | ExternalFlow.cs:36:43:36:54 | object creation of type Object : Object | object creation of type Object : Object |
| ExternalFlow.cs:37:18:37:27 | access to field Field | ExternalFlow.cs:36:43:36:54 | object creation of type Object : Object | ExternalFlow.cs:37:18:37:27 | access to field Field | $@ | ExternalFlow.cs:36:43:36:54 | object creation of type Object : Object | object creation of type Object : Object |
| ExternalFlow.cs:43:18:43:42 | call to method StepPropertyGetter | ExternalFlow.cs:42:29:42:40 | object creation of type Object : Object | ExternalFlow.cs:43:18:43:42 | call to method StepPropertyGetter | $@ | ExternalFlow.cs:42:29:42:40 | object creation of type Object : Object | object creation of type Object : Object |
| ExternalFlow.cs:49:18:49:30 | access to property Property | ExternalFlow.cs:48:37:48:48 | object creation of type Object : Object | ExternalFlow.cs:49:18:49:30 | access to property Property | $@ | ExternalFlow.cs:48:37:48:48 | object creation of type Object : Object | object creation of type Object : Object |
| ExternalFlow.cs:55:18:55:41 | call to method StepElementGetter | ExternalFlow.cs:54:36:54:47 | object creation of type Object : Object | ExternalFlow.cs:55:18:55:41 | call to method StepElementGetter | $@ | ExternalFlow.cs:54:36:54:47 | object creation of type Object : Object | object creation of type Object : Object |
@@ -141,4 +144,3 @@ invalidModelRow
| ExternalFlow.cs:92:18:92:18 | (...) ... | ExternalFlow.cs:90:21:90:34 | object creation of type String : String | ExternalFlow.cs:92:18:92:18 | (...) ... | $@ | ExternalFlow.cs:90:21:90:34 | object creation of type String : String | object creation of type String : String |
| ExternalFlow.cs:102:22:102:22 | access to parameter d | ExternalFlow.cs:98:24:98:35 | object creation of type Object : Object | ExternalFlow.cs:102:22:102:22 | access to parameter d | $@ | ExternalFlow.cs:98:24:98:35 | object creation of type Object : Object | object creation of type Object : Object |
| ExternalFlow.cs:104:18:104:25 | access to field Field | ExternalFlow.cs:98:24:98:35 | object creation of type Object : Object | ExternalFlow.cs:104:18:104:25 | access to field Field | $@ | ExternalFlow.cs:98:24:98:35 | object creation of type Object : Object | object creation of type Object : Object |
| ExternalFlow.cs:105:18:105:26 | access to field Field2 | ExternalFlow.cs:98:24:98:35 | object creation of type Object : Object | ExternalFlow.cs:105:18:105:26 | access to field Field2 | $@ | ExternalFlow.cs:98:24:98:35 | object creation of type Object : Object | object creation of type Object : Object |

View File

@@ -17,6 +17,7 @@ class SummaryModelTest extends SummaryModelCsv {
"My.Qltest;D;false;StepArgQual;(System.Object);;Argument[0];Argument[-1];taint",
"My.Qltest;D;false;StepFieldGetter;();;Field[My.Qltest.D.Field] of Argument[-1];ReturnValue;value",
"My.Qltest;D;false;StepFieldSetter;(System.Object);;Argument[0];Field[My.Qltest.D.Field] of Argument[-1];value",
"My.Qltest;D;false;StepFieldSetter;(System.Object);;Argument[-1];Field[My.Qltest.D.Field2] of ReturnValue;value",
"My.Qltest;D;false;StepPropertyGetter;();;Property[My.Qltest.D.Property] of Argument[-1];ReturnValue;value",
"My.Qltest;D;false;StepPropertySetter;(System.Object);;Argument[0];Property[My.Qltest.D.Property] of Argument[-1];value",
"My.Qltest;D;false;StepElementGetter;();;Element of Argument[-1];ReturnValue;value",

View File

@@ -403,6 +403,7 @@
| LocalDataFlow.cs:235:9:235:14 | access to local variable sink36 | LocalDataFlow.cs:235:9:235:33 | call to method AppendLine |
| LocalDataFlow.cs:235:9:235:14 | access to local variable sink36 | LocalDataFlow.cs:236:15:236:20 | access to local variable sink36 |
| LocalDataFlow.cs:235:27:235:32 | access to local variable sink35 | LocalDataFlow.cs:235:9:235:14 | [post] access to local variable sink36 |
| LocalDataFlow.cs:235:27:235:32 | access to local variable sink35 | LocalDataFlow.cs:235:9:235:33 | call to method AppendLine |
| LocalDataFlow.cs:239:13:239:51 | SSA def(nonSink10) | LocalDataFlow.cs:240:15:240:23 | access to local variable nonSink10 |
| LocalDataFlow.cs:239:25:239:51 | object creation of type StringBuilder | LocalDataFlow.cs:239:13:239:51 | SSA def(nonSink10) |
| LocalDataFlow.cs:239:43:239:50 | access to local variable nonSink0 | LocalDataFlow.cs:239:25:239:51 | object creation of type StringBuilder |
@@ -419,6 +420,7 @@
| LocalDataFlow.cs:243:9:243:17 | access to local variable nonSink10 | LocalDataFlow.cs:243:9:243:38 | call to method AppendLine |
| LocalDataFlow.cs:243:9:243:17 | access to local variable nonSink10 | LocalDataFlow.cs:244:15:244:23 | access to local variable nonSink10 |
| LocalDataFlow.cs:243:30:243:37 | access to local variable nonSink0 | LocalDataFlow.cs:243:9:243:17 | [post] access to local variable nonSink10 |
| LocalDataFlow.cs:243:30:243:37 | access to local variable nonSink0 | LocalDataFlow.cs:243:9:243:38 | call to method AppendLine |
| LocalDataFlow.cs:247:13:247:52 | SSA def(taintedDataContract) | LocalDataFlow.cs:248:22:248:40 | access to local variable taintedDataContract |
| LocalDataFlow.cs:247:13:247:52 | SSA qualifier def(taintedDataContract.AList) | LocalDataFlow.cs:250:22:250:46 | access to property AList |
| LocalDataFlow.cs:247:13:247:52 | SSA qualifier def(taintedDataContract.AString) | LocalDataFlow.cs:248:22:248:48 | access to property AString |

View File

@@ -20,7 +20,10 @@ setup, as outlined in the steps below.
If you use Linux, Windows, or macOS version 10.14 ("Mojave") or earlier, simply
follow the steps below. For macOS version 10.15 ("Catalina") or newer, steps 1
and 4 are slightly different---for further details, see the sections labeled
**Information for macOS "Catalina" (or newer) users**.
**Information for macOS "Catalina" (or newer) users**. If you are using macOS
on Apple Silicon (e.g. Apple M1), ensure that the `Xcode command-line developer
tools <https://developer.apple.com/downloads/index.action>`__ and `Rosetta 2
<https://support.apple.com/en-us/HT211861>`__ are installed.
For information about installing the CodeQL CLI in a CI system to create results
to display in GitHub as code scanning alerts, see

View File

@@ -0,0 +1,2 @@
lgtm,codescanning
* The predicate `Method.overrides(Method)` was accidentally transitive. This has been fixed. This fix also affects `Method.overridesOrInstantiates(Method)` and `Method.getASourceOverriddenMethod()`.

View File

@@ -27,7 +27,7 @@ jakarta.ws.rs.container,,9,,,,,,,,,,,,,,,,,,,,,,9,,
jakarta.ws.rs.core,2,,149,,,,,,,,,,,,,,,,2,,,,,,94,55
java.beans,,,1,,,,,,,,,,,,,,,,,,,,,,1,
java.io,3,,31,,3,,,,,,,,,,,,,,,,,,,,30,1
java.lang,,,51,,,,,,,,,,,,,,,,,,,,,,41,10
java.lang,,,52,,,,,,,,,,,,,,,,,,,,,,42,10
java.net,10,3,7,,,,,,,,,,,,10,,,,,,,,,3,7,
java.nio,10,,4,,10,,,,,,,,,,,,,,,,,,,,4,
java.sql,7,,,,,,,,,,,,,,,,7,,,,,,,,,
1 package sink source summary sink:bean-validation sink:create-file sink:groovy sink:header-splitting sink:information-leak sink:intent-start sink:jexl sink:jndi-injection sink:ldap sink:mvel sink:ognl-injection sink:open-url sink:set-hostname-verifier sink:sql sink:url-open-stream sink:url-redirect sink:xpath sink:xslt sink:xss source:contentprovider source:remote summary:taint summary:value
27 jakarta.ws.rs.core 2 149 2 94 55
28 java.beans 1 1
29 java.io 3 31 3 30 1
30 java.lang 51 52 41 42 10
31 java.net 10 3 7 10 3 7
32 java.nio 10 4 10 4
33 java.sql 7 7

View File

@@ -15,9 +15,9 @@ Java framework & library support
`Apache HttpComponents <https://hc.apache.org/>`_,"``org.apache.hc.core5.*``, ``org.apache.http``",5,136,28,,,3,,,,25
`Google Guava <https://guava.dev/>`_,``com.google.common.*``,,728,6,,6,,,,,
`JSON-java <https://github.com/stleary/JSON-java>`_,``org.json``,,236,,,,,,,,
Java Standard Library,``java.*``,3,523,30,13,,,7,,,10
Java Standard Library,``java.*``,3,524,30,13,,,7,,,10
Java extensions,"``javax.*``, ``jakarta.*``",54,552,32,,,4,,1,1,2
`Spring <https://spring.io/>`_,``org.springframework.*``,29,469,91,,,,19,14,,29
Others,"``cn.hutool.core.codec``, ``com.esotericsoftware.kryo.io``, ``com.esotericsoftware.kryo5.io``, ``com.fasterxml.jackson.core``, ``com.fasterxml.jackson.databind``, ``com.opensymphony.xwork2.ognl``, ``com.unboundid.ldap.sdk``, ``flexjson``, ``groovy.lang``, ``groovy.util``, ``jodd.json``, ``net.sf.saxon.s9api``, ``ognl``, ``org.apache.commons.codec``, ``org.apache.commons.jexl2``, ``org.apache.commons.jexl3``, ``org.apache.commons.ognl``, ``org.apache.directory.ldap.client.api``, ``org.apache.ibatis.jdbc``, ``org.apache.shiro.codec``, ``org.apache.shiro.jndi``, ``org.codehaus.groovy.control``, ``org.dom4j``, ``org.hibernate``, ``org.jooq``, ``org.mvel2``, ``org.xml.sax``, ``org.xmlpull.v1``, ``play.mvc``, ``ratpack.core.form``, ``ratpack.core.handling``, ``ratpack.core.http``, ``ratpack.exec``, ``ratpack.form``, ``ratpack.func``, ``ratpack.handling``, ``ratpack.http``, ``ratpack.util``",39,99,151,,,,14,18,,
Totals,,175,5368,431,13,6,10,107,33,1,66
Totals,,175,5369,431,13,6,10,107,33,1,66

View File

@@ -285,7 +285,20 @@ private predicate overrides(Method m1, Method m2) {
or
m2.isProtected()
or
m2.isPackageProtected() and t1.getPackage() = t2.getPackage()
m2.isPackageProtected() and
pragma[only_bind_out](t1.getPackage()) = pragma[only_bind_out](t2.getPackage())
)
}
pragma[nomagic]
private predicate overridesCandidateType(RefType tsup, string sig, RefType t, Method m) {
virtualMethodWithSignature(sig, t, m) and
t.extendsOrImplements(tsup)
or
exists(RefType mid |
overridesCandidateType(mid, sig, t, m) and
mid.extendsOrImplements(tsup) and
not virtualMethodWithSignature(sig, mid, _)
)
}
@@ -294,11 +307,10 @@ private predicate overrides(Method m1, Method m2) {
* ignoring any access modifiers. Additionally, this predicate binds
* `t1` to the type declaring `m1` and `t2` to the type declaring `m2`.
*/
pragma[noopt]
cached
predicate overridesIgnoringAccess(Method m1, RefType t1, Method m2, RefType t2) {
exists(string sig |
virtualMethodWithSignature(sig, t1, m1) and
t1.extendsOrImplements+(t2) and
overridesCandidateType(t2, sig, t1, m1) and
virtualMethodWithSignature(sig, t2, m2)
)
}

View File

@@ -31,7 +31,7 @@ predicate hasSubtype(RefType t, Type sub) {
arraySubtype(t, sub) and t != sub
or
// Type parameter containment for parameterized types.
parContainmentSubtype(t, sub) and t != sub
parContainmentSubtype(t, sub)
or
// Type variables are subtypes of their upper bounds.
typeVarSubtypeBound(t, sub) and t != sub
@@ -59,19 +59,23 @@ private predicate arraySubtype(Array sup, Array sub) {
* )
* ```
* For performance several transformations are made. First, the `forex` is
* written as a loop where `typeArgumentsContain(_, pt, psub, n)` encode that
* the `forex` holds for `i in [0..n]`. Second, the relation is split into two
* cases depending on whether `pt.getNumberOfTypeArguments()` is 1 or 2+, as
* this allows us to unroll the loop and collapse the first two iterations. The
* base case for `typeArgumentsContain` is therefore `n=1` and this allows an
* improved join order implemented by `contains01`.
* written as a loop where `typePrefixContains(ppt, ppsub)` encode that
* `ppt` and `ppsub` are prefixes of `pt` and `ptsub` and that
* the `forex` holds for `i in [0..n-1]` where `n` is the length of the prefixes.
* Second, the recursive case that determines containment of length `n+1`
* prefixes is split into three cases depending on whether there is
* non-reflexive type parameter containment:
* - only in the length `n` prefix,
* - only in the `n`th position,
* - both in the length `n` prefix and the `n`th position.
*/
private predicate parContainmentSubtype(ParameterizedType pt, ParameterizedType psub) {
pt != psub and
typeArgumentsContain(_, pt, psub, pt.getNumberOfTypeArguments() - 1)
or
typeArgumentsContain0(_, pt, psub)
exists(ParameterizedPrefix ppt, ParameterizedPrefix ppsub |
typePrefixContains(ppt, ppsub) and
ppt.equals(pt) and
ppsub.equals(psub)
)
}
/**
@@ -94,100 +98,116 @@ private RefType parameterisationTypeArgumentVarianceCand(
varianceCandidate(t)
}
/**
* Holds if every type argument of `s` (up to `n` with `n >= 1`) contains the
* corresponding type argument of `t`. Both `s` and `t` are constrained to
* being parameterizations of `g`.
*/
pragma[nomagic]
private predicate typeArgumentsContain(
GenericType g, ParameterizedType s, ParameterizedType t, int n
) {
contains01(g, s, t) and n = 1
private newtype TParameterizedPrefix =
TGenericType(GenericType g) or
TTypeParam(ParameterizedPrefix pp, RefType t) { prefixMatches(pp, t, _, _) }
/** Holds if `pp` is a length `n` prefix of `pt`. */
private predicate prefixMatches(ParameterizedPrefix pp, ParameterizedType pt, int n) {
pp = TGenericType(pt.getGenericType()) and n = 0
or
contains(g, s, t, n) and
typeArgumentsContain(g, s, t, n - 1)
}
private predicate typeArgumentsContain0(
GenericType g, ParameterizedType sParm, ParameterizedType tParm
) {
exists(RefType s, RefType t |
containsAux0(g, tParm, s, t) and
s = parameterisationTypeArgument(g, sParm, 0) and
s != t
exists(ParameterizedPrefix pp0, RefType t |
pp = TTypeParam(pp0, t) and prefixMatches(pp0, t, pt, n - 1)
)
}
/**
* Holds if the `n`-th type argument of `sParm` contain the `n`-th type
* argument of `tParm` for both `n = 0` and `n = 1`, where both `sParm` and
* `tParm` are parameterizations of the same generic type `g`.
*
* This is equivalent to
* ```
* contains(g, sParm, tParm, 0) and
* contains(g, sParm, tParm, 1)
* ```
* except `contains` is restricted to only include `n >= 2`.
* Holds if `pp` is a length `n` prefix of `pt` and `t` is the `n`th type
* argument of `pt`.
*/
private predicate contains01(GenericType g, ParameterizedType sParm, ParameterizedType tParm) {
exists(RefType s0, RefType t0, RefType s1, RefType t1 |
contains01Aux0(g, tParm, s0, t0, t1) and
contains01Aux1(g, sParm, s0, s1, t1)
)
}
pragma[nomagic]
private predicate contains01Aux0(
GenericType g, ParameterizedType tParm, RefType s0, RefType t0, RefType t1
) {
typeArgumentContains(g, s0, t0, 0) and
t0 = parameterisationTypeArgument(g, tParm, 0) and
t1 = parameterisationTypeArgument(g, tParm, 1)
}
pragma[nomagic]
private predicate contains01Aux1(
GenericType g, ParameterizedType sParm, RefType s0, RefType s1, RefType t1
) {
typeArgumentContains(g, s1, t1, 1) and
s0 = parameterisationTypeArgumentVarianceCand(g, sParm, 0) and
s1 = parameterisationTypeArgumentVarianceCand(g, sParm, 1)
}
pragma[nomagic]
private predicate containsAux0(GenericType g, ParameterizedType tParm, RefType s, RefType t) {
typeArgumentContains(g, s, t, 0) and
t = parameterisationTypeArgument(g, tParm, 0) and
g.getNumberOfTypeParameters() = 1
private predicate prefixMatches(ParameterizedPrefix pp, RefType t, ParameterizedType pt, int n) {
prefixMatches(pp, pt, n) and
t = pt.getTypeArgument(n)
}
/**
* Holds if the `n`-th type argument of `sParm` contain the `n`-th type
* argument of `tParm`, where both `sParm` and `tParm` are parameterizations of
* the same generic type `g`. The index `n` is restricted to `n >= 2`, the
* cases `n < 2` are handled by `contains01`.
*
* See JLS 4.5.1, Type Arguments of Parameterized Types.
* A prefix of a `ParameterizedType`. This encodes the corresponding
* `GenericType` and the first `n` type arguments where `n` is the prefix
* length.
*/
private predicate contains(GenericType g, ParameterizedType sParm, ParameterizedType tParm, int n) {
exists(RefType s, RefType t |
containsAux(g, tParm, n, s, t) and
s = parameterisationTypeArgumentVarianceCand(g, sParm, n)
private class ParameterizedPrefix extends TParameterizedPrefix {
string toString() { result = "ParameterizedPrefix" }
predicate equals(ParameterizedType pt) { prefixMatches(this, pt, pt.getNumberOfTypeArguments()) }
/** Holds if this prefix has length `n`, applies to `g`, and equals `TTypeParam(pp, t)`. */
predicate split(GenericType g, ParameterizedPrefix pp, RefType t, int n) {
this = TTypeParam(pp, t) and
(
pp = TGenericType(g) and n = 0
or
pp.split(g, _, _, n - 1)
)
}
}
/**
* Holds if every type argument of `pps` contains the corresponding type
* argument of `ppt`. Both `pps` and `ppt` are constrained to be equal-length
* prefixes of parameterizations of the same `GenericType`.
*/
pragma[nomagic]
private predicate typePrefixContains(ParameterizedPrefix pps, ParameterizedPrefix ppt) {
// Let `pps = TTypeParam(pps0, s)` and `ppt = TTypeParam(ppt0, t)`.
// Case 1: pps0 = ppt0 and typeArgumentContains(_, s, t, _)
typePrefixContains_base(pps, ppt)
or
// Case 2: typePrefixContains(pps0, ppt0) and s = t
typePrefixContains_ext_eq(pps, ppt)
or
// Case 3: typePrefixContains(pps0, ppt0) and typeArgumentContains(_, s, t, _)
typePrefixContains_ext_neq(pps, ppt)
}
private predicate typePrefixContains_base(ParameterizedPrefix pps, ParameterizedPrefix ppt) {
exists(ParameterizedPrefix pp, RefType s |
pps = TTypeParam(pp, s) and
typePrefixContainsAux2(ppt, pp, s)
)
}
private predicate typePrefixContains_ext_eq(ParameterizedPrefix pps, ParameterizedPrefix ppt) {
exists(ParameterizedPrefix pps0, ParameterizedPrefix ppt0, RefType t |
typePrefixContains(pragma[only_bind_into](pps0), pragma[only_bind_into](ppt0)) and
pps = TTypeParam(pragma[only_bind_into](pps0), t) and
ppt = TTypeParam(ppt0, t)
)
}
private predicate typePrefixContains_ext_neq(ParameterizedPrefix pps, ParameterizedPrefix ppt) {
exists(ParameterizedPrefix ppt0, RefType s |
typePrefixContainsAux1(pps, ppt0, s) and
typePrefixContainsAux2(ppt, ppt0, s)
)
}
pragma[nomagic]
private predicate containsAux(GenericType g, ParameterizedType tParm, int n, RefType s, RefType t) {
typeArgumentContains(g, s, t, n) and
t = parameterisationTypeArgument(g, tParm, n) and
n >= 2
private predicate typePrefixContainsAux1(
ParameterizedPrefix pps, ParameterizedPrefix ppt0, RefType s
) {
exists(ParameterizedPrefix pps0 |
typePrefixContains(pps0, ppt0) and
pps = TTypeParam(pps0, s) and
s instanceof Wildcard // manual magic, implied by `typeArgumentContains(_, s, t, _)`
)
}
pragma[nomagic]
private predicate typePrefixContainsAux2(
ParameterizedPrefix ppt, ParameterizedPrefix ppt0, RefType s
) {
exists(GenericType g, int n, RefType t |
// Implies `ppt = TTypeParam(ppt0, t)`
ppt.split(g, ppt0, t, n) and
typeArgumentContains(g, s, t, n)
)
}
/**
* Holds if the type argument `s` contains the type argument `t`, where both
* type arguments occur as index `n` in an instantiation of `g`.
*
* The case `s = t` is not included.
*/
pragma[noinline]
private predicate typeArgumentContains(GenericType g, RefType s, RefType t, int n) {
@@ -205,18 +225,18 @@ private predicate typeArgumentContainsAux2(GenericType g, RefType s, RefType t,
* Holds if the type argument `s` contains the type argument `t`, where both
* type arguments occur as index `n` in some parameterized types.
*
* The case `s = t` is not included.
*
* See JLS 4.5.1, Type Arguments of Parameterized Types.
*/
private predicate typeArgumentContainsAux1(RefType s, RefType t, int n) {
exists(int i |
s = parameterisationTypeArgumentVarianceCand(_, _, i) and
t = parameterisationTypeArgument(_, _, n) and
i <= n and
n <= i
|
s = parameterisationTypeArgumentVarianceCand(_, _, pragma[only_bind_into](n)) and
t = parameterisationTypeArgument(_, _, pragma[only_bind_into](n)) and
s != t and
(
exists(RefType tUpperBound | tUpperBound = t.(Wildcard).getUpperBound().getType() |
// ? extends T <= ? extends S if T <: S
hasSubtypeStar(s.(Wildcard).getUpperBound().getType(), tUpperBound)
hasSubtypeStar1(s.(Wildcard).getUpperBound().getType(), tUpperBound)
or
// ? extends T <= ?
s.(Wildcard).isUnconstrained()
@@ -224,7 +244,7 @@ private predicate typeArgumentContainsAux1(RefType s, RefType t, int n) {
or
exists(RefType tLowerBound | tLowerBound = t.(Wildcard).getLowerBound().getType() |
// ? super T <= ? super S if s <: T
hasSubtypeStar(tLowerBound, s.(Wildcard).getLowerBound().getType())
hasSubtypeStar2(tLowerBound, s.(Wildcard).getLowerBound().getType())
or
// ? super T <= ?
s.(Wildcard).isUnconstrained()
@@ -233,14 +253,14 @@ private predicate typeArgumentContainsAux1(RefType s, RefType t, int n) {
wildcardExtendsObject(s)
)
or
// T <= T
s = t
or
// T <= ? extends T
hasSubtypeStar(s.(Wildcard).getUpperBound().getType(), t)
hasSubtypeStar1(s.(Wildcard).getUpperBound().getType(), t)
or
// T <= ? super T
hasSubtypeStar(t, s.(Wildcard).getLowerBound().getType())
hasSubtypeStar2(t, s.(Wildcard).getLowerBound().getType())
// or
// T <= T
// but this case is handled directly in `typePrefixContains`
)
}
@@ -249,12 +269,38 @@ private predicate wildcardExtendsObject(Wildcard wc) {
wc.getUpperBound().getType() instanceof TypeObject
}
private predicate hasSubtypeStar(RefType t, RefType sub) {
sub = t
// manual magic for `hasSubtypeStar1`
private predicate getAWildcardUpperBound(RefType t) {
t = any(Wildcard w).getUpperBound().getType()
}
// manual magic for `hasSubtypeStar2`
private predicate getAWildcardLowerBound(RefType t) {
t = any(Wildcard w).getLowerBound().getType()
}
/**
* Holds if `hasSubtype*(t, sub)`, but manual-magic'ed with `getAWildcardUpperBound(t)`.
*/
pragma[nomagic]
private predicate hasSubtypeStar1(RefType t, RefType sub) {
sub = t and getAWildcardUpperBound(t)
or
hasSubtype(t, sub)
hasSubtype(t, sub) and getAWildcardUpperBound(t)
or
exists(RefType mid | hasSubtypeStar(t, mid) and hasSubtype(mid, sub))
exists(RefType mid | hasSubtypeStar1(t, mid) and hasSubtype(mid, sub))
}
/**
* Holds if `hasSubtype*(t, sub)`, but manual-magic'ed with `getAWildcardLowerBound(sub)`.
*/
pragma[nomagic]
private predicate hasSubtypeStar2(RefType t, RefType sub) {
sub = t and getAWildcardLowerBound(sub)
or
hasSubtype(t, sub) and getAWildcardLowerBound(sub)
or
exists(RefType mid | hasSubtype(t, mid) and hasSubtypeStar2(mid, sub))
}
/** Holds if type `t` declares member `m`. */

View File

@@ -85,6 +85,9 @@ module Public {
/** Holds if this stack contains summary component `c`. */
predicate contains(SummaryComponent c) { c = this.drop(_).head() }
/** Gets the bottom element of this stack. */
SummaryComponent bottom() { result = this.drop(this.length() - 1).head() }
/** Gets a textual representation of this stack. */
string toString() {
exists(SummaryComponent head, SummaryComponentStack tail |
@@ -197,6 +200,8 @@ module Private {
or
tail.(RequiredSummaryComponentStack).required(TParameterSummaryComponent(_)) and
head = thisParam()
or
derivedFluentFlowPush(_, _, _, head, tail, _)
}
pragma[nomagic]
@@ -210,7 +215,7 @@ module Private {
c.propagatesFlow(output, input, preservesValue) and
preservesValue = true and
isCallbackParameter(input) and
isContentOfArgument(output)
isContentOfArgument(output, _)
or
// flow from the receiver of a callback into the instance-parameter
exists(SummaryComponentStack s, SummaryComponentStack callbackRef |
@@ -222,16 +227,81 @@ module Private {
output = TConsSummaryComponentStack(thisParam(), input) and
preservesValue = true
)
or
exists(SummaryComponentStack arg, SummaryComponentStack return |
derivedFluentFlow(c, input, arg, return, preservesValue)
|
arg.length() = 1 and
output = return
or
exists(SummaryComponent head, SummaryComponentStack tail |
derivedFluentFlowPush(c, input, arg, head, tail, 0) and
output = SummaryComponentStack::push(head, tail)
)
)
or
// Chain together summaries where values get passed into callbacks along the way
exists(SummaryComponentStack mid, boolean preservesValue1, boolean preservesValue2 |
c.propagatesFlow(input, mid, preservesValue1) and
c.propagatesFlow(mid, output, preservesValue2) and
mid.drop(mid.length() - 2) =
SummaryComponentStack::push(TParameterSummaryComponent(_),
SummaryComponentStack::singleton(TArgumentSummaryComponent(_))) and
preservesValue = preservesValue1.booleanAnd(preservesValue2)
)
}
/**
* Holds if `c` has a flow summary from `input` to `arg`, where `arg`
* writes to (contents of) the `i`th argument, and `c` has a
* value-preserving flow summary from the `i`th argument to a return value
* (`return`).
*
* In such a case, we derive flow from `input` to (contents of) the return
* value.
*
* As an example, this simplifies modeling of fluent methods:
* for `StringBuilder.append(x)` with a specified value flow from qualifier to
* return value and taint flow from argument 0 to the qualifier, then this
* allows us to infer taint flow from argument 0 to the return value.
*/
pragma[nomagic]
private predicate derivedFluentFlow(
SummarizedCallable c, SummaryComponentStack input, SummaryComponentStack arg,
SummaryComponentStack return, boolean preservesValue
) {
exists(int i |
summary(c, input, arg, preservesValue) and
isContentOfArgument(arg, i) and
summary(c, SummaryComponentStack::singleton(TArgumentSummaryComponent(i)), return, true) and
return.bottom() = TReturnSummaryComponent(_)
)
}
pragma[nomagic]
private predicate derivedFluentFlowPush(
SummarizedCallable c, SummaryComponentStack input, SummaryComponentStack arg,
SummaryComponent head, SummaryComponentStack tail, int i
) {
derivedFluentFlow(c, input, arg, tail, _) and
head = arg.drop(i).head() and
i = arg.length() - 2
or
exists(SummaryComponent head0, SummaryComponentStack tail0 |
derivedFluentFlowPush(c, input, arg, head0, tail0, i + 1) and
head = arg.drop(i).head() and
tail = SummaryComponentStack::push(head0, tail0)
)
}
private predicate isCallbackParameter(SummaryComponentStack s) {
s.head() = TParameterSummaryComponent(_) and exists(s.tail())
}
private predicate isContentOfArgument(SummaryComponentStack s) {
s.head() = TContentSummaryComponent(_) and isContentOfArgument(s.tail())
private predicate isContentOfArgument(SummaryComponentStack s, int i) {
s.head() = TContentSummaryComponent(_) and isContentOfArgument(s.tail(), i)
or
s = TSingletonSummaryComponentStack(TArgumentSummaryComponent(_))
s = TSingletonSummaryComponentStack(TArgumentSummaryComponent(i))
}
private predicate outputState(SummarizedCallable c, SummaryComponentStack s) {
@@ -508,9 +578,14 @@ module Private {
* node, and back out to `p`.
*/
predicate summaryAllowParameterReturnInSelf(ParamNode p) {
exists(SummarizedCallable c, int i |
c.clearsContent(i, _) and
p.isParameterOf(c, i)
exists(SummarizedCallable c, int i | p.isParameterOf(c, i) |
c.clearsContent(i, _)
or
exists(SummaryComponentStack inputContents, SummaryComponentStack outputContents |
summary(c, inputContents, outputContents, _) and
inputContents.bottom() = pragma[only_bind_into](TArgumentSummaryComponent(i)) and
outputContents.bottom() = pragma[only_bind_into](TArgumentSummaryComponent(i))
)
)
}
@@ -534,22 +609,6 @@ module Private {
preservesValue = false and not summary(c, inputContents, outputContents, true)
)
or
// If flow through a method updates a parameter from some input A, and that
// parameter also is returned through B, then we'd like a combined flow from A
// to B as well. As an example, this simplifies modeling of fluent methods:
// for `StringBuilder.append(x)` with a specified value flow from qualifier to
// return value and taint flow from argument 0 to the qualifier, then this
// allows us to infer taint flow from argument 0 to the return value.
succ instanceof ParamNode and
summaryPostUpdateNode(pred, succ) and
preservesValue = true
or
// Similarly we would like to chain together summaries where values get passed
// into callbacks along the way.
pred instanceof ArgNode and
summaryPostUpdateNode(succ, pred) and
preservesValue = true
or
exists(SummarizedCallable c, int i |
pred.(ParamNode).isParameterOf(c, i) and
succ = summaryNode(c, TSummaryNodeClearsContentState(i, _)) and

View File

@@ -262,7 +262,7 @@ class ManagedBeanImplEntryPoint extends EntryPoint, RegisteredManagedBeanImpl {
// Find the method that will be called for each method on each managed bean that this class
// implements.
this.inherits(result) and
result.overrides(this.getAnImplementedManagedBean().getAMethod())
result.overrides+(this.getAnImplementedManagedBean().getAMethod())
}
}

View File

@@ -19,7 +19,7 @@ class Struts1ActionEntryPoint extends EntryPoint, Class {
exists(Method methodFromAction |
methodFromAction.getDeclaringType().hasQualifiedName("org.apache.struts.action", "Action")
|
result.(Method).overrides(methodFromAction)
result.(Method).overrides+(methodFromAction)
)
or
this.getASupertype*().hasQualifiedName("org.apache.struts.actions", "DispatchAction") and

View File

@@ -54,7 +54,8 @@ private class StringSummaryCsv extends SummaryModelCsv {
"java.lang;StringBuffer;true;StringBuffer;(CharSequence);;Argument[0];Argument[-1];taint",
"java.lang;StringBuffer;true;StringBuffer;(String);;Argument[0];Argument[-1];taint",
"java.lang;StringBuilder;true;StringBuilder;;;Argument[0];Argument[-1];taint",
"java.lang;CharSequence;true;subSequence;;;Argument[-1];ReturnValue;taint"
"java.lang;CharSequence;true;subSequence;;;Argument[-1];ReturnValue;taint",
"java.lang;CharSequence;true;toString;;;Argument[-1];ReturnValue;taint"
]
}
}

View File

@@ -27,7 +27,7 @@ class ThriftIface extends Interface {
Method getAnImplementingMethod() {
result.getDeclaringType().(Class).getASupertype+() = this and
result.overrides(this.getAMethod()) and
result.overrides+(this.getAMethod()) and
not result.getFile() = this.getFile()
}
}

View File

@@ -572,7 +572,7 @@ class RemoteInterface extends Interface {
* abstract methods or overriding within an interface hierarchy.
*/
Method getARemoteMethodImplementationChecked() {
result.overrides(this.getARemoteMethod()) and
result.overrides+(this.getARemoteMethod()) and
exists(result.getBody())
}

View File

@@ -54,7 +54,6 @@ where
sup.isSynchronized() and
not sub.isSynchronized() and
not delegatingOverride(sub, sup) and
not exists(Method mid | sub.overrides(mid) and mid.overrides(sup)) and
supSrc = sup.getDeclaringType().getSourceDeclaration()
select sub,
"Method '" + sub.getName() + "' overrides a synchronized method in $@ but is not synchronized.",

View File

@@ -45,7 +45,7 @@ where
// which is an access to the object being initialized, ...
ma = unqualifiedCallToNonAbstractMethod(c, m) and
// ... there exists an overriding method in a subtype,
n.overrides(m) and
n.overrides+(m) and
n.getDeclaringType().getASupertype+() = c.getDeclaringType() and
// ... the method is in a supertype of c,
m.getDeclaringType() = c.getDeclaringType().getASupertype*() and

View File

@@ -0,0 +1,57 @@
/**
* @name Capture sink models.
* @description Finds public methods that act as sinks as they flow into a a known sink.
* @id java/utils/model-generator/sink-models
*/
import java
private import Telemetry.ExternalAPI
private import semmle.code.java.dataflow.DataFlow
private import semmle.code.java.dataflow.TaintTracking
private import semmle.code.java.dataflow.ExternalFlow
private import ModelGeneratorUtils
private import semmle.code.java.dataflow.internal.DataFlowNodes::Private
class PropagateToSinkConfiguration extends TaintTracking::Configuration {
PropagateToSinkConfiguration() { this = "parameters or flowing into sinks" }
override predicate isSource(DataFlow::Node source) {
(source.asExpr().(FieldAccess).isOwnFieldAccess() or source instanceof DataFlow::ParameterNode) and
source.getEnclosingCallable().isPublic() and
exists(RefType t |
t = source.getEnclosingCallable().getDeclaringType().getAnAncestor() and
not t instanceof TypeObject and
t.isPublic()
) and
isRelevantForModels(source.getEnclosingCallable())
}
override predicate isSink(DataFlow::Node sink) { sinkNode(sink, _) }
override DataFlow::FlowFeature getAFeature() {
result instanceof DataFlow::FeatureHasSourceCallContext
}
}
string asInputArgument(DataFlow::Node source) {
exists(int pos |
source.(DataFlow::ParameterNode).isParameterOf(_, pos) and
result = "Argument[" + pos + "]"
)
or
source.asExpr() instanceof FieldAccess and
result = "Argument[-1]"
}
string captureSink(TargetAPI api) {
exists(DataFlow::Node src, DataFlow::Node sink, PropagateToSinkConfiguration config, string kind |
config.hasFlow(src, sink) and
sinkNode(sink, kind) and
api = src.getEnclosingCallable() and
result = asSinkModel(api, asInputArgument(src), kind)
)
}
from TargetAPI api, string sink
where sink = captureSink(api)
select sink order by sink

View File

@@ -0,0 +1,53 @@
/**
* @name Capture source models.
* @description Finds APIs that act as sources as they expose already known sources.
* @id java/utils/model-generator/sink-models
*/
import java
private import Telemetry.ExternalAPI
private import semmle.code.java.dataflow.DataFlow
private import semmle.code.java.dataflow.TaintTracking
private import semmle.code.java.dataflow.ExternalFlow
private import ModelGeneratorUtils
private import semmle.code.java.dataflow.internal.FlowSummaryImplSpecific
private import semmle.code.java.dataflow.internal.FlowSummaryImpl
private import semmle.code.java.dataflow.internal.DataFlowImplCommon
private import semmle.code.java.dataflow.internal.DataFlowPrivate
private import semmle.code.java.dataflow.internal.DataFlowNodes::Private
class FromSourceConfiguration extends TaintTracking::Configuration {
FromSourceConfiguration() { this = "FromSourceConfiguration" }
override predicate isSource(DataFlow::Node source) { sourceNode(source, _) }
override predicate isSink(DataFlow::Node sink) {
exists(TargetAPI c |
sink instanceof ReturnNodeExt and
sink.getEnclosingCallable() = c and
c.isPublic() and
c.fromSource()
)
}
override DataFlow::FlowFeature getAFeature() {
result instanceof DataFlow::FeatureHasSinkCallContext
}
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
isRelevantTaintStep(node1, node2)
}
}
string captureSource(TargetAPI api) {
exists(DataFlow::Node source, DataFlow::Node sink, FromSourceConfiguration config, string kind |
config.hasFlow(source, sink) and
sourceNode(source, kind) and
api = source.getEnclosingCallable() and
result = asSourceModel(api, returnNodeAsOutput(api, sink), kind)
)
}
from TargetAPI api, string sink
where sink = captureSource(api)
select sink order by sink

View File

@@ -0,0 +1,232 @@
/**
* @name Capture summary models.
* @description Finds applicable summary models to be used by other queries.
* @id java/utils/model-generator/summary-models
*/
import java
import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.dataflow.internal.DataFlowImplCommon
import semmle.code.java.dataflow.internal.DataFlowNodes
import semmle.code.java.dataflow.internal.DataFlowPrivate
import semmle.code.java.dataflow.InstanceAccess
import ModelGeneratorUtils
string captureFlow(TargetAPI api) {
result = captureQualifierFlow(api) or
result = captureParameterFlowToReturnValue(api) or
result = captureFieldFlowIn(api) or
result = captureParameterToParameterFlow(api) or
result = captureFieldFlow(api)
}
/**
* Capture fluent APIs that return `this`.
* Example of a fluent API:
* ```
* public class Foo {
* public Foo someAPI() {
* // some side-effect
* return this;
* }
* }
* ```
*/
string captureQualifierFlow(TargetAPI api) {
exists(ReturnStmt rtn |
rtn.getEnclosingCallable() = api and
rtn.getResult().(ThisAccess).isOwnInstanceAccess()
) and
result = asValueModel(api, "Argument[-1]", "ReturnValue")
}
class FieldToReturnConfig extends TaintTracking::Configuration {
FieldToReturnConfig() { this = "FieldToReturnConfig" }
override predicate isSource(DataFlow::Node source) {
source instanceof DataFlow::InstanceParameterNode
}
override predicate isSink(DataFlow::Node sink) {
sink instanceof ReturnNodeExt and
not sink.(ReturnNode).asExpr().(ThisAccess).isOwnInstanceAccess() and
not exists(captureQualifierFlow(sink.asExpr().getEnclosingCallable()))
}
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
isRelevantTaintStep(node1, node2)
}
override DataFlow::FlowFeature getAFeature() {
result instanceof DataFlow::FeatureEqualSourceSinkCallContext
}
}
/**
* Capture APIs that return tainted instance data.
* Example of an API that returns tainted instance data:
* ```
* public class Foo {
* private String tainted;
*
* public String returnsTainted() {
* return tainted;
* }
*
* public void putsTaintIntoParameter(List<String> foo) {
* foo.add(tainted);
* }
* }
* ```
* Captured Model:
* ```
* p;Foo;true;returnsTainted;;Argument[-1];ReturnValue;taint
* p;Foo;true;putsTaintIntoParameter;(List);Argument[-1];Argument[0];taint
* ```
*/
string captureFieldFlow(TargetAPI api) {
exists(FieldToReturnConfig config, ReturnNodeExt returnNodeExt |
config.hasFlow(_, returnNodeExt) and
returnNodeExt.getEnclosingCallable() = api and
not api.getDeclaringType() instanceof EnumType and
isRelevantType(returnNodeExt.getType())
|
result = asTaintModel(api, "Argument[-1]", returnNodeAsOutput(api, returnNodeExt))
)
}
class ParameterToFieldConfig extends TaintTracking::Configuration {
ParameterToFieldConfig() { this = "ParameterToFieldConfig" }
override predicate isSource(DataFlow::Node source) {
source instanceof DataFlow::ParameterNode and
isRelevantType(source.getType())
}
override predicate isSink(DataFlow::Node sink) {
thisAccess(sink.(DataFlow::PostUpdateNode).getPreUpdateNode())
}
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
store(node1, _, node2, _)
}
override DataFlow::FlowFeature getAFeature() {
result instanceof DataFlow::FeatureEqualSourceSinkCallContext
}
}
private predicate thisAccess(DataFlow::Node n) {
n.asExpr().(InstanceAccess).isOwnInstanceAccess()
or
n.(DataFlow::ImplicitInstanceAccess).getInstanceAccess() instanceof OwnInstanceAccess
}
/**
* Captures APIs that accept input and store them in a field.
* Example:
* ```
* public class Foo {
* private String tainted;
* public void doSomething(String input) {
* tainted = input;
* }
* ```
* Captured Model:
* `p;Foo;true;doSomething;(String);Argument[0];Argument[-1];taint`
*/
string captureFieldFlowIn(TargetAPI api) {
exists(DataFlow::Node source, ParameterToFieldConfig config |
config.hasFlow(source, _) and
source.asParameter().getCallable() = api
|
result =
asTaintModel(api, "Argument[" + source.asParameter().getPosition() + "]", "Argument[-1]")
)
}
class ParameterToReturnValueTaintConfig extends TaintTracking::Configuration {
ParameterToReturnValueTaintConfig() { this = "ParameterToReturnValueTaintConfig" }
override predicate isSource(DataFlow::Node source) {
exists(TargetAPI api |
api = source.asParameter().getCallable() and
isRelevantType(api.getReturnType()) and
isRelevantType(source.asParameter().getType())
)
}
override predicate isSink(DataFlow::Node sink) { sink instanceof ReturnNode }
// consider store steps to track taint across objects to model factory methods returning tainted objects
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
store(node1, _, node2, _)
}
override DataFlow::FlowFeature getAFeature() {
result instanceof DataFlow::FeatureEqualSourceSinkCallContext
}
}
predicate paramFlowToReturnValueExists(Parameter p) {
exists(ParameterToReturnValueTaintConfig config, ReturnStmt rtn |
config.hasFlow(DataFlow::parameterNode(p), DataFlow::exprNode(rtn.getResult()))
)
}
/**
* Capture APIs that return (parts of) data passed in as a parameter.
* Example:
* ```
* public class Foo {
*
* public String returnData(String tainted) {
* return tainted.substring(0,10)
* }
* }
* ```
* Captured Model:
* ```
* p;Foo;true;returnData;;Argument[0];ReturnValue;taint
* ```
*/
string captureParameterFlowToReturnValue(TargetAPI api) {
exists(Parameter p |
p = api.getAParameter() and
paramFlowToReturnValueExists(p)
|
result = asTaintModel(api, parameterAccess(p), "ReturnValue")
)
}
/**
* Capture APIs that pass tainted data from a parameter to a parameter.
* Example:
* ```
* public class Foo {
*
* public void addToList(String tainted, List<String> foo) {
* foo.add(tainted);
* }
* }
* ```
* Captured Model:
* ```
* p;Foo;true;addToList;;Argument[0];Argument[1];taint
* ```
*/
string captureParameterToParameterFlow(TargetAPI api) {
exists(DataFlow::ParameterNode source, DataFlow::PostUpdateNode sink |
source.getEnclosingCallable() = api and
sink.getPreUpdateNode().asExpr() = api.getAParameter().getAnAccess() and
TaintTracking::localTaint(source, sink)
|
result =
asTaintModel(api, parameterAccess(source.asParameter()),
parameterAccess(sink.getPreUpdateNode().asExpr().(VarAccess).getVariable().(Parameter)))
)
}
from TargetAPI api, string flow
where flow = captureFlow(api)
select flow order by flow

View File

@@ -0,0 +1,178 @@
#!/usr/bin/python3
import json
import os
import os.path
import shlex
import subprocess
import sys
import tempfile
def printHelp():
print("""Usage:
GenerateFlowModel.py <library-database> <outputQll> [--with-sinks] [--with-sources] [--with-summaries]
This generates summary, source and sink models for the code in the database.
The files will be placed in `java/ql/lib/semmle/code/java/frameworks/<outputQll>` where
outputQll is the name (and path) of the output QLL file. Usually, models are grouped by their
respective frameworks.
Which models are generated is controlled by the flags:
--with-sinks
--with-sources
--with-summaries
If none of these flags are specified, all models are generated.
Example invocations:
$ GenerateFlowModel.py /tmp/dbs/apache_commons-codec_45649c8 "apache/Codec.qll"
$ GenerateFlowModel.py /tmp/dbs/jdk15_db "javase/jdk_sinks.qll" --with-sinks
Requirements: `codeql` should both appear on your path.
""")
if any(s == "--help" for s in sys.argv):
printHelp()
sys.exit(0)
generateSinks = False
generateSources = False
generateSummaries = False
if "--with-sinks" in sys.argv:
sys.argv.remove("--with-sinks")
generateSinks = True
if "--with-sources" in sys.argv:
sys.argv.remove("--with-sources")
generateSources = True
if "--with-summaries" in sys.argv:
sys.argv.remove("--with-summaries")
generateSummaries = True
if not generateSinks and not generateSources and not generateSummaries:
generateSinks = generateSources = generateSummaries = True
if len(sys.argv) != 3:
printHelp()
sys.exit(1)
codeQlRoot = subprocess.check_output(
["git", "rev-parse", "--show-toplevel"]).decode("utf-8").strip()
targetQll = sys.argv[2]
if not targetQll.endswith(".qll"):
targetQll += ".qll"
filename = os.path.basename(targetQll)
shortname = filename[:-4]
generatedFrameworks = os.path.join(
codeQlRoot, "java/ql/lib/semmle/code/java/frameworks/")
frameworkTarget = os.path.join(generatedFrameworks, targetQll)
workDir = tempfile.mkdtemp()
os.makedirs(generatedFrameworks, exist_ok=True)
def runQuery(infoMessage, query):
print("########## Querying " + infoMessage + "...")
database = sys.argv[1]
queryFile = os.path.join(os.path.dirname(
__file__), query)
resultBqrs = os.path.join(workDir, "out.bqrs")
cmd = ['codeql', 'query', 'run', queryFile, '--database',
database, '--output', resultBqrs, '--threads', '8']
ret = subprocess.call(cmd)
if ret != 0:
print("Failed to generate " + infoMessage +
". Failed command was: " + shlex.join(cmd))
sys.exit(1)
return readRows(resultBqrs)
def readRows(bqrsFile):
generatedJson = os.path.join(workDir, "out.json")
cmd = ['codeql', 'bqrs', 'decode', bqrsFile,
'--format=json', '--output', generatedJson]
ret = subprocess.call(cmd)
if ret != 0:
print("Failed to decode BQRS. Failed command was: " + shlex.join(cmd))
sys.exit(1)
with open(generatedJson) as f:
results = json.load(f)
try:
results['#select']['tuples']
except KeyError:
print('Unexpected JSON output - no tuples found')
exit(1)
rows = ""
for (row) in results['#select']['tuples']:
rows += " \"" + row[0] + "\",\n"
return rows[:-2]
def asCsvModel(superclass, kind, rows):
classTemplate = """
private class {0}{1}Csv extends {2} {{
override predicate row(string row) {{
row =
[
{3}
]
}}
}}
"""
if rows.strip() == "":
return ""
return classTemplate.format(shortname[0].upper() + shortname[1:], kind.capitalize(), superclass, rows)
if generateSummaries:
summaryRows = runQuery("summary models", "CaptureSummaryModels.ql")
summaryCsv = asCsvModel("SummaryModelCsv", "summary", summaryRows)
else:
summaryCsv = ""
if generateSinks:
sinkRows = runQuery("sink models", "CaptureSinkModels.ql")
sinkCsv = asCsvModel("SinkModelCsv", "sinks", sinkRows)
else:
sinkCsv = ""
if generateSources:
sourceRows = runQuery("source models", "CaptureSourceModels.ql")
sourceCsv = asCsvModel("SourceModelCsv", "sources", sourceRows)
else:
sourceCsv = ""
qllTemplate = """
/** Definitions of taint steps in the {0} framework */
import java
private import semmle.code.java.dataflow.ExternalFlow
{1}
{2}
{3}
"""
qllContents = qllTemplate.format(shortname, sinkCsv, sourceCsv, summaryCsv)
with open(frameworkTarget, "w") as frameworkQll:
frameworkQll.write(qllContents)
cmd = ['codeql', 'query', 'format', '--in-place', frameworkTarget]
ret = subprocess.call(cmd)
if ret != 0:
print("Failed to format query. Failed command was: " + shlex.join(cmd))
sys.exit(1)
print("")
print("CSV model written to " + frameworkTarget)

View File

@@ -0,0 +1,173 @@
import java
private import semmle.code.java.dataflow.ExternalFlow
private import semmle.code.java.dataflow.internal.ContainerFlow
private import semmle.code.java.dataflow.internal.DataFlowImplCommon
private import semmle.code.java.dataflow.DataFlow
private import semmle.code.java.dataflow.internal.DataFlowPrivate
Method superImpl(Method m) {
result = m.getAnOverride() and
not exists(result.getAnOverride()) and
not m instanceof ToStringMethod
}
class TargetAPI extends Callable {
TargetAPI() {
this.isPublic() and
this.fromSource() and
(
this.getDeclaringType().isPublic() or
superImpl(this).getDeclaringType().isPublic()
) and
isRelevantForModels(this)
}
}
private string isExtensible(RefType ref) {
if ref.isFinal() then result = "false" else result = "true"
}
predicate isRelevantForModels(Callable api) {
not isInTestFile(api.getCompilationUnit().getFile()) and
not isJdkInternal(api.getCompilationUnit())
}
private predicate isInTestFile(File file) {
file.getAbsolutePath().matches("%src/test/%") or
file.getAbsolutePath().matches("%/guava-tests/%") or
file.getAbsolutePath().matches("%/guava-testlib/%")
}
private predicate isJdkInternal(CompilationUnit cu) {
cu.getPackage().getName().matches("org.graalvm%") or
cu.getPackage().getName().matches("com.sun%") or
cu.getPackage().getName().matches("javax.swing%") or
cu.getPackage().getName().matches("java.awt%") or
cu.getPackage().getName().matches("sun%") or
cu.getPackage().getName().matches("jdk.%") or
cu.getPackage().getName().matches("java2d.%") or
cu.getPackage().getName().matches("build.tools.%") or
cu.getPackage().getName().matches("propertiesparser.%") or
cu.getPackage().getName().matches("org.jcp.%") or
cu.getPackage().getName().matches("org.w3c.%") or
cu.getPackage().getName().matches("org.ietf.jgss.%") or
cu.getPackage().getName().matches("org.xml.sax%") or
cu.getPackage().getName() = "compileproperties" or
cu.getPackage().getName() = "netscape.javascript" or
cu.getPackage().getName() = ""
}
bindingset[input, output]
string asTaintModel(TargetAPI api, string input, string output) {
result = asSummaryModel(api, input, output, "taint")
}
bindingset[input, output]
string asValueModel(TargetAPI api, string input, string output) {
result = asSummaryModel(api, input, output, "value")
}
bindingset[input, output, kind]
string asSummaryModel(TargetAPI api, string input, string output, string kind) {
result =
asPartialModel(api) + input + ";" //
+ output + ";" //
+ kind
}
bindingset[input, kind]
string asSinkModel(TargetAPI api, string input, string kind) {
result = asPartialModel(api) + input + ";" + kind
}
bindingset[output, kind]
string asSourceModel(TargetAPI api, string output, string kind) {
result = asPartialModel(api) + output + ";" + kind
}
/**
* Computes the first 6 columns for CSV rows.
*/
private string asPartialModel(TargetAPI api) {
result =
typeAsSummaryModel(api) + ";" //
+ isExtensible(bestTypeForModel(api)) + ";" //
+ api.getName() + ";" //
+ paramsString(api) + ";" //
+ /* ext + */ ";" //
}
/**
* Returns the appropriate type name for the model. Either the type
* declaring the method or the supertype introducing the method.
*/
private string typeAsSummaryModel(TargetAPI api) { result = typeAsModel(bestTypeForModel(api)) }
private RefType bestTypeForModel(TargetAPI api) {
if exists(superImpl(api))
then superImpl(api).fromSource() and result = superImpl(api).getDeclaringType()
else result = api.getDeclaringType()
}
private string typeAsModel(RefType type) {
result = type.getCompilationUnit().getPackage().getName() + ";" + type.nestedName()
}
predicate isRelevantType(Type t) {
not t instanceof TypeClass and
not t instanceof EnumType and
not t instanceof PrimitiveType and
not t instanceof BoxedType and
not t.(RefType).getAnAncestor().hasQualifiedName("java.lang", "Number") and
not t.(RefType).getAnAncestor().hasQualifiedName("java.nio.charset", "Charset") and
(
not t.(Array).getElementType() instanceof PrimitiveType or
isPrimitiveTypeUsedForBulkData(t.(Array).getElementType())
) and
(
not t.(Array).getElementType() instanceof BoxedType or
isPrimitiveTypeUsedForBulkData(t.(Array).getElementType())
) and
(
not t.(CollectionType).getElementType() instanceof BoxedType or
isPrimitiveTypeUsedForBulkData(t.(CollectionType).getElementType())
)
}
predicate isRelevantTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
exists(DataFlow::Content f |
readStep(node1, f, node2) and
if f instanceof DataFlow::FieldContent
then isRelevantType(f.(DataFlow::FieldContent).getField().getType())
else any()
)
or
exists(DataFlow::Content f | storeStep(node1, f, node2) |
f instanceof DataFlow::ArrayContent or
f instanceof DataFlow::CollectionContent or
f instanceof DataFlow::MapKeyContent or
f instanceof DataFlow::MapValueContent
)
}
string returnNodeAsOutput(TargetAPI api, ReturnNodeExt node) {
if node.getKind() instanceof ValueReturnKind
then result = "ReturnValue"
else
result = parameterAccess(api.getParameter(node.getKind().(ParamUpdateReturnKind).getPosition()))
}
string parameterAccess(Parameter p) {
if
p.getType() instanceof Array and
not isPrimitiveTypeUsedForBulkData(p.getType().(Array).getElementType())
then result = "ArrayElement of Argument[" + p.getPosition() + "]"
else
if p.getType() instanceof ContainerType
then result = "Element of Argument[" + p.getPosition() + "]"
else result = "Argument[" + p.getPosition() + "]"
}
predicate isPrimitiveTypeUsedForBulkData(Type t) {
t.getName().regexpMatch("byte|char|Byte|Character")
}

View File

@@ -151,15 +151,7 @@ public class A {
forEach(new Object[] {source(16)}, x -> sink(x)); // $ flow=16
// Spurious flow from 17 is reasonable as it would likely
// also occur if the lambda body was inlined in a for loop.
// It occurs from the combination of being able to observe
// the side-effect of the callback on the other argument and
// being able to chain summaries that update/read arguments,
// e.g. fluent apis.
// Spurious flow from 18 is due to not matching call targets
// in a return-from-call-to-enter-call flow sequence.
forEach(new Object[2][], xs -> { sink(xs[0]); xs[0] = source(17); }); // $ SPURIOUS: flow=17 flow=18
forEach(new Object[2][], xs -> { sink(xs[0]); xs[0] = source(17); });
Object[][] xss = new Object[][] { { null } };
forEach(xss, x -> {x[0] = source(18);});

View File

@@ -9,5 +9,8 @@ public class CharSeq {
CharSequence seqFromSeq = seq.subSequence(0, 1);
sink(seqFromSeq);
String stringFromSeq = seq.toString();
sink(stringFromSeq);
}
}

View File

@@ -43,6 +43,7 @@
| B.java:15:21:15:27 | taint(...) | B.java:157:10:157:46 | toFile(...) |
| CharSeq.java:7:26:7:32 | taint(...) | CharSeq.java:8:12:8:14 | seq |
| CharSeq.java:7:26:7:32 | taint(...) | CharSeq.java:11:12:11:21 | seqFromSeq |
| CharSeq.java:7:26:7:32 | taint(...) | CharSeq.java:14:12:14:24 | stringFromSeq |
| MethodFlow.java:7:22:7:28 | taint(...) | MethodFlow.java:8:10:8:16 | tainted |
| MethodFlow.java:9:31:9:37 | taint(...) | MethodFlow.java:10:10:10:17 | tainted2 |
| MethodFlow.java:11:35:11:41 | taint(...) | MethodFlow.java:12:10:12:17 | tainted3 |

View File

@@ -436,7 +436,7 @@ public class Test {
sink(y); // $ hasValueFlow=reduce_3 hasValueFlow=reduce_4 hasValueFlow=reduce_5
return source("reduce_5");
});
sink(out); // $ hasValueFlow=reduce_4 hasValueFlow=reduce_5 SPURIOUS: hasValueFlow=reduce_3
sink(out); // $ hasValueFlow=reduce_4 hasValueFlow=reduce_5
}
{
// "java.util.stream;Stream;true;reduce;(Object,BiFunction,BinaryOperator);;Argument[0];ReturnValue;value"

View File

@@ -3,4 +3,3 @@
| ConstructedOverrides.java:17:7:17:9 | Sub | usedGeneric(U, String) | Super.class:0:0:0:0 | Super<String> | usedGeneric(U, String) |
| ConstructedOverrides.java:23:7:23:10 | Sub2 | unusedGeneric(V, String) | Super.class:0:0:0:0 | Super<String> | unusedGeneric(U, String) |
| ConstructedOverrides.java:23:7:23:10 | Sub2 | usedGeneric(V, String) | ConstructedOverrides.java:17:7:17:9 | Sub | usedGeneric(U, String) |
| ConstructedOverrides.java:23:7:23:10 | Sub2 | usedGeneric(V, String) | Super.class:0:0:0:0 | Super<String> | usedGeneric(U, String) |

View File

@@ -0,0 +1,4 @@
| p;PrivateFlowViaPublicInterface$SPI;true;openStream;();;Argument[-1];create-file |
| p;Sinks;true;copyFileToDirectory;(Path,Path,CopyOption[]);;Argument[1];create-file |
| p;Sinks;true;readUrl;(URL,Charset);;Argument[0];open-url |
| p;Sources;true;readUrl;(URL);;Argument[0];open-url |

View File

@@ -0,0 +1 @@
utils/model-generator/CaptureSinkModels.ql

View File

@@ -0,0 +1,4 @@
| p;Sources;true;readUrl;(URL);;ReturnValue;remote |
| p;Sources;true;socketStream;();;ReturnValue;remote |
| p;Sources;true;sourceToParameter;(InputStream[],List);;ArrayElement of Argument[0];remote |
| p;Sources;true;sourceToParameter;(InputStream[],List);;Element of Argument[1];remote |

View File

@@ -0,0 +1 @@
utils/model-generator/CaptureSourceModels.ql

View File

@@ -0,0 +1,47 @@
| p;Factory;false;create;(String);;Argument[0];ReturnValue;taint |
| p;Factory;false;create;(String,int);;Argument[0];ReturnValue;taint |
| p;Factory;false;getValue;();;Argument[-1];ReturnValue;taint |
| p;FinalClass;false;returnsInput;(String);;Argument[0];ReturnValue;taint |
| p;FluentAPI;false;returnsThis;(String);;Argument[-1];ReturnValue;value |
| p;ImmutablePojo;false;ImmutablePojo;(String,int);;Argument[0];Argument[-1];taint |
| p;ImmutablePojo;false;getValue;();;Argument[-1];ReturnValue;taint |
| p;ImmutablePojo;false;or;(String);;Argument[-1];ReturnValue;taint |
| p;ImmutablePojo;false;or;(String);;Argument[0];ReturnValue;taint |
| p;InnerClasses$CaptureMe;true;yesCm;(String);;Argument[0];ReturnValue;taint |
| p;InnerClasses;true;yes;(String);;Argument[0];ReturnValue;taint |
| p;InnerHolder;false;append;(String);;Argument[0];Argument[-1];taint |
| p;InnerHolder;false;explicitSetContext;(String);;Argument[0];Argument[-1];taint |
| p;InnerHolder;false;getValue;();;Argument[-1];ReturnValue;taint |
| p;InnerHolder;false;setContext;(String);;Argument[0];Argument[-1];taint |
| p;Joiner;false;Joiner;(CharSequence);;Argument[0];Argument[-1];taint |
| p;Joiner;false;Joiner;(CharSequence,CharSequence,CharSequence);;Argument[0];Argument[-1];taint |
| p;Joiner;false;Joiner;(CharSequence,CharSequence,CharSequence);;Argument[1];Argument[-1];taint |
| p;Joiner;false;Joiner;(CharSequence,CharSequence,CharSequence);;Argument[2];Argument[-1];taint |
| p;Joiner;false;add;(CharSequence);;Argument[-1];ReturnValue;value |
| p;Joiner;false;merge;(Joiner);;Argument[-1];ReturnValue;value |
| p;Joiner;false;setEmptyValue;(CharSequence);;Argument[-1];ReturnValue;value |
| p;Joiner;false;setEmptyValue;(CharSequence);;Argument[0];Argument[-1];taint |
| p;Joiner;false;setEmptyValue;(CharSequence);;Argument[0];ReturnValue;taint |
| p;Joiner;false;toString;();;Argument[-1];ReturnValue;taint |
| p;MultipleImpls$Strat2;true;getValue;();;Argument[-1];ReturnValue;taint |
| p;MultipleImpls$Strategy;true;doSomething;(String);;Argument[0];Argument[-1];taint |
| p;MultipleImpls$Strategy;true;doSomething;(String);;Argument[0];ReturnValue;taint |
| p;ParamFlow;true;addTo;(String,List);;Argument[0];Element of Argument[1];taint |
| p;ParamFlow;true;returnArrayElement;(String[]);;ArrayElement of Argument[0];ReturnValue;taint |
| p;ParamFlow;true;returnCollectionElement;(List);;Element of Argument[0];ReturnValue;taint |
| p;ParamFlow;true;returnIterableElement;(Iterable);;Element of Argument[0];ReturnValue;taint |
| p;ParamFlow;true;returnIteratorElement;(Iterator);;Element of Argument[0];ReturnValue;taint |
| p;ParamFlow;true;returnMultipleParameters;(String,String);;Argument[0];ReturnValue;taint |
| p;ParamFlow;true;returnMultipleParameters;(String,String);;Argument[1];ReturnValue;taint |
| p;ParamFlow;true;returnVarArgElement;(String[]);;ArrayElement of Argument[0];ReturnValue;taint |
| p;ParamFlow;true;returnsInput;(String);;Argument[0];ReturnValue;taint |
| p;ParamFlow;true;writeChunked;(byte[],OutputStream);;Argument[0];Argument[1];taint |
| p;ParamFlow;true;writeChunked;(char[],OutputStream);;Argument[0];Argument[1];taint |
| p;Pojo;false;fillIn;(List);;Argument[-1];Element of Argument[0];taint |
| p;Pojo;false;getBoxedBytes;();;Argument[-1];ReturnValue;taint |
| p;Pojo;false;getBoxedChars;();;Argument[-1];ReturnValue;taint |
| p;Pojo;false;getByteArray;();;Argument[-1];ReturnValue;taint |
| p;Pojo;false;getCharArray;();;Argument[-1];ReturnValue;taint |
| p;Pojo;false;getValue;();;Argument[-1];ReturnValue;taint |
| p;Pojo;false;setValue;(String);;Argument[0];Argument[-1];taint |
| p;PrivateFlowViaPublicInterface;true;createAnSPI;(File);;Argument[0];ReturnValue;taint |

View File

@@ -0,0 +1 @@
utils/model-generator/CaptureSummaryModels.ql

View File

@@ -0,0 +1,13 @@
package p;
import java.io.File;
import java.io.FileFilter;
public abstract class AbstractImplOfExternalSPI implements FileFilter {
@Override
public boolean accept(File pathname) {
return false;
}
}

View File

@@ -0,0 +1,30 @@
package p;
public final class Factory {
private String value;
private int intValue;
public static Factory create(String value, int foo) {
return new Factory(value, foo);
}
public static Factory create(String value) {
return new Factory(value, 0);
}
private Factory(String value, int intValue) {
this.value = value;
this.intValue = intValue;
}
public String getValue() {
return value;
}
public int getIntValue() {
return intValue;
}
}

View File

@@ -0,0 +1,15 @@
package p;
public final class FinalClass {
private static final String C = "constant";
public String returnsInput(String input) {
return input;
}
public String returnsConstant() {
return C;
}
}

View File

@@ -0,0 +1,15 @@
package p;
public final class FluentAPI {
public FluentAPI returnsThis(String input) {
return this;
}
public class Inner {
public FluentAPI notThis(String input) {
return FluentAPI.this;
}
}
}

View File

@@ -0,0 +1,26 @@
package p;
public final class ImmutablePojo {
private final String value;
private final long x;
public ImmutablePojo(String value, int x) {
this.value = value;
this.x = x;
}
public String getValue() {
return value;
}
public long getX() {
return x;
}
public String or(String defaultValue) {
return value != null ? value : defaultValue;
}
}

View File

@@ -0,0 +1,19 @@
package p;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
public class ImplOfExternalSPI extends AbstractImplOfExternalSPI {
@Override
public boolean accept(File pathname) {
try {
Files.createFile(pathname.toPath());
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
}

View File

@@ -0,0 +1,21 @@
package p;
public class InnerClasses {
class IgnoreMe {
public String no(String input) {
return input;
}
}
public class CaptureMe {
public String yesCm(String input) {
return input;
}
}
public String yes(String input) {
return input;
}
}

View File

@@ -0,0 +1,37 @@
package p;
public final class InnerHolder {
private class Context {
private String value;
Context(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
private Context context = null;
private StringBuilder sb = new StringBuilder();
public void setContext(String value) {
context = new Context(value);
}
public void explicitSetContext(String value) {
this.context = new Context(value);
}
public void append(String value) {
sb.append(value);
}
public String getValue() {
return context.getValue();
}
}

View File

@@ -0,0 +1,118 @@
package p;
import java.util.Arrays;
import java.util.Objects;
public final class Joiner {
private final String prefix;
private final String delimiter;
private final String suffix;
private String[] elts;
private int size;
private int len;
private String emptyValue;
public Joiner(CharSequence delimiter) {
this(delimiter, "", "");
}
public Joiner(CharSequence delimiter,
CharSequence prefix,
CharSequence suffix) {
Objects.requireNonNull(prefix, "The prefix must not be null");
Objects.requireNonNull(delimiter, "The delimiter must not be null");
Objects.requireNonNull(suffix, "The suffix must not be null");
this.prefix = prefix.toString();
this.delimiter = delimiter.toString();
this.suffix = suffix.toString();
checkAddLength(0, 0);
}
public Joiner setEmptyValue(CharSequence emptyValue) {
this.emptyValue = Objects.requireNonNull(emptyValue,
"The empty value must not be null").toString();
return this;
}
private static int getChars(String s, char[] chars, int start) {
int len = s.length();
s.getChars(0, len, chars, start);
return len;
}
@Override
public String toString() {
final String[] elts = this.elts;
if (elts == null && emptyValue != null) {
return emptyValue;
}
final int size = this.size;
final int addLen = prefix.length() + suffix.length();
if (addLen == 0) {
compactElts();
return size == 0 ? "" : elts[0];
}
final String delimiter = this.delimiter;
final char[] chars = new char[len + addLen];
int k = getChars(prefix, chars, 0);
if (size > 0) {
k += getChars(elts[0], chars, k);
for (int i = 1; i < size; i++) {
k += getChars(delimiter, chars, k);
k += getChars(elts[i], chars, k);
}
}
k += getChars(suffix, chars, k);
return new String(chars);
}
public Joiner add(CharSequence newElement) {
final String elt = String.valueOf(newElement);
if (elts == null) {
elts = new String[8];
} else {
if (size == elts.length)
elts = Arrays.copyOf(elts, 2 * size);
len = checkAddLength(len, delimiter.length());
}
len = checkAddLength(len, elt.length());
elts[size++] = elt;
return this;
}
private int checkAddLength(int oldLen, int inc) {
long newLen = (long)oldLen + (long)inc;
long tmpLen = newLen + (long)prefix.length() + (long)suffix.length();
if (tmpLen != (int)tmpLen) {
throw new OutOfMemoryError("Requested array size exceeds VM limit");
}
return (int)newLen;
}
public Joiner merge(Joiner other) {
Objects.requireNonNull(other);
if (other.elts == null) {
return this;
}
other.compactElts();
return add(other.elts[0]);
}
private void compactElts() {
if (size > 1) {
final char[] chars = new char[len];
int i = 1, k = getChars(elts[0], chars, 0);
do {
k += getChars(delimiter, chars, k);
k += getChars(elts[i], chars, k);
elts[i] = null;
} while (++i < size);
size = 1;
elts[0] = new String(chars);
}
}
public int length() {
return (size == 0 && emptyValue != null) ? emptyValue.length() :
len + prefix.length() + suffix.length();
}
}

View File

@@ -0,0 +1,38 @@
package p;
import java.util.concurrent.Callable;
public class MultipleImpls {
public static interface Strategy {
String doSomething(String value);
}
public static class Strat1 implements Strategy {
public String doSomething(String value) {
return value;
}
}
// implements in different library should not count as impl
public static class Strat3 implements Callable<String> {
@Override
public String call() throws Exception {
return null;
}
}
public static class Strat2 implements Strategy {
private String foo;
public String doSomething(String value) {
this.foo = value;
return "none";
}
public String getValue() {
return this.foo;
}
}
}

View File

@@ -0,0 +1,64 @@
package p;
import java.util.Iterator;
import java.util.List;
import java.io.IOException;
import java.io.OutputStream;
public class ParamFlow {
public String returnsInput(String input) {
return input;
}
public int ignorePrimitiveReturnValue(String input) {
return input.length();
}
public String returnMultipleParameters(String one, String two) {
if (System.currentTimeMillis() > 100) {
return two;
}
return one;
}
public String returnArrayElement(String[] input) {
return input[0];
}
public String returnVarArgElement(String... input) {
return input[0];
}
public String returnCollectionElement(List<String> input) {
return input.get(0);
}
public String returnIteratorElement(Iterator<String> input) {
return input.next();
}
public String returnIterableElement(Iterable<String> input) {
return input.iterator().next();
}
public Class<?> mapType(Class<?> input) {
return input;
}
public void writeChunked(byte[] data, OutputStream output)
throws IOException {
output.write(data, 0, data.length);
}
public void writeChunked(char[] data, OutputStream output)
throws IOException {
output.write(String.valueOf(data).getBytes(), 0, data.length);
}
public void addTo(String data, List<String> target) {
target.add(data);
}
}

View File

@@ -0,0 +1,98 @@
package p;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
public final class Pojo {
private class Holder {
private String value;
Holder(String value) {
this.value = value;
}
int length() {
return value.length();
}
}
private String value;
private int intValue = 2;
private byte[] byteArray = new byte[] {1, 2, 3} ;
private float[] floatArray = new float[] {1, 2, 3} ;
private char[] charArray = new char[] {'a', 'b', 'c'} ;
private List<Character> charList = Arrays.asList('a', 'b', 'c');
private Byte[] byteObjectArray = new Byte[] { 1, 2, 3 };
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public int doNotSetValue(String value) {
Holder h = new Holder(value);
return h.length();
}
public int getIntValue() {
return intValue;
}
public Integer getBoxedValue() {
return Integer.valueOf(intValue);
}
public int[] getPrimitiveArray() {
return new int[] { intValue };
}
public char[] getCharArray() {
return charArray;
}
public byte[] getByteArray() {
return byteArray;
}
public float[] getFloatArray() {
return floatArray;
}
public Integer[] getBoxedArray() {
return new Integer[] { Integer.valueOf(intValue) };
}
public Collection<Integer> getBoxedCollection() {
return List.of(Integer.valueOf(intValue));
}
public List<Character> getBoxedChars() {
return charList;
}
public Byte[] getBoxedBytes() {
return byteObjectArray;
}
public BigInteger getBigInt() {
return BigInteger.valueOf(intValue);
}
public BigDecimal getBigDecimal() {
return new BigDecimal(value);
}
public void fillIn(List<String> target) {
target.add(value);
}
}

View File

@@ -0,0 +1,61 @@
package p;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class PrivateFlowViaPublicInterface {
static class RandomPojo {
public File someFile = new File("someFile");
}
public static interface SPI {
OutputStream openStream() throws IOException;
default OutputStream openStreamNone() throws IOException {
return null;
};
}
private static final class PrivateImplWithSink implements SPI {
private File file;
public PrivateImplWithSink(File file) {
this.file = file;
}
@Override
public OutputStream openStream() throws IOException {
return new FileOutputStream(file);
}
}
private static final class PrivateImplWithRandomField implements SPI {
public PrivateImplWithRandomField(File file) {
}
@Override
public OutputStream openStream() throws IOException {
return null;
}
@Override
public OutputStream openStreamNone() throws IOException {
return new FileOutputStream(new RandomPojo().someFile);
}
}
public static SPI createAnSPI(File file) {
return new PrivateImplWithSink(file);
}
public static SPI createAnSPIWithoutTrackingFile(File file) {
return new PrivateImplWithRandomField(file);
}
}

View File

@@ -0,0 +1,24 @@
package p;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.CopyOption;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
public class Sinks {
public Path copyFileToDirectory(final Path sourceFile, final Path targetFile, final CopyOption... copyOptions) throws IOException {
return Files.copy(sourceFile, targetFile, copyOptions);
}
public String readUrl(final URL url, Charset encoding) throws IOException {
try (InputStream in = url.openStream()) {
byte[] bytes = in.readAllBytes();
return new String(bytes, encoding);
}
}
}

View File

@@ -0,0 +1,17 @@
package p;
enum SomeEnum {
FOO("input");
private String input;
private SomeEnum(String input) {
this.input = input;
}
public String getValue() {
return input;
}
}

View File

@@ -0,0 +1,28 @@
package p;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.URL;
import java.util.function.Consumer;
import java.util.List;
public class Sources {
public InputStream readUrl(final URL url) throws IOException {
return url.openConnection().getInputStream();
}
public InputStream socketStream() throws IOException {
ServerSocket socket = new ServerSocket(123);
return socket.accept().getInputStream();
}
public void sourceToParameter(InputStream[] streams, List<InputStream> otherStreams) throws IOException {
ServerSocket socket = new ServerSocket(123);
streams[0] = socket.accept().getInputStream();
otherStreams.add(socket.accept().getInputStream());
}
}

View File

@@ -0,0 +1,4 @@
lgtm,codescanning
* A new query, `js/bad-tag-filter`, has been added to the query suite,
highlighting regular expressions that only match a subset of the HTML tags
it is supposed to match.

View File

@@ -4,6 +4,8 @@
* Extracts data about the functions in the database for use in adaptive threat modeling (ATM).
*/
private import EndpointFeatures::NeighborhoodBodies
module Raw {
private import javascript as raw
@@ -30,6 +32,9 @@ module Raw {
entity.getNumBodyStmt() = 0 and not exists(entity.getAReturnedExpr())
}
/**
* Wrapper for RawAstNode (could be an alias instead of a newtype)
*/
newtype WrappedAstNode = TAstNode(RawAstNode rawNode)
/**
@@ -44,6 +49,10 @@ module Raw {
AstNode getParentNode() { result = TAstNode(rawNode.getParent()) }
raw::ASTNode getNode() { result = rawNode }
raw::StmtContainer getContainer() { result = rawNode.getContainer() }
/**
* Holds if the AST node has `result` as its `index`th attribute.
*
@@ -128,6 +137,11 @@ module Raw {
}
}
/**
* Returns the `Raw::AstNode` wrapper of `rawNode`
*/
AstNode astNode(RawAstNode rawNode) { result = TAstNode(rawNode) }
/**
* Holds if `result` is the `index`'th child of the `parent` entity. Such
* a node is a root of an AST associated with this entity.
@@ -324,6 +338,11 @@ module Wrapped {
Raw::Location getLocation() { result = rawNode.getLocation() }
}
/**
* Returns the `Wrapped::AstNode` for a `rawNode` in the context of `entity`
*/
AstNode astNode(Raw::Entity entity, Raw::AstNode rawNode) { result = TAstNode(entity, rawNode) }
/**
* A synthetic AST node, created to be a leaf for an otherwise non-leaf attribute.
*/
@@ -383,6 +402,8 @@ module DatabaseFeatures {
override Location getLocation() { result = entity.getLocation() }
UnderlyingFunction getDefinedFunction() { result = entity.getDefinedFunction() }
Wrapped::Entity getWrappedEntity() { result = entity }
}
class AstNode extends EntityOrAstNode, TAstNode {
@@ -392,8 +413,12 @@ module DatabaseFeatures {
AstNode getChild(int index) { result = TAstNode(rawNode.getChild(index)) }
AstNode getAChild() { result = this.getChild(_) }
string getAttribute(int index) { result = rawNode.getAttribute(index) }
Wrapped::AstNode getRawNode() { result = rawNode }
override string getType() { result = rawNode.getType() }
override string toString() { result = this.getType() }
@@ -401,6 +426,9 @@ module DatabaseFeatures {
override Location getLocation() { result = rawNode.getLocation() }
}
/** Gets the `DatabaseFeatures::AstNode` that wraps `wrapped` */
AstNode astNode(Wrapped::AstNode wrapped) { result.getRawNode() = wrapped }
/** Consistency checks: these predicates should each have no results */
module Consistency {
query predicate nonLeafAttribute(AstNode node, int index, string attribute) {
@@ -423,15 +451,15 @@ module DatabaseFeatures {
}
query predicate astNodes(
Entity enclosingEntity, EntityOrAstNode parent, int index, AstNode node, string node_type
Entity enclosingEntity, EntityOrAstNode parent, int index, AstNode child, string childType
) {
node = enclosingEntity.getAstRoot(index) and
child = enclosingEntity.getAstRoot(index) and
parent = enclosingEntity and
node_type = node.getType()
childType = child.getType()
or
astNodes(enclosingEntity, _, _, parent, _) and
node = parent.(AstNode).getChild(index) and
node_type = node.getType()
child = parent.(AstNode).getChild(index) and
childType = child.getType()
}
query predicate nodeAttributes(AstNode node, string attr) {

View File

@@ -8,6 +8,28 @@ import javascript
import CodeToFeatures
import EndpointScoring
/** Maximum number of descendants of an AST node to be considered to be in the "neighborhood" of that node */
class DescendantsThreshold extends int {
DescendantsThreshold() { this = [1 .. 10000] }
predicate is1024() { this = 1024 }
predicate is512() { this = 512 }
predicate is256() { this = 256 }
predicate is128() { this = 128 }
predicate is64() { this = 64 }
predicate is32() { this = 32 }
int getMaxNumDescendants() { result = this }
}
/** Maximum number of subtokens in a function body */
private int maxNumBodySubtokens() { result = 256 }
/**
* Gets the value of the token-based feature named `featureName` for the endpoint `endpoint`.
*
@@ -25,6 +47,40 @@ private string getTokenFeature(DataFlow::Node endpoint, string featureName) {
result = unique(string x | x = FunctionBodies::getBodyTokenFeatureForEntity(entity))
)
or
// A feature containing natural language tokens from the neighborhood around the endpoint
// (limited to within the function that encloses the endpoint), in the order they appear
// in the source code.
exists(
Raw::AstNode rootNode, DatabaseFeatures::AstNode rootNodeWrapped, DescendantsThreshold thresh
|
(
featureName = "enclosingFunctionBodyEndpointNeighborhood1024" and
thresh.is1024()
or
featureName = "enclosingFunctionBodyEndpointNeighborhood512" and
thresh.is512()
or
featureName = "enclosingFunctionBodyEndpointNeighborhood256" and
thresh.is256()
or
featureName = "enclosingFunctionBodyEndpointNeighborhood128" and
thresh.is128()
or
featureName = "enclosingFunctionBodyEndpointNeighborhood64" and
thresh.is64()
or
featureName = "enclosingFunctionBodyEndpointNeighborhood32" and
thresh.is32()
) and
rootNode =
NeighborhoodBodies::getNeighborhoodAstNode(Raw::astNode(endpoint.getAstNode()), thresh) and
rootNodeWrapped = DatabaseFeatures::astNode(Wrapped::astNode(rootNode.getContainer(), rootNode)) and
result =
unique(string x |
x = NeighborhoodBodies::getBodyTokenFeatureForNeighborhoodNode(rootNodeWrapped)
)
)
or
exists(getACallBasedTokenFeatureComponent(endpoint, _, featureName)) and
result =
concat(DataFlow::CallNode call, string component |
@@ -121,13 +177,17 @@ module FunctionBodies {
/**
* Gets the body token feature for the specified entity.
*
* This is a string containing natural language tokens in the order that they appear in the source code for the entity.
* This is a string containing natural language tokens in the order that they appear in the source
* code for the entity.
*
* If a function has more than `maxNumBodySubtokens` body subtokens, then featurize it as absent.
* This approximates the behavior of the classifer on non-generic body features where large body
* features are replaced by the absent token.
*/
string getBodyTokenFeatureForEntity(DatabaseFeatures::Entity entity) {
// If a function has more than 256 body subtokens, then featurize it as absent. This
// approximates the behavior of the classifer on non-generic body features where large body
// features are replaced by the absent token.
if count(DatabaseFeatures::AstNode node, string token | bodyTokens(entity, node, token)) > 256
if
count(DatabaseFeatures::AstNode node, string token | bodyTokens(entity, node, token)) >
maxNumBodySubtokens()
then result = ""
else
result =
@@ -147,6 +207,86 @@ module FunctionBodies {
}
}
/**
* This module provides functionality for getting the local neighborhood around an AST node within
* its enclosing function body, providing a locally-scoped version of the `enclosingFunctionBody` feature.
*/
module NeighborhoodBodies {
/**
* Return the ancestor of the input AST node that has the largest number of descendants (i.e. the
* node nearest the root) but has no more than `maxNumDescendants` descendants.
*
* TODO: Maybe instead of a threshold on number of descendants, we should instead have a threshold
* on the number of leaves in the subtree, which is a closer approximation to the number of tokens
* in the subtree.
*/
Raw::AstNode getNeighborhoodAstNode(Raw::AstNode node, DescendantsThreshold maxNumDescendants) {
if
node.getParentNode() = getOutermostEnclosingFunction(node) or
getNumDescendents(node.getParentNode()) > maxNumDescendants.getMaxNumDescendants()
then result = node
else result = getNeighborhoodAstNode(node.getParentNode(), maxNumDescendants)
}
/** Count number of descendants of an AST node */
int getNumDescendents(Raw::AstNode node) { result = count(node.getAChildNode*()) }
private ASTNode getContainer(ASTNode node) { result = node.getContainer() }
/** Return the AST node that is outermost enclosing function (as an AST Node) */
Raw::AstNode getOutermostEnclosingFunction(Raw::AstNode node) {
result = Raw::astNode(getContainer*(node.getNode())) and
result.getContainer() instanceof TopLevel
}
/**
* Holds if `childNode` is an AST node under `rootNode` and `token` is a node attribute associated
* with `childNode`. Note that only AST leaves have node attributes.
*
* TODO we may need to restrict `rootNode` to be a neighborhood root to avoid a potentially big result set.
*/
private predicate bodyTokens(
DatabaseFeatures::AstNode rootNode, DatabaseFeatures::AstNode childNode, string token
) {
childNode = rootNode.getAChild*() and
token = unique(string t | DatabaseFeatures::nodeAttributes(childNode, t))
}
/**
* Gets the body token feature limited to the part of the function body that lies under `rootNode` in the AST.
*
* This is a string of space-separated natural language tokens (AST leaves) in the order that they
* appear in the source code for the AST subtree rooted at `rootNode`. This is equivalent to the
* portion of the code that falls under the AST subtree rooted at the given node, except that
* non-leaf nodes (such as operators) are excluded.
*
* If a function has more than `maxNumBodySubtokens` body subtokens, then featurize it as absent.
* This approximates the behavior of the classifer on non-generic body features where large body
* features are replaced by the absent token.
*/
string getBodyTokenFeatureForNeighborhoodNode(DatabaseFeatures::AstNode rootNode) {
if
count(DatabaseFeatures::AstNode node, string token | bodyTokens(rootNode, node, token)) >
maxNumBodySubtokens()
then result = ""
else
result =
concat(int i, string rankedToken |
rankedToken =
rank[i](DatabaseFeatures::AstNode node, string token, Location l |
bodyTokens(rootNode, node, token) and l = node.getLocation()
|
token
order by
l.getFile().getAbsolutePath(), l.getStartLine(), l.getStartColumn(), l.getEndLine(),
l.getEndColumn(), token
)
|
rankedToken, " " order by i
)
}
}
/**
* This module provides functionality for getting a representation of the access path of nodes
* within the program.
@@ -191,7 +331,10 @@ private module AccessPaths {
Boolean() { this = true or this = false }
}
/** Get the access path for the node. This includes structural information like `member`, `param`, and `functionalarg` if `includeStructuralInfo` is true. */
/**
* Get the access path for the node. This includes structural information like `member`, `param`,
* and `functionalarg` if `includeStructuralInfo` is true.
*/
predicate accessPaths(
API::Node node, Boolean includeStructuralInfo, string accessPath, string apiName
) {
@@ -269,7 +412,12 @@ private string getASupportedFeatureName() {
result =
[
"enclosingFunctionName", "calleeName", "receiverName", "argumentIndex", "calleeApiName",
"calleeAccessPath", "calleeAccessPathWithStructuralInfo", "enclosingFunctionBody"
"calleeAccessPath", "calleeAccessPathWithStructuralInfo", "enclosingFunctionBody",
"enclosingFunctionBodyEndpointNeighborhood1024",
"enclosingFunctionBodyEndpointNeighborhood512",
"enclosingFunctionBodyEndpointNeighborhood256",
"enclosingFunctionBodyEndpointNeighborhood128", "enclosingFunctionBodyEndpointNeighborhood64",
"enclosingFunctionBodyEndpointNeighborhood32"
]
}

View File

@@ -54,7 +54,7 @@ DatabaseFeatures::Entity getRepresentativeEntityForEndpoint(DataFlow::Node endpo
// Use the largest entity smaller than the AST node limit, resolving ties using the entity that
// appears first in the source archive.
result =
rank[1](DatabaseFeatures::Entity entity, int numAstNodes, Location l |
min(DatabaseFeatures::Entity entity, int numAstNodes, Location l |
entity = EndpointToEntity::getAnEntityForEndpoint(endpoint) and
numAstNodes = getNumAstNodesInEntity(entity) and
numAstNodes <= getMaxNumAstNodes() and
@@ -68,7 +68,7 @@ DatabaseFeatures::Entity getRepresentativeEntityForEndpoint(DataFlow::Node endpo
// Use the smallest entity, resolving ties using the entity that
// appears first in the source archive.
result =
rank[1](DatabaseFeatures::Entity entity, int numAstNodes, Location l |
min(DatabaseFeatures::Entity entity, int numAstNodes, Location l |
entity = EndpointToEntity::getAnEntityForEndpoint(endpoint) and
numAstNodes = getNumAstNodesInEntity(entity) and
l = entity.getLocation()

View File

@@ -207,7 +207,9 @@ private predicate isAmbientTopLevel(TopLevel tl) {
*/
class TopLevel extends @toplevel, StmtContainer {
/** Holds if this toplevel is minified. */
cached
predicate isMinified() {
Stages::Ast::ref() and
// file name contains 'min' (not as part of a longer word)
getFile().getBaseName().regexpMatch(".*[^-._]*[-._]min([-._].*)?\\.\\w+")
or

View File

@@ -359,35 +359,6 @@ module DOM {
call.getNumArgument() = 1 and
unique(InferredType t | t = getArgumentTypeFromJQueryMethodGet(call)) = TTNumber()
)
or
// A `this` node from a callback given to a `$().each(callback)` call.
// purposely not using JQuery::MethodCall to avoid `jquery.each()`.
exists(DataFlow::CallNode eachCall | eachCall = JQuery::objectRef().getAMethodCall("each") |
this = DataFlow::thisNode(eachCall.getCallback(0).getFunction()) or
this = eachCall.getABoundCallbackParameter(0, 1)
)
or
// A read of an array-element from a JQuery object. E.g. `$("#foo")[0]`
exists(DataFlow::PropRead read |
read = this and read = JQuery::objectRef().getAPropertyRead()
|
unique(InferredType t | t = read.getPropertyNameExpr().analyze().getAType()) = TTNumber()
)
or
// A receiver node of an event handler on a DOM node
exists(DataFlow::SourceNode domNode, DataFlow::FunctionNode eventHandler |
// NOTE: we do not use `getABoundFunctionValue()`, since bound functions tend to have
// a different receiver anyway
eventHandler = domNode.getAPropertySource(any(string n | n.matches("on%")))
or
eventHandler =
domNode.getAMethodCall("addEventListener").getArgument(1).getAFunctionValue()
|
domNode = domValueRef() and
this = eventHandler.getReceiver()
)
or
this = DataFlow::thisNode(any(EventHandlerCode evt))
}
}
}
@@ -421,11 +392,6 @@ module DOM {
or
t.start() and
result = domValueRef().getAMethodCall(["item", "namedItem"])
or
t.startInProp("target") and
result = domEventSource()
or
exists(DataFlow::TypeTracker t2 | result = domValueRef(t2).track(t2, t))
}
/** Gets a data flow node that may refer to a value from the DOM. */

View File

@@ -141,6 +141,19 @@ private DataFlow::Node getAValueExportedByPackage() {
result = unique( | | call.getCalleeNode().getAFunctionValue()).getAReturn()
)
or
// the exported value is a function that returns another import.
// ```JavaScript
// module.exports = function foo() {
// return require("./other-module.js");
// }
// ```
exists(DataFlow::FunctionNode func, Module mod |
func = getAValueExportedByPackage().getABoundFunctionValue(_)
|
mod = func.getAReturn().getALocalSource().getEnclosingExpr().(Import).getImportedModule() and
result = getAnExportFromModule(mod)
)
or
// *****
// Common styles of transforming exported objects.
// *****

View File

@@ -185,12 +185,12 @@ module Promises {
/**
* Gets the pseudo-field used to describe resolved values in a promise.
*/
string valueProp() { result = "$PromiseResolveField$" }
string valueProp() { none() }
/**
* Gets the pseudo-field used to describe rejected values in a promise.
*/
string errorProp() { result = "$PromiseRejectField$" }
string errorProp() { none() }
}
/**

View File

@@ -7,6 +7,7 @@
import javascript
private import semmle.javascript.dataflow.InferredTypes
private import semmle.javascript.internal.CachedStages
/**
* An element containing a regular expression term, that is, either
@@ -955,7 +956,9 @@ private predicate isUsedAsNonMatchObject(DataFlow::MethodCallNode call) {
/**
* Holds if `source` may be interpreted as a regular expression.
*/
cached
predicate isInterpretedAsRegExp(DataFlow::Node source) {
Stages::Taint::ref() and
source.analyze().getAType() = TTString() and
(
// The first argument to an invocation of `RegExp` (with or without `new`).

View File

@@ -777,10 +777,10 @@ private class AdditionalFlowStepAsSharedStep extends SharedFlowStep {
*/
module PseudoProperties {
bindingset[s]
private string pseudoProperty(string s) { result = "$" + s + "$" }
private string pseudoProperty(string s) { none() }
bindingset[s, v]
private string pseudoProperty(string s, string v) { result = "$" + s + "|" + v + "$" }
private string pseudoProperty(string s, string v) { none() }
/**
* Gets a pseudo-property for the location of elements in a `Set`

View File

@@ -136,7 +136,7 @@ module Angular2 {
/** Gets a reference to a `DomSanitizer` object. */
DataFlow::SourceNode domSanitizer() {
result.hasUnderlyingType(["@angular/platform-browser", "@angular/core"], "DomSanitizer")
result.hasUnderlyingType("@angular/platform-browser", "DomSanitizer")
}
/** A value that is about to be promoted to a trusted HTML or CSS value. */

View File

@@ -864,28 +864,6 @@ module Express {
override string getCredentialsKind() { result = kind }
}
/** A call to `response.sendFile`, considered as a file system access. */
private class ResponseSendFileAsFileSystemAccess extends FileSystemReadAccess,
DataFlow::MethodCallNode {
ResponseSendFileAsFileSystemAccess() {
exists(string name | name = "sendFile" or name = "sendfile" |
calls(any(ResponseExpr res).flow(), name)
)
}
override DataFlow::Node getADataNode() { none() }
override DataFlow::Node getAPathArgument() { result = getArgument(0) }
override DataFlow::Node getRootPathArgument() {
result = this.(DataFlow::CallNode).getOptionArgument(1, "root")
}
override predicate isUpwardNavigationRejected(DataFlow::Node argument) {
argument = getAPathArgument()
}
}
/**
* A function that flows to a route setup.
*/

View File

@@ -4,23 +4,6 @@
import javascript
/**
* A call that can produce a file name.
*/
abstract private class FileNameProducer extends DataFlow::Node {
/**
* Gets a file name produced by this producer.
*/
abstract DataFlow::Node getAFileName();
}
/**
* A node that contains a file name, and is produced by a `ProducesFileNames`.
*/
private class ProducedFileName extends FileNameSource {
ProducedFileName() { this = any(FileNameProducer producer).getAFileName() }
}
/**
* A file name from the `walk-sync` library.
*/
@@ -143,338 +126,3 @@ private DataFlow::Node fastGlobFileNameSource(DataFlow::TypeTracker t) {
private class FastGlobFileNameSource extends FileNameSource {
FastGlobFileNameSource() { this = fastGlobFileNameSource(DataFlow::TypeTracker::end()) }
}
/**
* Classes and predicates for modelling the `fstream` library (https://www.npmjs.com/package/fstream).
*/
private module FStream {
/**
* Gets a reference to a method in the `fstream` library.
*/
private DataFlow::SourceNode getAnFStreamProperty(boolean writer) {
exists(DataFlow::SourceNode mod, string readOrWrite, string subMod |
mod = DataFlow::moduleImport("fstream") and
(
readOrWrite = "Reader" and writer = false
or
readOrWrite = "Writer" and writer = true
) and
(subMod = "File" or subMod = "Dir" or subMod = "Link" or subMod = "Proxy")
|
result = mod.getAPropertyRead(readOrWrite) or
result = mod.getAPropertyRead(readOrWrite).getAPropertyRead(subMod) or
result = mod.getAPropertyRead(subMod).getAPropertyRead(readOrWrite)
)
}
/**
* An invocation of a method defined in the `fstream` library.
*/
private class FStream extends FileSystemAccess, DataFlow::InvokeNode {
boolean writer;
FStream() { this = getAnFStreamProperty(writer).getAnInvocation() }
override DataFlow::Node getAPathArgument() {
result = getOptionArgument(0, "path")
or
not exists(getOptionArgument(0, "path")) and
result = getArgument(0)
}
}
/**
* An invocation of an `fstream` method that writes to a file.
*/
private class FStreamWriter extends FileSystemWriteAccess, FStream {
FStreamWriter() { writer = true }
override DataFlow::Node getADataNode() { none() }
}
/**
* An invocation of an `fstream` method that reads a file.
*/
private class FStreamReader extends FileSystemReadAccess, FStream {
FStreamReader() { writer = false }
override DataFlow::Node getADataNode() { none() }
}
}
/**
* A call to the library `write-file-atomic`.
*/
private class WriteFileAtomic extends FileSystemWriteAccess, DataFlow::CallNode {
WriteFileAtomic() {
this = DataFlow::moduleImport("write-file-atomic").getACall()
or
this = DataFlow::moduleMember("write-file-atomic", "sync").getACall()
}
override DataFlow::Node getAPathArgument() { result = getArgument(0) }
override DataFlow::Node getADataNode() { result = getArgument(1) }
}
/**
* A call to the library `recursive-readdir`.
*/
private class RecursiveReadDir extends FileSystemAccess, FileNameProducer, DataFlow::CallNode {
RecursiveReadDir() { this = DataFlow::moduleImport("recursive-readdir").getACall() }
override DataFlow::Node getAPathArgument() { result = getArgument(0) }
override DataFlow::Node getAFileName() { result = trackFileSource(DataFlow::TypeTracker::end()) }
private DataFlow::SourceNode trackFileSource(DataFlow::TypeTracker t) {
t.start() and result = getCallback([1 .. 2]).getParameter(1)
or
t.startInPromise() and not exists(getCallback([1 .. 2])) and result = this
or
// Tracking out of a promise
exists(DataFlow::TypeTracker t2 |
result = PromiseTypeTracking::promiseStep(trackFileSource(t2), t, t2)
)
}
}
/**
* Classes and predicates for modelling the `jsonfile` library (https://www.npmjs.com/package/jsonfile).
*/
private module JSONFile {
/**
* A reader for JSON files.
*/
class JSONFileReader extends FileSystemReadAccess, DataFlow::CallNode {
JSONFileReader() {
this =
DataFlow::moduleMember("jsonfile", any(string s | s = "readFile" or s = "readFileSync"))
.getACall()
}
override DataFlow::Node getAPathArgument() { result = getArgument(0) }
override DataFlow::Node getADataNode() { result = trackRead(DataFlow::TypeTracker::end()) }
private DataFlow::SourceNode trackRead(DataFlow::TypeTracker t) {
this.getCalleeName() = "readFile" and
(
t.start() and result = getCallback([1 .. 2]).getParameter(1)
or
t.startInPromise() and not exists(getCallback([1 .. 2])) and result = this
)
or
t.start() and
this.getCalleeName() = "readFileSync" and
result = this
or
// Tracking out of a promise
exists(DataFlow::TypeTracker t2 |
result = PromiseTypeTracking::promiseStep(trackRead(t2), t, t2)
)
}
}
/**
* A writer for JSON files.
*/
class JSONFileWriter extends FileSystemWriteAccess, DataFlow::CallNode {
JSONFileWriter() {
this =
DataFlow::moduleMember("jsonfile", any(string s | s = "writeFile" or s = "writeFileSync"))
.getACall()
}
override DataFlow::Node getAPathArgument() { result = getArgument(0) }
override DataFlow::Node getADataNode() { result = getArgument(1) }
}
}
/**
* A call to the library `load-json-file`.
*/
private class LoadJsonFile extends FileSystemReadAccess, DataFlow::CallNode {
LoadJsonFile() {
this = DataFlow::moduleImport("load-json-file").getACall()
or
this = DataFlow::moduleMember("load-json-file", "sync").getACall()
}
override DataFlow::Node getAPathArgument() { result = getArgument(0) }
override DataFlow::Node getADataNode() { result = trackRead(DataFlow::TypeTracker::end()) }
private DataFlow::SourceNode trackRead(DataFlow::TypeTracker t) {
this.getCalleeName() = "sync" and t.start() and result = this
or
not this.getCalleeName() = "sync" and t.startInPromise() and result = this
or
// Tracking out of a promise
exists(DataFlow::TypeTracker t2 |
result = PromiseTypeTracking::promiseStep(trackRead(t2), t, t2)
)
}
}
/**
* A call to the library `write-json-file`.
*/
private class WriteJsonFile extends FileSystemWriteAccess, DataFlow::CallNode {
WriteJsonFile() {
this = DataFlow::moduleImport("write-json-file").getACall()
or
this = DataFlow::moduleMember("write-json-file", "sync").getACall()
}
override DataFlow::Node getAPathArgument() { result = getArgument(0) }
override DataFlow::Node getADataNode() { result = getArgument(1) }
}
/**
* A call to the library `walkdir`.
*/
private class WalkDir extends FileNameProducer, FileSystemAccess, DataFlow::CallNode {
WalkDir() {
this = DataFlow::moduleImport("walkdir").getACall()
or
this = DataFlow::moduleMember("walkdir", "sync").getACall()
or
this = DataFlow::moduleMember("walkdir", "async").getACall()
}
override DataFlow::Node getAPathArgument() { result = getArgument(0) }
override DataFlow::Node getAFileName() { result = trackFileSource(DataFlow::TypeTracker::end()) }
private DataFlow::SourceNode trackFileSource(DataFlow::TypeTracker t) {
not this.getCalleeName() = any(string s | s = "sync" or s = "async") and
t.start() and
(
result = getCallback(getNumArgument() - 1).getParameter(0)
or
result = getAMethodCall(EventEmitter::on()).getCallback(1).getParameter(0)
)
or
t.start() and this.getCalleeName() = "sync" and result = this
or
t.startInPromise() and this.getCalleeName() = "async" and result = this
or
// Tracking out of a promise
exists(DataFlow::TypeTracker t2 |
result = PromiseTypeTracking::promiseStep(trackFileSource(t2), t, t2)
)
}
}
/**
* A call to the library `globule`.
*/
private class Globule extends FileNameProducer, FileSystemAccess, DataFlow::CallNode {
Globule() {
this = DataFlow::moduleMember("globule", "find").getACall()
or
this = DataFlow::moduleMember("globule", "match").getACall()
or
this = DataFlow::moduleMember("globule", "isMatch").getACall()
or
this = DataFlow::moduleMember("globule", "mapping").getACall()
or
this = DataFlow::moduleMember("globule", "findMapping").getACall()
}
override DataFlow::Node getAPathArgument() {
(this.getCalleeName() = "match" or this.getCalleeName() = "isMatch") and
result = getArgument(1)
or
this.getCalleeName() = "mapping" and
(
result = getAnArgument() and not exists(result.getALocalSource().getAPropertyWrite("src"))
or
result = getAnArgument().getALocalSource().getAPropertyWrite("src").getRhs()
)
}
override DataFlow::Node getAFileName() {
result = this and
(
this.getCalleeName() = "find" or
this.getCalleeName() = "match" or
this.getCalleeName() = "findMapping" or
this.getCalleeName() = "mapping"
)
}
}
/**
* A file system access made by a NodeJS library.
* This class models multiple NodeJS libraries that access files.
*/
private class LibraryAccess extends FileSystemAccess, DataFlow::InvokeNode {
int pathArgument; // The index of the path argument.
LibraryAccess() {
pathArgument = 0 and
(
this = DataFlow::moduleImport("path-exists").getACall()
or
this = DataFlow::moduleImport("rimraf").getACall()
or
this = DataFlow::moduleImport("readdirp").getACall()
or
this = DataFlow::moduleImport("walker").getACall()
or
this =
DataFlow::moduleMember("node-dir",
any(string s |
s = ["readFiles", "readFilesStream", "files", "promiseFiles", "subdirs", "paths"]
)).getACall()
)
or
pathArgument = 0 and
this =
DataFlow::moduleMember("vinyl-fs", any(string s | s = "src" or s = "dest" or s = "symlink"))
.getACall()
or
pathArgument = [0 .. 1] and
(
this = DataFlow::moduleImport("ncp").getACall() or
this = DataFlow::moduleMember("ncp", "ncp").getACall()
)
}
override DataFlow::Node getAPathArgument() { result = getArgument(pathArgument) }
}
/**
* A call to the library [`chokidar`](https://www.npmjs.com/package/chokidar), where a call to `on` receives file names.
*/
class Chokidar extends FileNameProducer, FileSystemAccess, API::CallNode {
Chokidar() { this = API::moduleImport("chokidar").getMember("watch").getACall() }
override DataFlow::Node getAPathArgument() { result = getArgument(0) }
override DataFlow::Node getAFileName() {
exists(DataFlow::CallNode onCall, int pathIndex |
onCall = getAChainedMethodCall("on") and
if onCall.getArgument(0).mayHaveStringValue("all") then pathIndex = 1 else pathIndex = 0
|
result = onCall.getCallback(1).getParameter(pathIndex)
)
}
}
/**
* A call to the [`mkdirp`](https://www.npmjs.com/package/mkdirp) library.
*/
private class Mkdirp extends FileSystemAccess, API::CallNode {
Mkdirp() {
this = API::moduleImport("mkdirp").getACall()
or
this = API::moduleImport("mkdirp").getMember("sync").getACall()
}
override DataFlow::Node getAPathArgument() { result = getArgument(0) }
}

View File

@@ -6,146 +6,120 @@ import javascript
module NoSQL {
/** An expression that is interpreted as a NoSQL query. */
abstract class Query extends Expr {
/** Gets an expression that is interpreted as a code operator in this query. */
DataFlow::Node getACodeOperator() { none() }
}
}
/**
* Gets a value that has been assigned to the "$where" property of an object that flows to `queryArg`.
*/
private DataFlow::Node getADollarWhereProperty(API::Node queryArg) {
result = queryArg.getMember("$where").getARhs()
abstract class Query extends Expr { }
}
/**
* Provides classes modeling the MongoDB library.
*/
private module MongoDB {
/**
* Gets an import of MongoDB.
*/
DataFlow::ModuleImportNode mongodb() { result.getPath() = "mongodb" }
/**
* Gets an access to `mongodb.MongoClient`.
*/
private API::Node getAMongoClient() {
result = API::moduleImport("mongodb").getMember("MongoClient")
private DataFlow::SourceNode getAMongoClient(DataFlow::TypeTracker t) {
t.start() and
result = mongodb().getAPropertyRead("MongoClient")
or
result = getAMongoDbCallback().getParameter(1) and
not result.getAnImmediateUse().(DataFlow::ParameterNode).getName() = "db" // mongodb v2 provides a `Db` here
}
/** Gets an API-graph node that refers to a `connect` callback. */
private API::Node getAMongoDbCallback() {
result = getAMongoClient().getMember("connect").getLastParameter()
exists(DataFlow::TypeTracker t2 | result = getAMongoClient(t2).track(t2, t))
}
/**
* Gets an API-graph node that may refer to a MongoDB database connection.
* Gets an access to `mongodb.MongoClient`.
*/
private API::Node getAMongoDb() {
result = getAMongoClient().getMember("db").getReturn()
DataFlow::SourceNode getAMongoClient() { result = getAMongoClient(DataFlow::TypeTracker::end()) }
/** Gets a data flow node that leads to a `connect` callback. */
private DataFlow::SourceNode getAMongoDbCallback(DataFlow::TypeBackTracker t) {
t.start() and
result = getAMongoClient().getAMemberCall("connect").getArgument(1).getALocalSource()
or
result = getAMongoDbCallback().getParameter(1) and
not result.getAnImmediateUse().(DataFlow::ParameterNode).getName() = "client" // mongodb v3 provides a `Mongoclient` here
exists(DataFlow::TypeBackTracker t2 | result = getAMongoDbCallback(t2).backtrack(t2, t))
}
/** Gets a data flow node that leads to a `connect` callback. */
private DataFlow::FunctionNode getAMongoDbCallback() {
result = getAMongoDbCallback(DataFlow::TypeBackTracker::end())
}
/**
* Gets an expression that may refer to a MongoDB database connection.
*/
private DataFlow::SourceNode getAMongoDb(DataFlow::TypeTracker t) {
t.start() and
result = getAMongoDbCallback().getParameter(1)
or
exists(DataFlow::TypeTracker t2 | result = getAMongoDb(t2).track(t2, t))
}
/**
* Gets an expression that may refer to a MongoDB database connection.
*/
DataFlow::SourceNode getAMongoDb() { result = getAMongoDb(DataFlow::TypeTracker::end()) }
/**
* A data flow node that may hold a MongoDB collection.
*/
abstract class Collection extends DataFlow::SourceNode { }
/**
* A collection resulting from calling `Db.collection(...)`.
*/
private class CollectionFromDb extends Collection {
CollectionFromDb() {
this = getAMongoDb().getAMethodCall("collection")
or
this = getAMongoDb().getAMethodCall("collection").getCallback(1).getParameter(0)
}
}
/**
* A collection based on the type `mongodb.Collection`.
*
* Note that this also covers `mongoose` models since they are subtypes
* of `mongodb.Collection`.
*/
private class CollectionFromType extends Collection {
CollectionFromType() { hasUnderlyingType("mongodb", "Collection") }
}
/** Gets a data flow node referring to a MongoDB collection. */
private API::Node getACollection() {
// A collection resulting from calling `Db.collection(...)`.
exists(API::Node collection | collection = getAMongoDb().getMember("collection").getReturn() |
result = collection
or
result = collection.getParameter(1).getParameter(0)
)
private DataFlow::SourceNode getACollection(DataFlow::TypeTracker t) {
t.start() and
result instanceof Collection
or
// note that this also covers `mongoose` models since they are subtypes of `mongodb.Collection`
result = API::Node::ofType("mongodb", "Collection")
exists(DataFlow::TypeTracker t2 | result = getACollection(t2).track(t2, t))
}
/** Gets a data flow node referring to a MongoDB collection. */
DataFlow::SourceNode getACollection() { result = getACollection(DataFlow::TypeTracker::end()) }
/** A call to a MongoDB query method. */
private class QueryCall extends DatabaseAccess, API::CallNode {
private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode {
int queryArgIdx;
QueryCall() {
exists(string method |
CollectionMethodSignatures::interpretsArgumentAsQuery(method, queryArgIdx) and
this = getACollection().getMember(method).getACall()
exists(string m | this = getACollection().getAMethodCall(m) |
m = "count" and queryArgIdx = 0
or
m = "distinct" and queryArgIdx = 1
or
m = "find" and queryArgIdx = 0
)
}
override DataFlow::Node getAQueryArgument() { result = getArgument(queryArgIdx) }
DataFlow::Node getACodeOperator() {
result = getADollarWhereProperty(getParameter(queryArgIdx))
}
}
/**
* An expression that is interpreted as a MongoDB query.
*/
class Query extends NoSQL::Query {
QueryCall qc;
Query() { this = qc.getAQueryArgument().asExpr() }
override DataFlow::Node getACodeOperator() { result = qc.getACodeOperator() }
}
/**
* Provides signatures for the Collection methods.
*/
module CollectionMethodSignatures {
/**
* Holds if Collection method `name` interprets parameter `n` as a query.
*/
predicate interpretsArgumentAsQuery(string name, int n) {
// FilterQuery
(
name = "aggregate" and n = 0
or
name = "count" and n = 0
or
name = "countDocuments" and n = 0
or
name = "deleteMany" and n = 0
or
name = "deleteOne" and n = 0
or
name = "distinct" and n = 1
or
name = "find" and n = 0
or
name = "findOne" and n = 0
or
name = "findOneAndDelete" and n = 0
or
name = "findOneAndRemove" and n = 0
or
name = "findOneAndReplace" and n = 0
or
name = "findOneAndUpdate" and n = 0
or
name = "remove" and n = 0
or
name = "replaceOne" and n = 0
or
name = "update" and n = 0
or
name = "updateMany" and n = 0
or
name = "updateOne" and n = 0
)
or
// UpdateQuery
(
name = "findOneAndUpdate" and n = 1
or
name = "update" and n = 1
or
name = "updateMany" and n = 1
or
name = "updateOne" and n = 1
)
}
Query() { this = any(QueryCall qc).getAQueryArgument().asExpr() }
}
}
@@ -156,345 +130,20 @@ private module Mongoose {
/**
* Gets an import of Mongoose.
*/
API::Node getAMongooseInstance() { result = API::moduleImport("mongoose") }
DataFlow::ModuleImportNode getAMongooseInstance() { result.getPath() = "mongoose" }
/**
* Gets a reference to `mongoose.createConnection`.
* Gets a call to `mongoose.createConnection`.
*/
API::Node createConnection() { result = getAMongooseInstance().getMember("createConnection") }
/**
* A Mongoose function.
*/
abstract private class MongooseFunction extends API::Node {
/**
* Gets the API-graph node for the result from this function (if the function returns a `Query`).
*/
abstract API::Node getQueryReturn();
/**
* Holds if this function returns a `Query` that evaluates to one or
* more Documents (`asArray` is false if it evaluates to a single
* Document).
*/
abstract predicate returnsDocumentQuery(boolean asArray);
/**
* Gets an argument that this function interprets as a query.
*/
abstract API::Node getQueryArgument();
DataFlow::CallNode createConnection() {
result = getAMongooseInstance().getAMemberCall("createConnection")
}
/**
* Provides classes modeling the Mongoose Model class
* A Mongoose collection object.
*/
module Model {
private class ModelFunction extends MongooseFunction {
string methodName;
ModelFunction() { this = getModelObject().getMember(methodName) }
override API::Node getQueryReturn() {
MethodSignatures::returnsQuery(methodName) and result = this.getReturn()
}
override predicate returnsDocumentQuery(boolean asArray) {
MethodSignatures::returnsDocumentQuery(methodName, asArray)
}
override API::Node getQueryArgument() {
exists(int n |
MethodSignatures::interpretsArgumentAsQuery(methodName, n) and
result = this.getParameter(n)
)
}
}
/**
* Gets a API-graph node referring to a Mongoose Model object.
*/
private API::Node getModelObject() {
result = getAMongooseInstance().getMember("model").getReturn()
or
exists(API::Node conn | conn = createConnection().getReturn() |
result = conn.getMember("model").getReturn() or
result = conn.getMember("models").getAMember()
)
or
result = API::Node::ofType("mongoose", "Model")
}
/**
* Provides signatures for the Model methods.
*/
module MethodSignatures {
/**
* Holds if Model method `name` interprets parameter `n` as a query.
*/
predicate interpretsArgumentAsQuery(string name, int n) {
// implement lots of the MongoDB collection interface
MongoDB::CollectionMethodSignatures::interpretsArgumentAsQuery(name, n)
or
name = "find" + ["ById", "One"] + "AndUpdate" and n = 1
or
name in ["delete" + ["Many", "One"], "geoSearch", "remove", "replaceOne", "where"] and
n = 0
or
name in [
"find" + ["", "ById", "One"],
"find" + ["ById", "One"] + "And" + ["Delete", "Remove", "Update"],
"update" + ["", "Many", "One"]
] and
n = 0
}
/**
* Holds if Model method `name` returns a Query.
*/
predicate returnsQuery(string name) {
name =
[
"$where", "count", "findOne", "findOneAndDelete", "findOneAndRemove",
"findOneAndReplace", "findOneAndUpdate", "geosearch", "remove", "replaceOne", "update",
"updateMany", "countDocuments", "updateOne", "where", "deleteMany", "deleteOne", "find",
"findById", "findByIdAndDelete", "findByIdAndRemove", "findByIdAndUpdate"
]
}
/**
* Holds if Document method `name` returns a query that results in
* one or more documents, the documents are wrapped in an array
* if `asArray` is true.
*/
predicate returnsDocumentQuery(string name, boolean asArray) {
asArray = false and name = "findOne"
or
asArray = true and name = "find"
}
}
}
/**
* Provides classes modeling the Mongoose Query class
*/
module Query {
private class QueryFunction extends MongooseFunction {
string methodName;
QueryFunction() { this = getAMongooseQuery().getMember(methodName) }
override API::Node getQueryReturn() {
MethodSignatures::returnsQuery(methodName) and result = this.getReturn()
}
override predicate returnsDocumentQuery(boolean asArray) {
MethodSignatures::returnsDocumentQuery(methodName, asArray)
}
override API::Node getQueryArgument() {
exists(int n |
MethodSignatures::interpretsArgumentAsQuery(methodName, n) and
result = this.getParameter(n)
)
}
}
private class NewQueryFunction extends MongooseFunction {
NewQueryFunction() { this = getAMongooseInstance().getMember("Query") }
override API::Node getQueryReturn() { result = this.getInstance() }
override predicate returnsDocumentQuery(boolean asArray) { none() }
override API::Node getQueryArgument() { result = this.getParameter(2) }
}
/**
* Gets a data flow node referring to a Mongoose query object.
*/
API::Node getAMongooseQuery() {
result = any(MongooseFunction f).getQueryReturn()
or
result = API::Node::ofType("mongoose", "Query")
or
result =
getAMongooseQuery()
.getMember(any(string name | MethodSignatures::returnsQuery(name)))
.getReturn()
}
/**
* Provides signatures for the Query methods.
*/
module MethodSignatures {
/**
* Holds if Query method `name` interprets parameter `n` as a query.
*/
predicate interpretsArgumentAsQuery(string name, int n) {
n = 0 and
name =
[
"and", "count", "findOneAndReplace", "findOneAndUpdate", "merge", "nor", "or", "remove",
"replaceOne", "setQuery", "setUpdate", "update", "countDocuments", "updateMany",
"updateOne", "where", "deleteMany", "deleteOne", "elemMatch", "find", "findOne",
"findOneAndDelete", "findOneAndRemove"
]
or
n = 1 and
name = ["distinct", "findOneAndUpdate", "update", "updateMany", "updateOne"]
}
/**
* Holds if Query method `name` returns a Query.
*/
predicate returnsQuery(string name) {
name =
[
"$where", "J", "comment", "count", "countDocuments", "distinct", "elemMatch", "equals",
"error", "estimatedDocumentCount", "exists", "explain", "all", "find", "findById",
"findOne", "findOneAndRemove", "findOneAndUpdate", "geometry", "get", "gt", "gte",
"hint", "and", "in", "intersects", "lean", "limit", "lt", "lte", "map", "map",
"maxDistance", "maxTimeMS", "batchsize", "maxscan", "mod", "ne", "near", "nearSphere",
"nin", "or", "orFail", "polygon", "populate", "box", "read", "readConcern", "regexp",
"remove", "select", "session", "set", "setOptions", "setQuery", "setUpdate", "center",
"size", "skip", "slaveOk", "slice", "snapshot", "sort", "update", "w", "where",
"within", "centerSphere", "wtimeout", "circle", "collation"
]
}
/**
* Holds if Query method `name` returns a query that results in
* one or more documents, the documents are wrapped in an array
* if `asArray` is true.
*/
predicate returnsDocumentQuery(string name, boolean asArray) {
asArray = false and name = "findOne"
or
asArray = true and name = "find"
}
}
}
/**
* Provides classes modeling the Mongoose Document class
*/
module Document {
private class DocumentFunction extends MongooseFunction {
string methodName;
DocumentFunction() { this = getAMongooseDocument().getMember(methodName) }
override API::Node getQueryReturn() {
MethodSignatures::returnsQuery(methodName) and result = this.getReturn()
}
override predicate returnsDocumentQuery(boolean asArray) {
MethodSignatures::returnsDocumentQuery(methodName, asArray)
}
override API::Node getQueryArgument() {
exists(int n |
MethodSignatures::interpretsArgumentAsQuery(methodName, n) and
result = this.getParameter(n)
)
}
}
/**
* A Mongoose Document that is retrieved from the backing database.
*/
class RetrievedDocument extends API::Node {
RetrievedDocument() {
exists(boolean asArray, API::Node param |
exists(MongooseFunction func |
func.returnsDocumentQuery(asArray) and
param = func.getLastParameter().getParameter(1)
)
or
exists(API::Node f |
f = Query::getAMongooseQuery().getMember("then") and
param = f.getParameter(0).getParameter(0)
or
f = Query::getAMongooseQuery().getMember("exec") and
param = f.getParameter(0).getParameter(1)
|
exists(DataFlow::MethodCallNode pred |
// limitation: look at the previous method call
Query::MethodSignatures::returnsDocumentQuery(pred.getMethodName(), asArray) and
pred.getAMethodCall() = f.getACall()
)
)
|
asArray = false and this = param
or
asArray = true and
// limitation: look for direct accesses
this = param.getUnknownMember()
)
}
}
/**
* Gets a data flow node referring to a Mongoose Document object.
*/
private API::Node getAMongooseDocument() {
result instanceof RetrievedDocument
or
result = API::Node::ofType("mongoose", "Document")
or
result =
getAMongooseDocument()
.getMember(any(string name | MethodSignatures::returnsDocument(name)))
.getReturn()
}
private module MethodSignatures {
/**
* Holds if Document method `name` returns a Query.
*/
predicate returnsQuery(string name) {
// Documents are subtypes of Models
Model::MethodSignatures::returnsQuery(name) or
name = "replaceOne" or
name = "update" or
name = "updateOne"
}
/**
* Holds if Document method `name` interprets parameter `n` as a query.
*/
predicate interpretsArgumentAsQuery(string name, int n) {
// Documents are subtypes of Models
Model::MethodSignatures::interpretsArgumentAsQuery(name, n)
or
n = 0 and
(
name = "replaceOne" or
name = "update" or
name = "updateOne"
)
}
/**
* Holds if Document method `name` returns a query that results in
* one or more documents, the documents are wrapped in an array
* if `asArray` is true.
*/
predicate returnsDocumentQuery(string name, boolean asArray) {
// Documents are subtypes of Models
Model::MethodSignatures::returnsDocumentQuery(name, asArray)
}
/**
* Holds if Document method `name` returns a Document.
*/
predicate returnsDocument(string name) {
name = "depopulate" or
name = "init" or
name = "populate" or
name = "overwrite"
}
}
class Model extends MongoDB::Collection {
Model() { this = getAMongooseInstance().getAMemberCall("model") }
}
/**
@@ -504,9 +153,7 @@ private module Mongoose {
string kind;
Credentials() {
exists(string prop |
this = createConnection().getParameter(3).getMember(prop).getARhs().asExpr()
|
exists(string prop | this = createConnection().getOptionArgument(3, prop).asExpr() |
prop = "user" and kind = "user name"
or
prop = "pass" and kind = "password"
@@ -515,222 +162,4 @@ private module Mongoose {
override string getCredentialsKind() { result = kind }
}
/**
* An expression that is interpreted as a (part of a) MongoDB query.
*/
class MongoDBQueryPart extends NoSQL::Query {
MongooseFunction f;
MongoDBQueryPart() { this = f.getQueryArgument().getARhs().asExpr() }
override DataFlow::Node getACodeOperator() {
result = getADollarWhereProperty(f.getQueryArgument())
}
}
/**
* An evaluation of a MongoDB query.
*/
class ShorthandQueryEvaluation extends DatabaseAccess, DataFlow::InvokeNode {
MongooseFunction f;
ShorthandQueryEvaluation() {
this = f.getACall() and
// shorthand for execution: provide a callback
exists(f.getQueryReturn()) and
exists(this.getCallback(this.getNumArgument() - 1))
}
override DataFlow::Node getAQueryArgument() {
// NB: the complete information is not easily accessible for deeply chained calls
f.getQueryArgument().getARhs() = result
}
}
class ExplicitQueryEvaluation extends DatabaseAccess {
ExplicitQueryEvaluation() {
// explicit execution using a Query method call
Query::getAMongooseQuery().getMember(["exec", "then", "catch"]).getACall() = this
}
override DataFlow::Node getAQueryArgument() {
// NB: the complete information is not easily accessible for deeply chained calls
none()
}
}
}
/**
* Provides classes modeling the Minimongo library.
*/
private module Minimongo {
/**
* Provides signatures for the Collection methods.
*/
module CollectionMethodSignatures {
/**
* Holds if Collection method `name` interprets parameter `n` as a query.
*/
predicate interpretsArgumentAsQuery(string m, int queryArgIdx) {
// implements most of the MongoDB interface
MongoDB::CollectionMethodSignatures::interpretsArgumentAsQuery(m, queryArgIdx)
}
}
/** A call to a Minimongo query method. */
private class QueryCall extends DatabaseAccess, API::CallNode {
int queryArgIdx;
QueryCall() {
exists(string m |
this =
API::moduleImport("minimongo")
.getAMember()
.getReturn()
.getAMember()
.getMember(m)
.getACall() and
CollectionMethodSignatures::interpretsArgumentAsQuery(m, queryArgIdx)
)
}
override DataFlow::Node getAQueryArgument() { result = getArgument(queryArgIdx) }
DataFlow::Node getACodeOperator() {
result = getADollarWhereProperty(getParameter(queryArgIdx))
}
}
/**
* An expression that is interpreted as a Minimongo query.
*/
class Query extends NoSQL::Query {
QueryCall qc;
Query() { this = qc.getAQueryArgument().asExpr() }
override DataFlow::Node getACodeOperator() { result = qc.getACodeOperator() }
}
}
/**
* Provides classes modeling the MarsDB library.
*/
private module MarsDB {
/** A call to a MarsDB query method. */
private class QueryCall extends DatabaseAccess, API::CallNode {
int queryArgIdx;
QueryCall() {
exists(string m |
this =
API::moduleImport("marsdb").getMember("Collection").getInstance().getMember(m).getACall() and
// implements parts of the Minimongo interface
Minimongo::CollectionMethodSignatures::interpretsArgumentAsQuery(m, queryArgIdx)
)
}
override DataFlow::Node getAQueryArgument() { result = getArgument(queryArgIdx) }
DataFlow::Node getACodeOperator() {
result = getADollarWhereProperty(getParameter(queryArgIdx))
}
}
/**
* An expression that is interpreted as a MarsDB query.
*/
class Query extends NoSQL::Query {
QueryCall qc;
Query() { this = qc.getAQueryArgument().asExpr() }
override DataFlow::Node getACodeOperator() { result = qc.getACodeOperator() }
}
}
/**
* Provides classes modeling the `Node Redis` library.
*
* Redis is an in-memory key-value store and not a database,
* but `Node Redis` can be exploited similarly to a NoSQL database by giving a method an array as argument instead of a string.
* As an example the below two invocations of `client.set` are equivalent:
*
* ```
* const redis = require("redis");
* const client = redis.createClient();
* client.set("key", "value");
* client.set(["key", "value"]);
* ```
*
* ioredis is a very similar library. However, ioredis does not support array arguments in the same way, and is therefore not vulnerable to the same kind of type confusion.
*/
private module Redis {
/**
* Gets a `Node Redis` client.
*/
private API::Node client() {
result = API::moduleImport("redis").getMember("createClient").getReturn()
or
result = API::moduleImport("redis").getMember("RedisClient").getInstance()
or
result = client().getMember("duplicate").getReturn()
or
result = client().getMember("duplicate").getLastParameter().getParameter(1)
}
/**
* Gets a (possibly chained) reference to a batch operation object.
* These have the same API as a redis client, except the calls are chained, and the sequence is terminated with a `.exec` call.
*/
private API::Node multi() {
result = client().getMember(["multi", "batch"]).getReturn()
or
result = multi().getAMember().getReturn()
}
/**
* Gets a `Node Redis` client instance. Either a client created using `createClient()`, or a batch operation object.
*/
private API::Node redis() { result = [client(), multi()] }
/**
* Provides signatures for the query methods from Node Redis.
*/
module QuerySignatures {
/**
* Holds if `method` interprets parameter `argIndex` as a key, and a later parameter determines a value/field.
* Thereby the method is vulnerable if parameter `argIndex` is unexpectedly an array instead of a string, as an attacker can control arguments to Redis that the attacker was not supposed to control.
*
* Only setters and similar methods are included.
* For getter-like methods it is not generally possible to gain access "outside" of where you are supposed to have access,
* it is at most possible to get a Redis call to return more results than expected (e.g. by adding more members to [`geohash`](https://redis.io/commands/geohash)).
*/
predicate argumentIsAmbiguousKey(string method, int argIndex) {
method =
[
"set", "publish", "append", "bitfield", "decrby", "getset", "hincrby", "hincrbyfloat",
"hset", "hsetnx", "incrby", "incrbyfloat", "linsert", "lpush", "lpushx", "lset", "ltrim",
"rename", "renamenx", "rpushx", "setbit", "setex", "smove", "zincrby", "zinterstore",
"hdel", "lpush", "pfadd", "rpush", "sadd", "sdiffstore", "srem"
] and
argIndex = 0
or
method = ["bitop", "hmset", "mset", "msetnx", "geoadd"] and
argIndex in [0 .. any(DataFlow::InvokeNode invk).getNumArgument() - 1]
}
}
/**
* An expression that is interpreted as a key in a Node Redis call.
*/
class RedisKeyArgument extends NoSQL::Query {
RedisKeyArgument() {
exists(string method, int argIndex |
QuerySignatures::argumentIsAmbiguousKey(method, argIndex) and
this = redis().getMember(method).getParameter(argIndex).getARhs().asExpr()
)
}
}
}

View File

@@ -448,56 +448,11 @@ module NodeJSLib {
*/
module FS {
/**
* A member `member` from module `fs` or its drop-in replacements `graceful-fs`, `fs-extra`, `original-fs`.
* A member `member` from module `fs`.
*/
DataFlow::SourceNode moduleMember(string member) {
result = fsModule(DataFlow::TypeTracker::end()).getAPropertyRead(member)
}
private DataFlow::SourceNode fsModule(DataFlow::TypeTracker t) {
exists(string moduleName |
moduleName = ["mz/fs", "original-fs", "fs-extra", "graceful-fs", "fs"]
|
result = DataFlow::moduleImport(moduleName)
or
// extra support for flexible names
result.asExpr().(Require).getArgument(0).mayHaveStringValue(moduleName)
) and
t.start()
or
t.start() and
result = DataFlow::moduleMember("fs", "promises")
or
exists(DataFlow::TypeTracker t2, DataFlow::SourceNode pred | pred = fsModule(t2) |
result = pred.track(t2, t)
or
t.continue() = t2 and
exists(Promisify::PromisifyAllCall promisifyAllCall |
result = promisifyAllCall and
pred.flowsTo(promisifyAllCall.getArgument(0))
)
or
// const fs = require('fs');
// let fs_copy = methods.reduce((obj, method) => {
// obj[method] = fs[method];
// return obj;
// }, {});
t.continue() = t2 and
exists(
DataFlow::MethodCallNode call, DataFlow::ParameterNode obj, DataFlow::SourceNode method
|
call.getMethodName() = "reduce" and
result = call and
obj = call.getABoundCallbackParameter(0, 0) and
obj.flowsTo(any(DataFlow::FunctionNode f).getAReturn()) and
exists(DataFlow::PropWrite write, DataFlow::PropRead read |
write = obj.getAPropertyWrite() and
method.flowsToExpr(write.getPropertyNameExpr()) and
method.flowsToExpr(read.getPropertyNameExpr()) and
read.getBase().getALocalSource() = fsModule(t2) and
write.getRhs() = maybePromisified(read)
)
)
exists(string moduleName | moduleName = ["fs"] |
result = DataFlow::moduleMember(moduleName, member)
)
}
}
@@ -508,7 +463,7 @@ module NodeJSLib {
private class NodeJSFileSystemAccess extends FileSystemAccess, DataFlow::CallNode {
string methodName;
NodeJSFileSystemAccess() { this = maybePromisified(FS::moduleMember(methodName)).getACall() }
NodeJSFileSystemAccess() { this = FS::moduleMember(methodName).getACall() }
/**
* Gets the name of the called method.

View File

@@ -28,54 +28,38 @@ module SQL {
* Provides classes modelling the (API compatible) `mysql` and `mysql2` packages.
*/
private module MySql {
private string moduleName() { result = ["mysql", "mysql2", "mysql2/promise"] }
private DataFlow::SourceNode mysql() { result = DataFlow::moduleImport(["mysql", "mysql2"]) }
/** Gets the package name `mysql` or `mysql2`. */
API::Node mysql() { result = API::moduleImport(moduleName()) }
private DataFlow::CallNode createPool() { result = mysql().getAMemberCall("createPool") }
/** Gets a reference to `mysql.createConnection`. */
API::Node createConnection() {
result = mysql().getMember(["createConnection", "createConnectionPromise"])
/** Gets a reference to a MySQL pool. */
private DataFlow::SourceNode pool(DataFlow::TypeTracker t) {
t.start() and
result = createPool()
}
/** Gets a reference to `mysql.createPool`. */
API::Node createPool() { result = mysql().getMember(["createPool", "createPoolCluster"]) }
/** Gets a reference to a MySQL pool. */
private DataFlow::SourceNode pool() { result = pool(DataFlow::TypeTracker::end()) }
/** Gets a node that contains a MySQL pool created using `mysql.createPool()`. */
API::Node pool() {
result = createPool().getReturn()
or
result = pool().getMember("on").getReturn()
or
result = API::Node::ofType(moduleName(), ["Pool", "PoolCluster"])
}
/** Gets a call to `mysql.createConnection`. */
DataFlow::CallNode createConnection() { result = mysql().getAMemberCall("createConnection") }
/** Gets a data flow node that contains a freshly created MySQL connection instance. */
API::Node connection() {
result = createConnection().getReturn()
or
result = createConnection().getReturn().getPromised()
or
result = pool().getMember("getConnection").getParameter(0).getParameter(1)
or
result = pool().getMember("getConnection").getPromised()
or
exists(API::CallNode call |
call = pool().getMember("on").getACall() and
call.getArgument(0).getStringValue() = ["connection", "acquire", "release"] and
result = call.getParameter(1).getParameter(0)
/** Gets a reference to a MySQL connection instance. */
private DataFlow::SourceNode connection(DataFlow::TypeTracker t) {
t.start() and
(
result = createConnection()
or
result = pool().getAMethodCall("getConnection").getABoundCallbackParameter(0, 1)
)
or
result = API::Node::ofType(moduleName(), ["Connection", "PoolConnection"])
}
/** Gets a reference to a MySQL connection instance. */
DataFlow::SourceNode connection() { result = connection(DataFlow::TypeTracker::end()) }
/** A call to the MySql `query` method. */
private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode {
QueryCall() {
exists(API::Node recv | recv = pool() or recv = connection() |
this = recv.getMember(["query", "execute"]).getACall()
)
}
QueryCall() { this = [pool(), connection()].getAMethodCall("query") }
override DataFlow::Node getAQueryArgument() { result = getArgument(0) }
}
@@ -88,7 +72,7 @@ private module MySql {
/** A call to the `escape` or `escapeId` method that performs SQL sanitization. */
class EscapingSanitizer extends SQL::SqlSanitizer, MethodCallExpr {
EscapingSanitizer() {
this = [mysql(), pool(), connection()].getMember(["escape", "escapeId"]).getACall().asExpr() and
this = [mysql(), pool(), connection()].getAMethodCall(["escape", "escapeId"]).asExpr() and
input = this.getArgument(0) and
output = this
}
@@ -99,9 +83,8 @@ private module MySql {
string kind;
Credentials() {
exists(API::Node callee, string prop |
callee in [createConnection(), createPool()] and
this = callee.getParameter(0).getMember(prop).getARhs().asExpr() and
exists(string prop |
this = [createConnection(), createPool()].getOptionArgument(0, prop).asExpr() and
(
prop = "user" and kind = "user name"
or
@@ -118,61 +101,23 @@ private module MySql {
* Provides classes modelling the PostgreSQL packages, such as `pg` and `pg-promise`.
*/
private module Postgres {
API::Node pg() {
result = API::moduleImport("pg")
or
result = pgpMain().getMember("pg")
}
/** Gets a reference to the `Client` constructor in the `pg` package, for example `require('pg').Client`. */
API::Node newClient() { result = pg().getMember("Client") }
/** Gets a freshly created Postgres client instance. */
API::Node client() {
result = newClient().getInstance()
or
// pool.connect(function(err, client) { ... })
result = pool().getMember("connect").getParameter(0).getParameter(1)
or
// await pool.connect()
result = pool().getMember("connect").getReturn().getPromised()
or
result = pgpConnection().getMember("client")
or
exists(API::CallNode call |
call = pool().getMember("on").getACall() and
call.getArgument(0).getStringValue() = ["connect", "acquire"] and
result = call.getParameter(1).getParameter(0)
)
or
result = client().getMember("on").getReturn()
or
result = API::Node::ofType("pg", ["Client", "PoolClient"])
}
/** Gets a constructor that when invoked constructs a new connection pool. */
API::Node newPool() {
/** Gets an expression that constructs a new connection pool. */
DataFlow::InvokeNode newPool() {
// new require('pg').Pool()
result = pg().getMember("Pool")
result = DataFlow::moduleImport("pg").getAConstructorInvocation("Pool")
or
// new require('pg-pool')
result = API::moduleImport("pg-pool")
result = DataFlow::moduleImport("pg-pool").getAnInstantiation()
}
/** Gets an API node that refers to a connection pool. */
API::Node pool() {
result = newPool().getInstance()
or
result = pgpDatabase().getMember("$pool")
or
result = pool().getMember("on").getReturn()
or
result = API::Node::ofType("pg", "Pool")
/** Gets a creation of a Postgres client. */
DataFlow::InvokeNode newClient() {
result = DataFlow::moduleImport("pg").getAConstructorInvocation("Client")
}
/** A call to the Postgres `query` method. */
private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode {
QueryCall() { this = [client(), pool()].getMember("query").getACall() }
QueryCall() { this = [newClient(), newPool()].getAMethodCall("query") }
override DataFlow::Node getAQueryArgument() { result = getArgument(0) }
}
@@ -191,11 +136,7 @@ private module Postgres {
string kind;
Credentials() {
exists(string prop |
this = [newClient(), newPool()].getParameter(0).getMember(prop).getARhs().asExpr()
or
this = pgPromise().getParameter(0).getMember(prop).getARhs().asExpr()
|
exists(string prop | this = [newClient(), newPool()].getOptionArgument(0, prop).asExpr() |
prop = "user" and kind = "user name"
or
prop = "password" and kind = prop
@@ -335,35 +276,30 @@ private module Postgres {
*/
private module Sqlite {
/** Gets a reference to the `sqlite3` module. */
API::Node sqlite() {
result = API::moduleImport("sqlite3")
DataFlow::SourceNode sqlite() {
result = DataFlow::moduleImport("sqlite3")
or
result = sqlite().getMember("verbose").getReturn()
result = sqlite().getAMemberCall("verbose")
}
/** Gets an expression that constructs or returns a Sqlite database instance. */
API::Node database() {
/** Gets an expression that constructs a Sqlite database instance. */
DataFlow::SourceNode newDb() {
// new require('sqlite3').Database()
result = sqlite().getMember("Database").getInstance()
or
// chained call
result = getAChainingQueryCall()
or
result = API::Node::ofType("sqlite3", "Database")
result = sqlite().getAConstructorInvocation("Database")
}
/** A call to a query method on a Sqlite database instance that returns the same instance. */
private API::Node getAChainingQueryCall() {
result = database().getMember(["all", "each", "exec", "get", "run"]).getReturn()
/** Gets a data flow node referring to a Sqlite database instance. */
private DataFlow::SourceNode db(DataFlow::TypeTracker t) {
t.start() and
result = newDb()
}
/** Gets a data flow node referring to a Sqlite database instance. */
DataFlow::SourceNode db() { result = db(DataFlow::TypeTracker::end()) }
/** A call to a Sqlite query method. */
private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode {
QueryCall() {
this = getAChainingQueryCall().getAnImmediateUse()
or
this = database().getMember("prepare").getACall()
}
QueryCall() { this = db().getAMethodCall(["all", "each", "exec", "get", "prepare", "run"]) }
override DataFlow::Node getAQueryArgument() { result = getArgument(0) }
}
@@ -373,276 +309,3 @@ private module Sqlite {
QueryString() { this = any(QueryCall qc).getAQueryArgument().asExpr() }
}
}
/**
* Provides classes modelling the `mssql` package.
*/
private module MsSql {
/** Gets a reference to the `mssql` module. */
API::Node mssql() { result = API::moduleImport("mssql") }
/** Gets a node referring to an instance of the given class. */
API::Node mssqlClass(string name) {
result = mssql().getMember(name).getInstance()
or
result = API::Node::ofType("mssql", name)
}
/** Gets an API node referring to a Request object. */
API::Node request() {
result = mssqlClass("Request")
or
result = request().getMember(["input", "replaceInput", "output", "replaceOutput"]).getReturn()
or
result = [transaction(), pool()].getMember("request").getReturn()
}
/** Gets an API node referring to a Transaction object. */
API::Node transaction() {
result = mssqlClass("Transaction")
or
result = pool().getMember("transaction").getReturn()
}
/** Gets a API node referring to a ConnectionPool object. */
API::Node pool() { result = mssqlClass("ConnectionPool") }
/** A tagged template evaluated as a query. */
private class QueryTemplateExpr extends DatabaseAccess, DataFlow::ValueNode {
override TaggedTemplateExpr astNode;
QueryTemplateExpr() {
mssql().getMember("query").getAUse() = DataFlow::valueNode(astNode.getTag())
}
override DataFlow::Node getAQueryArgument() {
result = DataFlow::valueNode(astNode.getTemplate().getAnElement())
}
}
/** A call to a MsSql query method. */
private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode {
QueryCall() { this = [mssql(), request()].getMember(["query", "batch"]).getACall() }
override DataFlow::Node getAQueryArgument() { result = getArgument(0) }
}
/** An expression that is passed to a method that interprets it as SQL. */
class QueryString extends SQL::SqlString {
QueryString() {
exists(DatabaseAccess dba | dba instanceof QueryTemplateExpr or dba instanceof QueryCall |
this = dba.getAQueryArgument().asExpr()
)
}
}
/** An element of a query template, which is automatically sanitized. */
class QueryTemplateSanitizer extends SQL::SqlSanitizer {
QueryTemplateSanitizer() {
this = any(QueryTemplateExpr qte).getAQueryArgument().asExpr() and
input = this and
output = this
}
}
/** An expression that is passed as user name or password when creating a client or a pool. */
class Credentials extends CredentialsExpr {
string kind;
Credentials() {
exists(API::Node callee, string prop |
(
callee = mssql().getMember("connect")
or
callee = mssql().getMember("ConnectionPool")
) and
this = callee.getParameter(0).getMember(prop).getARhs().asExpr() and
(
prop = "user" and kind = "user name"
or
prop = "password" and kind = prop
)
)
}
override string getCredentialsKind() { result = kind }
}
}
/**
* Provides classes modelling the `sequelize` package.
*/
private module Sequelize {
/** Gets an import of the `sequelize` module or one that re-exports it. */
API::Node sequelize() { result = API::moduleImport(["sequelize", "sequelize-typescript"]) }
/** Gets an expression that creates an instance of the `Sequelize` class. */
API::Node instance() {
result = [sequelize(), sequelize().getMember("Sequelize")].getInstance()
or
result = API::Node::ofType(["sequelize", "sequelize-typescript"], ["Sequelize", "default"])
}
/** A call to `Sequelize.query`. */
private class QueryCall extends DatabaseAccess, DataFlow::MethodCallNode {
QueryCall() { this = instance().getMember("query").getACall() }
override DataFlow::Node getAQueryArgument() {
result = getArgument(0)
or
result = getOptionArgument(0, "query")
}
}
/** An expression that is passed to `Sequelize.query` method and hence interpreted as SQL. */
class QueryString extends SQL::SqlString {
QueryString() {
this = any(QueryCall qc).getAQueryArgument().asExpr()
or
this = sequelize().getMember(["literal", "asIs"]).getParameter(0).getARhs().asExpr()
}
}
/**
* An expression that is passed as user name or password when creating an instance of the
* `Sequelize` class.
*/
class Credentials extends CredentialsExpr {
string kind;
Credentials() {
exists(NewExpr ne, string prop |
ne = sequelize().getAnInstantiation().asExpr() and
(
this = ne.getArgument(1) and prop = "username"
or
this = ne.getArgument(2) and prop = "password"
or
ne.hasOptionArgument(ne.getNumArgument() - 1, prop, this)
) and
(
prop = "username" and kind = "user name"
or
prop = "password" and kind = prop
)
)
}
override string getCredentialsKind() { result = kind }
}
}
/**
* Provides classes modelling the Google Cloud Spanner library.
*/
private module Spanner {
/**
* Gets a node that refers to the `Spanner` class
*/
API::Node spanner() {
// older versions
result = API::moduleImport("@google-cloud/spanner")
or
// newer versions
result = API::moduleImport("@google-cloud/spanner").getMember("Spanner")
}
/**
* Gets a node that refers to an instance of the `Database` class.
*/
API::Node database() {
result =
spanner().getReturn().getMember("instance").getReturn().getMember("database").getReturn()
or
result = API::Node::ofType("@google-cloud/spanner", "Database")
}
/**
* Gets a node that refers to an instance of the `v1.SpannerClient` class.
*/
API::Node v1SpannerClient() {
result = spanner().getMember("v1").getMember("SpannerClient").getInstance()
or
result = API::Node::ofType("@google-cloud/spanner", "v1.SpannerClient")
}
/**
* Gets a node that refers to a transaction object.
*/
API::Node transaction() {
result =
database()
.getMember(["runTransaction", "runTransactionAsync"])
.getParameter([0, 1])
.getParameter(1)
or
result = API::Node::ofType("@google-cloud/spanner", "Transaction")
}
/** Gets an API node referring to a `BatchTransaction` object. */
API::Node batchTransaction() {
result = database().getMember("batchTransaction").getReturn()
or
result = database().getMember("createBatchTransaction").getReturn().getPromised()
or
result = API::Node::ofType("@google-cloud/spanner", "BatchTransaction")
}
/**
* A call to a Spanner method that executes a SQL query.
*/
abstract class SqlExecution extends DatabaseAccess, DataFlow::InvokeNode { }
/**
* A SQL execution that takes the input directly in the first argument or in the `sql` option.
*/
class SqlExecutionDirect extends SqlExecution {
SqlExecutionDirect() {
this = database().getMember(["run", "runPartitionedUpdate", "runStream"]).getACall()
or
this = transaction().getMember(["run", "runStream", "runUpdate"]).getACall()
or
this = batchTransaction().getMember("createQueryPartitions").getACall()
}
override DataFlow::Node getAQueryArgument() {
result = getArgument(0)
or
result = getOptionArgument(0, "sql")
}
}
/**
* A SQL execution that takes an array of SQL strings or { sql: string } objects.
*/
class SqlExecutionBatch extends SqlExecution, API::CallNode {
SqlExecutionBatch() { this = transaction().getMember("batchUpdate").getACall() }
override DataFlow::Node getAQueryArgument() {
// just use the whole array as the query argument, as arrays becomes tainted if one of the elements
// are tainted
result = getArgument(0)
or
result = getParameter(0).getUnknownMember().getMember("sql").getARhs()
}
}
/**
* A SQL execution that only takes the input in the `sql` option, and do not accept query strings
* directly.
*/
class SqlExecutionWithOption extends SqlExecution {
SqlExecutionWithOption() {
this = v1SpannerClient().getMember(["executeSql", "executeStreamingSql"]).getACall()
}
override DataFlow::Node getAQueryArgument() { result = getOptionArgument(0, "sql") }
}
/**
* An expression that is interpreted as a SQL string.
*/
class QueryString extends SQL::SqlString {
QueryString() { this = any(SqlExecution se).getAQueryArgument().asExpr() }
}
}

View File

@@ -36,25 +36,7 @@ module ParseTorrent {
* An access to user-controlled torrent information.
*/
class UserControlledTorrentInfo extends RemoteFlowSource {
UserControlledTorrentInfo() {
exists(DataFlow::SourceNode ref, DataFlow::PropRead read |
ref = parsedTorrentRef() and
read = ref.getAPropertyRead() and
this = read
|
exists(string prop |
not (
prop = "private" or
prop = "infoHash" or
prop = "length"
// "pieceLength" and "lastPieceLength" are not guaranteed to be numbers as of commit ae3ad15d
) and
read.getPropertyName() = prop
)
or
not exists(read.getPropertyName())
)
}
UserControlledTorrentInfo() { none() }
override string getSourceType() { result = "torrent information" }
}

View File

@@ -484,8 +484,6 @@ module JQuery {
private DataFlow::SourceNode dollar(DataFlow::TypeTracker t) {
t.start() and
result = dollarSource()
or
exists(DataFlow::TypeTracker t2 | result = dollar(t2).track(t2, t))
}
/**
@@ -519,14 +517,6 @@ module JQuery {
}
}
/**
* A `this` node in a JQuery plugin function, which is a JQuery object.
*/
private class JQueryPluginThisObject extends Range {
JQueryPluginThisObject() {
this = DataFlow::thisNode(any(JQueryPluginMethod method).getFunction())
}
}
}
/** A source of jQuery objects from the AST-based `JQueryObject` class. */

View File

@@ -260,6 +260,8 @@ module Stages {
exists(RemoteFlowSource r)
or
exists(Exports::getALibraryInputParameter())
or
any(RegExpTerm t).isUsedAsRegExp()
}
}
}

View File

@@ -0,0 +1,306 @@
/**
* Provides precicates for reasoning about bad tag filter vulnerabilities.
*/
import performance.ReDoSUtil
/**
* A module for determining if a regexp matches a given string,
* and reasoning about which capture groups are filled by a given string.
*/
private module RegexpMatching {
/**
* A class to test whether a regular expression matches a string.
* Override this class and extend `test`/`testWithGroups` to configure which strings should be tested for acceptance by this regular expression.
* The result can afterwards be read from the `matches` predicate.
*
* Strings in the `testWithGroups` predicate are also tested for which capture groups are filled by the given string.
* The result is available in the `fillCaptureGroup` predicate.
*/
abstract class MatchedRegExp extends RegExpTerm {
MatchedRegExp() { this.isRootTerm() }
/**
* Holds if it should be tested whether this regular expression matches `str`.
*
* If `ignorePrefix` is true, then a regexp without a start anchor will be treated as if it had a start anchor.
* E.g. a regular expression `/foo$/` will match any string that ends with "foo",
* but if `ignorePrefix` is true, it will only match "foo".
*/
predicate test(string str, boolean ignorePrefix) {
none() // maybe overriden in subclasses
}
/**
* Same as `test(..)`, but where the `fillsCaptureGroup` afterwards tells which capture groups were filled by the given string.
*/
predicate testWithGroups(string str, boolean ignorePrefix) {
none() // maybe overriden in subclasses
}
/**
* Holds if this RegExp matches `str`, where `str` is either in the `test` or `testWithGroups` predicate.
*/
final predicate matches(string str) {
exists(State state | state = getAState(this, str.length() - 1, str, _) |
epsilonSucc*(state) = Accept(_)
)
}
/**
* Holds if matching `str` may fill capture group number `g`.
* Only holds if `str` is in the `testWithGroups` predicate.
*/
final predicate fillsCaptureGroup(string str, int g) {
exists(State s |
s = getAStateThatReachesAccept(this, _, str, _) and
g = group(s.getRepr())
)
}
}
/**
* Gets a state the regular expression `reg` can be in after matching the `i`th char in `str`.
* The regular expression is modelled as a non-determistic finite automaton,
* the regular expression can therefore be in multiple states after matching a character.
*
* It's a forward search to all possible states, and there is thus no guarantee that the state is on a path to an accepting state.
*/
private State getAState(MatchedRegExp reg, int i, string str, boolean ignorePrefix) {
// start state, the -1 position before any chars have been matched
i = -1 and
(
reg.test(str, ignorePrefix)
or
reg.testWithGroups(str, ignorePrefix)
) and
result.getRepr().getRootTerm() = reg and
isStartState(result)
or
// recursive case
result = getAStateAfterMatching(reg, _, str, i, _, ignorePrefix)
}
/**
* Gets the next state after the `prev` state from `reg`.
* `prev` is the state after matching `fromIndex` chars in `str`,
* and the result is the state after matching `toIndex` chars in `str`.
*
* This predicate is used as a step relation in the forwards search (`getAState`),
* and also as a step relation in the later backwards search (`getAStateThatReachesAccept`).
*/
private State getAStateAfterMatching(
MatchedRegExp reg, State prev, string str, int toIndex, int fromIndex, boolean ignorePrefix
) {
// the basic recursive case - outlined into a noopt helper to make performance work out.
result = getAStateAfterMatchingAux(reg, prev, str, toIndex, fromIndex, ignorePrefix)
or
// we can skip past word boundaries if the next char is a non-word char.
fromIndex = toIndex and
prev.getRepr() instanceof RegExpWordBoundary and
prev = getAState(reg, toIndex, str, ignorePrefix) and
after(prev.getRepr()) = result and
str.charAt(toIndex + 1).regexpMatch("\\W") // \W matches any non-word char.
}
pragma[noopt]
private State getAStateAfterMatchingAux(
MatchedRegExp reg, State prev, string str, int toIndex, int fromIndex, boolean ignorePrefix
) {
prev = getAState(reg, fromIndex, str, ignorePrefix) and
fromIndex = toIndex - 1 and
exists(string char | char = str.charAt(toIndex) | specializedDeltaClosed(prev, char, result)) and
not discardedPrefixStep(prev, result, ignorePrefix)
}
/** Holds if a step from `prev` to `next` should be discarded when the `ignorePrefix` flag is set. */
private predicate discardedPrefixStep(State prev, State next, boolean ignorePrefix) {
prev = mkMatch(any(RegExpRoot r)) and
ignorePrefix = true and
next = prev
}
// The `deltaClosed` relation specialized to the chars that exists in strings tested by a `MatchedRegExp`.
private predicate specializedDeltaClosed(State prev, string char, State next) {
deltaClosed(prev, specializedGetAnInputSymbolMatching(char), next)
}
// The `getAnInputSymbolMatching` relation specialized to the chars that exists in strings tested by a `MatchedRegExp`.
pragma[noinline]
private InputSymbol specializedGetAnInputSymbolMatching(string char) {
exists(string s, MatchedRegExp r |
r.test(s, _)
or
r.testWithGroups(s, _)
|
char = s.charAt(_)
) and
result = getAnInputSymbolMatching(char)
}
/**
* Gets the `i`th state on a path to the accepting state when `reg` matches `str`.
* Starts with an accepting state as found by `getAState` and searches backwards
* to the start state through the reachable states (as found by `getAState`).
*
* This predicate holds the invariant that the result state can be reached with `i` steps from a start state,
* and an accepting state can be found after (`str.length() - 1 - i`) steps from the result.
* The result state is therefore always on a valid path where `reg` accepts `str`.
*
* This predicate is only used to find which capture groups a regular expression has filled,
* and thus the search is only performed for the strings in the `testWithGroups(..)` predicate.
*/
private State getAStateThatReachesAccept(
MatchedRegExp reg, int i, string str, boolean ignorePrefix
) {
// base case, reaches an accepting state from the last state in `getAState(..)`
reg.testWithGroups(str, ignorePrefix) and
i = str.length() - 1 and
result = getAState(reg, i, str, ignorePrefix) and
epsilonSucc*(result) = Accept(_)
or
// recursive case. `next` is the next state to be matched after matching `prev`.
// this predicate is doing a backwards search, so `prev` is the result we are looking for.
exists(State next, State prev, int fromIndex, int toIndex |
next = getAStateThatReachesAccept(reg, toIndex, str, ignorePrefix) and
next = getAStateAfterMatching(reg, prev, str, toIndex, fromIndex, ignorePrefix) and
i = fromIndex and
result = prev
)
}
/** Gets the capture group number that `term` belongs to. */
private int group(RegExpTerm term) {
exists(RegExpGroup grp | grp.getNumber() = result | term.getParent*() = grp)
}
}
/** A class to test whether a regular expression matches certain HTML tags. */
class HTMLMatchingRegExp extends RegexpMatching::MatchedRegExp {
HTMLMatchingRegExp() {
// the regexp must mention "<" and ">" explicitly.
forall(string angleBracket | angleBracket = ["<", ">"] |
any(RegExpConstant term | term.getValue().matches("%" + angleBracket + "%")).getRootTerm() =
this
)
}
override predicate testWithGroups(string str, boolean ignorePrefix) {
ignorePrefix = true and
str = ["<!-- foo -->", "<!-- foo --!>", "<!- foo ->", "<foo>", "<script>"]
}
override predicate test(string str, boolean ignorePrefix) {
ignorePrefix = true and
str =
[
"<!-- foo -->", "<!- foo ->", "<!-- foo --!>", "<!-- foo\n -->", "<script>foo</script>",
"<script \n>foo</script>", "<script >foo\n</script>", "<foo ></foo>", "<foo>",
"<foo src=\"foo\"></foo>", "<script>", "<script src=\"foo\"></script>",
"<script src='foo'></script>", "<SCRIPT>foo</SCRIPT>", "<script\tsrc=\"foo\"/>",
"<script\tsrc='foo'></script>", "<sCrIpT>foo</ScRiPt>", "<script src=\"foo\">foo</script >",
"<script src=\"foo\">foo</script foo=\"bar\">", "<script src=\"foo\">foo</script\t\n bar>"
]
}
}
/**
* Holds if `regexp` matches some HTML tags, but misses some HTML tags that it should match.
*
* When adding a new case to this predicate, make sure the test string used in `matches(..)` calls are present in `HTMLMatchingRegExp::test` / `HTMLMatchingRegExp::testWithGroups`.
*/
predicate isBadRegexpFilter(HTMLMatchingRegExp regexp, string msg) {
// CVE-2021-33829 - matching both "<!-- foo -->" and "<!-- foo --!>", but in different capture groups
regexp.matches("<!-- foo -->") and
regexp.matches("<!-- foo --!>") and
exists(int a, int b | a != b |
regexp.fillsCaptureGroup("<!-- foo -->", a) and
// <!-- foo --> might be ambigously parsed (matching both capture groups), and that is ok here.
regexp.fillsCaptureGroup("<!-- foo --!>", b) and
not regexp.fillsCaptureGroup("<!-- foo --!>", a) and
msg =
"Comments ending with --> are matched differently from comments ending with --!>. The first is matched with capture group "
+ a + " and comments ending with --!> are matched with capture group " +
strictconcat(int i | regexp.fillsCaptureGroup("<!-- foo --!>", i) | i.toString(), ", ") +
"."
)
or
// CVE-2020-17480 - matching "<!-- foo -->" and other tags, but not "<!-- foo --!>".
exists(int group, int other |
group != other and
regexp.fillsCaptureGroup("<!-- foo -->", group) and
regexp.fillsCaptureGroup("<foo>", other) and
not regexp.matches("<!-- foo --!>") and
not regexp.fillsCaptureGroup("<!-- foo -->", any(int i | i != group)) and
not regexp.fillsCaptureGroup("<!- foo ->", group) and
not regexp.fillsCaptureGroup("<foo>", group) and
not regexp.fillsCaptureGroup("<script>", group) and
msg =
"This regular expression only parses --> (capture group " + group +
") and not --!> as a HTML comment end tag."
)
or
regexp.matches("<!-- foo -->") and
not regexp.matches("<!-- foo\n -->") and
not regexp.matches("<!- foo ->") and
not regexp.matches("<foo>") and
not regexp.matches("<script>") and
msg = "This regular expression does not match comments containing newlines."
or
regexp.matches("<script>foo</script>") and
regexp.matches("<script src=\"foo\"></script>") and
not regexp.matches("<foo ></foo>") and
(
not regexp.matches("<script \n>foo</script>") and
msg = "This regular expression matches <script></script>, but not <script \\n></script>"
or
not regexp.matches("<script >foo\n</script>") and
msg = "This regular expression matches <script>...</script>, but not <script >...\\n</script>"
)
or
regexp.matches("<script>foo</script>") and
regexp.matches("<script src=\"foo\"></script>") and
not regexp.matches("<script src='foo'></script>") and
not regexp.matches("<foo>") and
msg = "This regular expression does not match script tags where the attribute uses single-quotes."
or
regexp.matches("<script>foo</script>") and
regexp.matches("<script src='foo'></script>") and
not regexp.matches("<script src=\"foo\"></script>") and
not regexp.matches("<foo>") and
msg = "This regular expression does not match script tags where the attribute uses double-quotes."
or
regexp.matches("<script>foo</script>") and
regexp.matches("<script src='foo'></script>") and
not regexp.matches("<script\tsrc='foo'></script>") and
not regexp.matches("<foo>") and
not regexp.matches("<foo src=\"foo\"></foo>") and
msg = "This regular expression does not match script tags where tabs are used between attributes."
or
regexp.matches("<script>foo</script>") and
not RegExpFlags::isIgnoreCase(regexp) and
not regexp.matches("<foo>") and
not regexp.matches("<foo ></foo>") and
(
not regexp.matches("<SCRIPT>foo</SCRIPT>") and
msg = "This regular expression does not match upper case <SCRIPT> tags."
or
not regexp.matches("<sCrIpT>foo</ScRiPt>") and
regexp.matches("<SCRIPT>foo</SCRIPT>") and
msg = "This regular expression does not match mixed case <sCrIpT> tags."
)
or
regexp.matches("<script src=\"foo\"></script>") and
not regexp.matches("<foo>") and
not regexp.matches("<foo ></foo>") and
(
not regexp.matches("<script src=\"foo\">foo</script >") and
msg = "This regular expression does not match script end tags like </script >."
or
not regexp.matches("<script src=\"foo\">foo</script foo=\"bar\">") and
msg = "This regular expression does not match script end tags like </script foo=\"bar\">."
or
not regexp.matches("<script src=\"foo\">foo</script\t\n bar>") and
msg = "This regular expression does not match script end tags like </script\\t\\n bar>."
)
}

View File

@@ -229,14 +229,8 @@ module CodeInjection {
}
/**
* A code operator of a NoSQL query as a code injection sink.
*/
class NoSQLCodeInjectionSink extends Sink {
NoSQLCodeInjectionSink() { any(NoSQL::Query q).getACodeOperator() = this }
}
/**
* The first argument to `Module.prototype._compile`, considered as a code-injection sink.
* The first argument to `Module.prototype._compile` from the Node.js built-in module `module`,
* considered as a code-injection sink.
*/
class ModuleCompileSink extends Sink {
ModuleCompileSink() {

View File

@@ -55,7 +55,7 @@ module TaintedPath {
* There are currently four flow labels, representing the different combinations of
* normalization and absoluteness.
*/
abstract class PosixPath extends DataFlow::FlowLabel {
class PosixPath extends DataFlow::FlowLabel {
Normalization normalization;
Relativeness relativeness;
@@ -113,7 +113,7 @@ module TaintedPath {
/**
* A flow label representing an array of path elements that may include "..".
*/
abstract class SplitPath extends DataFlow::FlowLabel {
class SplitPath extends DataFlow::FlowLabel {
SplitPath() { this = "splitPath" }
}
}
@@ -218,12 +218,12 @@ module TaintedPath {
output = this
or
// non-global replace or replace of something other than /\.\./g, /[/]/g, or /[\.]/g.
this instanceof StringReplaceCall and
this.getCalleeName() = "replace" and
input = getReceiver() and
output = this and
not exists(RegExpLiteral literal, RegExpTerm term |
this.(StringReplaceCall).getRegExp().asExpr() = literal and
this.(StringReplaceCall).isGlobal() and
getArgument(0).getALocalSource().asExpr() = literal and
literal.isGlobal() and
literal.getRoot() = term
|
term.getAMatchedString() = "/" or
@@ -247,15 +247,16 @@ module TaintedPath {
/**
* A call that removes all instances of "../" in the prefix of the string.
*/
class DotDotSlashPrefixRemovingReplace extends StringReplaceCall {
class DotDotSlashPrefixRemovingReplace extends DataFlow::CallNode {
DataFlow::Node input;
DataFlow::Node output;
DotDotSlashPrefixRemovingReplace() {
this.getCalleeName() = "replace" and
input = getReceiver() and
output = this and
exists(RegExpLiteral literal, RegExpTerm term |
getRegExp().asExpr() = literal and
getArgument(0).getALocalSource().asExpr() = literal and
(term instanceof RegExpStar or term instanceof RegExpPlus) and
term.getChild(0) = getADotDotSlashMatcher()
|
@@ -297,16 +298,17 @@ module TaintedPath {
/**
* A call that removes all "." or ".." from a path, without also removing all forward slashes.
*/
class DotRemovingReplaceCall extends StringReplaceCall {
class DotRemovingReplaceCall extends DataFlow::CallNode {
DataFlow::Node input;
DataFlow::Node output;
DotRemovingReplaceCall() {
this.getCalleeName() = "replace" and
input = getReceiver() and
output = this and
isGlobal() and
exists(RegExpLiteral literal, RegExpTerm term |
getRegExp().asExpr() = literal and
getArgument(0).getALocalSource().asExpr() = literal and
literal.isGlobal() and
literal.getRoot() = term and
not term.getAMatchedString() = "/"
|
@@ -606,8 +608,6 @@ module TaintedPath {
(
this = fileSystemAccess.getAPathArgument() and
not exists(fileSystemAccess.getRootPathArgument())
or
this = fileSystemAccess.getRootPathArgument()
) and
not this = any(ResolvingPathCall call).getInput()
}
@@ -648,74 +648,6 @@ module TaintedPath {
AngularJSTemplateUrlSink() { this = any(AngularJS::CustomDirective d).getMember("templateUrl") }
}
/**
* The path argument of a [send](https://www.npmjs.com/package/send) call, viewed as a sink.
*/
class SendPathSink extends Sink, DataFlow::ValueNode {
SendPathSink() { this = DataFlow::moduleImport("send").getACall().getArgument(1) }
}
/**
* A path argument given to a `Page` in puppeteer, specifying where a pdf/screenshot should be saved.
*/
private class PuppeteerPath extends TaintedPath::Sink {
PuppeteerPath() {
this =
Puppeteer::page()
.getMember(["pdf", "screenshot"])
.getParameter(0)
.getMember("path")
.getARhs()
}
}
/**
* An argument given to the `prettier` library specifying the location of a config file.
*/
private class PrettierFileSink extends TaintedPath::Sink {
PrettierFileSink() {
this =
API::moduleImport("prettier")
.getMember(["resolveConfig", "resolveConfigFile", "getFileInfo"])
.getACall()
.getArgument(0)
or
this =
API::moduleImport("prettier")
.getMember("resolveConfig")
.getACall()
.getParameter(1)
.getMember("config")
.getARhs()
}
}
/**
* The `cwd` option for the `read-pkg` library.
*/
private class ReadPkgCwdSink extends TaintedPath::Sink {
ReadPkgCwdSink() {
this =
API::moduleImport("read-pkg")
.getMember(["readPackageAsync", "readPackageSync"])
.getParameter(0)
.getMember("cwd")
.getARhs()
}
}
/**
* The `cwd` option to a shell execution.
*/
private class ShellCwdSink extends TaintedPath::Sink {
ShellCwdSink() {
exists(SystemCommandExecution sys, API::Node opts |
opts.getARhs() = sys.getOptionsArg() and // assuming that an API::Node exists here.
this = opts.getMember("cwd").getARhs()
)
}
}
/**
* Holds if there is a step `src -> dst` mapping `srclabel` to `dstlabel` relevant for path traversal vulnerabilities.
*/

View File

@@ -183,17 +183,6 @@ module DomBasedXss {
this = any(Typeahead::TypeaheadSuggestionFunction f).getAReturn()
or
this = any(Handlebars::SafeString s).getAnArgument()
or
this = any(JQuery::MethodCall call | call.getMethodName() = "jGrowl").getArgument(0)
or
// A construction of a JSDOM object (server side DOM), where scripts are allowed.
exists(DataFlow::NewNode instance |
instance = API::moduleImport("jsdom").getMember("JSDOM").getInstance().getAnImmediateUse() and
this = instance.getArgument(0) and
instance.getOptionArgument(1, "runScripts").mayHaveStringValue("dangerously")
)
or
MooTools::interpretsNodeAsHtml(this)
}
}

View File

@@ -542,7 +542,7 @@ private State before(RegExpTerm t) { result = Match(t, 0) }
/**
* Gets a state the NFA may be in after matching `t`.
*/
private State after(RegExpTerm t) {
State after(RegExpTerm t) {
exists(RegExpAlt alt | t = alt.getAChild() | result = after(alt))
or
exists(RegExpSequence seq, int i | t = seq.getChild(i) |
@@ -671,7 +671,7 @@ RegExpRoot getRoot(RegExpTerm term) {
/**
* A state in the NFA.
*/
private newtype TState =
newtype TState =
/**
* A state representing that the NFA is about to match a term.
* `i` is used to index into multi-char literals.
@@ -801,29 +801,26 @@ InputSymbol getAnInputSymbolMatching(string char) {
result = Any()
}
/**
* Holds if `state` is a start state.
*/
predicate isStartState(State state) {
state = mkMatch(any(RegExpRoot r))
or
exists(RegExpCaret car | state = after(car))
}
/**
* Predicates for constructing a prefix string that leads to a given state.
*/
private module PrefixConstruction {
/**
* Holds if `state` starts the string matched by the regular expression.
*/
private predicate isStartState(State state) {
state instanceof StateInPumpableRegexp and
(
state = Match(any(RegExpRoot r), _)
or
exists(RegExpCaret car | state = after(car))
)
}
/**
* Holds if `state` is the textually last start state for the regular expression.
*/
private predicate lastStartState(State state) {
exists(RegExpRoot root |
state =
max(State s, Location l |
max(StateInPumpableRegexp s, Location l |
isStartState(s) and getRoot(s.getRepr()) = root and l = s.getRepr().getLocation()
|
s

View File

@@ -20,12 +20,7 @@ module RegExpFlags {
/**
* Holds if `root` has the `i` flag for case-insensitive matching.
*/
predicate isIgnoreCase(RegExpTerm root) {
root.isRootTerm() and
exists(DataFlow::RegExpCreationNode node | node.getRoot() = root |
RegExp::isIgnoreCase(node.getFlags())
)
}
predicate isIgnoreCase(RegExpTerm root) { RegExp::isIgnoreCase(getFlags(root)) }
/**
* Gets the flags for `root`, or the empty string if `root` has no flags.
@@ -38,15 +33,14 @@ module RegExpFlags {
not exists(node.getFlags()) and
result = ""
)
or
exists(RegExpPatternSource source | source.getRegExpTerm() = root |
result = source.getARegExpObject().(DataFlow::RegExpCreationNode).getFlags()
)
}
/**
* Holds if `root` has the `s` flag for multi-line matching.
*/
predicate isDotAll(RegExpTerm root) {
root.isRootTerm() and
exists(DataFlow::RegExpCreationNode node | node.getRoot() = root |
RegExp::isDotAll(node.getFlags())
)
}
predicate isDotAll(RegExpTerm root) { RegExp::isDotAll(getFlags(root)) }
}

View File

@@ -0,0 +1,54 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
It is possible to match some single HTML tags using regular expressions (parsing general HTML using
regular expressions is impossible). However, if the regular expression is not written well it might
be possible to circumvent it, which can lead to cross-site scripting or other security issues.
</p>
<p>
Some of these mistakes are caused by browsers having very forgiving HTML parsers, and
will often render invalid HTML containing syntax errors.
Regular expressions that attempt to match HTML should also recognize tags containing such syntax errors.
</p>
</overview>
<recommendation>
<p>
Use a well-tested sanitization or parser library if at all possible. These libraries are much more
likely to handle corner cases correctly than a custom implementation.
</p>
</recommendation>
<example>
<p>
The following example attempts to filters out all <code>&lt;script&gt;</code> tags.
</p>
<sample src="examples/BadTagFilter.js" />
<p>
The above sanitizer does not filter out all <code>&lt;script&gt;</code> tags.
Browsers will not only accept <code>&lt;/script&gt;</code> as script end tags, but also tags such as <code>&lt;/script foo="bar"&gt;</code> even though it is a parser error.
This means that an attack string such as <code>&lt;script&gt;alert(1)&lt;/script foo="bar"&gt;</code> will not be filtered by
the function, and <code>alert(1)</code> will be executed by a browser if the string is rendered as HTML.
</p>
<p>
Other corner cases include that HTML comments can end with <code>--!&gt;</code>,
and that HTML tag names can contain upper case characters.
</p>
</example>
<references>
<li>Securitum: <a href="https://research.securitum.com/the-curious-case-of-copy-paste/">The Curious Case of Copy &amp; Paste</a>.</li>
<li>stackoverflow.com: <a href="https://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags#answer-1732454">You can't parse [X]HTML with regex</a>.</li>
<li>HTML Standard: <a href="https://html.spec.whatwg.org/multipage/parsing.html#comment-end-bang-state">Comment end bang state</a>.</li>
<li>stackoverflow.com: <a href="https://stackoverflow.com/questions/25559999/why-arent-browsers-strict-about-html">Why aren't browsers strict about HTML?</a>.</li>
</references>
</qhelp>

View File

@@ -0,0 +1,19 @@
/**
* @name Bad HTML filtering regexp
* @description Matching HTML tags using regular expressions is hard to do right, and can easily lead to security issues.
* @kind problem
* @problem.severity warning
* @security-severity 7.8
* @precision high
* @id js/bad-tag-filter
* @tags correctness
* security
* external/cwe/cwe-116
* external/cwe/cwe-020
*/
import semmle.javascript.security.BadTagFilterQuery
from HTMLMatchingRegExp regexp, string msg
where msg = min(string m | isBadRegexpFilter(regexp, m) | m order by m.length(), m) // there might be multiple, we arbitrarily pick the shortest one
select regexp, msg

View File

@@ -0,0 +1,8 @@
function filterScript(html) {
var scriptRegex = /<script\b[^>]*>([\s\S]*?)<\/script>/gi;
var match;
while ((match = scriptRegex.exec(html)) !== null) {
html = html.replace(match[0], match[1]);
}
return html;
}

View File

@@ -28,6 +28,7 @@
| jsonschema.js:15:23:15:29 | (a?a?)* | Strings with many repetitions of 'a' can start matching anywhere after the start of the preceeding (a?a?)*b |
| jsonschema.js:20:18:20:24 | (a?a?)* | Strings with many repetitions of 'a' can start matching anywhere after the start of the preceeding (a?a?)*b |
| lib/closure.js:4:6:4:7 | u* | Strings with many repetitions of 'u' can start matching anywhere after the start of the preceeding u*o |
| lib/indirect.js:2:6:2:7 | k* | Strings with many repetitions of 'k' can start matching anywhere after the start of the preceeding k*h |
| lib/lib.js:1:15:1:16 | a* | Strings with many repetitions of 'a' can start matching anywhere after the start of the preceeding a*b |
| lib/lib.js:8:3:8:4 | f* | Strings with many repetitions of 'f' can start matching anywhere after the start of the preceeding f*g |
| lib/moduleLib/moduleLib.js:2:3:2:4 | a* | Strings with many repetitions of 'a' can start matching anywhere after the start of the preceeding a*b |

View File

@@ -3,6 +3,10 @@ nodes
| lib/closure.js:3:21:3:21 | x |
| lib/closure.js:4:16:4:16 | x |
| lib/closure.js:4:16:4:16 | x |
| lib/indirect.js:1:32:1:32 | x |
| lib/indirect.js:1:32:1:32 | x |
| lib/indirect.js:2:16:2:16 | x |
| lib/indirect.js:2:16:2:16 | x |
| lib/lib.js:3:28:3:31 | name |
| lib/lib.js:3:28:3:31 | name |
| lib/lib.js:4:14:4:17 | name |
@@ -170,6 +174,10 @@ edges
| lib/closure.js:3:21:3:21 | x | lib/closure.js:4:16:4:16 | x |
| lib/closure.js:3:21:3:21 | x | lib/closure.js:4:16:4:16 | x |
| lib/closure.js:3:21:3:21 | x | lib/closure.js:4:16:4:16 | x |
| lib/indirect.js:1:32:1:32 | x | lib/indirect.js:2:16:2:16 | x |
| lib/indirect.js:1:32:1:32 | x | lib/indirect.js:2:16:2:16 | x |
| lib/indirect.js:1:32:1:32 | x | lib/indirect.js:2:16:2:16 | x |
| lib/indirect.js:1:32:1:32 | x | lib/indirect.js:2:16:2:16 | x |
| lib/lib.js:3:28:3:31 | name | lib/lib.js:4:14:4:17 | name |
| lib/lib.js:3:28:3:31 | name | lib/lib.js:4:14:4:17 | name |
| lib/lib.js:3:28:3:31 | name | lib/lib.js:4:14:4:17 | name |
@@ -329,6 +337,7 @@ edges
| polynomial-redos.js:123:13:123:20 | replaced | polynomial-redos.js:123:3:123:20 | result |
#select
| lib/closure.js:4:5:4:17 | /u*o/.test(x) | lib/closure.js:3:21:3:21 | x | lib/closure.js:4:16:4:16 | x | This $@ that depends on $@ may run slow on strings with many repetitions of 'u'. | lib/closure.js:4:6:4:7 | u* | regular expression | lib/closure.js:3:21:3:21 | x | library input |
| lib/indirect.js:2:5:2:17 | /k*h/.test(x) | lib/indirect.js:1:32:1:32 | x | lib/indirect.js:2:16:2:16 | x | This $@ that depends on $@ may run slow on strings with many repetitions of 'k'. | lib/indirect.js:2:6:2:7 | k* | regular expression | lib/indirect.js:1:32:1:32 | x | library input |
| lib/lib.js:4:2:4:18 | regexp.test(name) | lib/lib.js:3:28:3:31 | name | lib/lib.js:4:14:4:17 | name | This $@ that depends on $@ may run slow on strings with many repetitions of 'a'. | lib/lib.js:1:15:1:16 | a* | regular expression | lib/lib.js:3:28:3:31 | name | library input |
| lib/lib.js:8:2:8:17 | /f*g/.test(name) | lib/lib.js:7:19:7:22 | name | lib/lib.js:8:13:8:16 | name | This $@ that depends on $@ may run slow on strings with many repetitions of 'f'. | lib/lib.js:8:3:8:4 | f* | regular expression | lib/lib.js:7:19:7:22 | name | library input |
| lib/moduleLib/moduleLib.js:2:2:2:17 | /a*b/.test(name) | lib/moduleLib/moduleLib.js:1:28:1:31 | name | lib/moduleLib/moduleLib.js:2:13:2:16 | name | This $@ that depends on $@ may run slow on strings with many repetitions of 'a'. | lib/moduleLib/moduleLib.js:2:3:2:4 | a* | regular expression | lib/moduleLib/moduleLib.js:1:28:1:31 | name | library input |

View File

@@ -0,0 +1,3 @@
module.exports.foo = function (x) {
/k*h/.test(x); // NOT OK
}

View File

@@ -12,4 +12,8 @@ if (typeof define !== 'undefined' && define.amd) { // AMD
define([], function () {return bar});
}
module.exports.closure = require("./closure")
module.exports.closure = require("./closure")
module.exports.func = function (conf) {
return require("./indirect")
}

View File

@@ -0,0 +1,17 @@
| tst.js:2:6:2:29 | <script.*?>.*?<\\/script> | This regular expression does not match script end tags like </script >. |
| tst.js:3:6:3:29 | <script.*?>.*?<\\/script> | This regular expression does not match script end tags like </script >. |
| tst.js:7:6:7:16 | <!--.*--!?> | This regular expression does not match comments containing newlines. |
| tst.js:8:6:8:39 | <script.*?>(.\|\\s)*?<\\/script[^>]*> | This regular expression matches <script></script>, but not <script \\n></script> |
| tst.js:9:6:9:37 | <script[^>]*?>.*?<\\/script[^>]*> | This regular expression matches <script>...</script>, but not <script >...\\n</script> |
| tst.js:10:6:10:44 | <script(\\s\|\\w\|=\|")*?>.*?<\\/script[^>]*> | This regular expression does not match script tags where the attribute uses single-quotes. |
| tst.js:11:6:11:44 | <script(\\s\|\\w\|=\|')*?>.*?<\\/script[^>]*> | This regular expression does not match script tags where the attribute uses double-quotes. |
| tst.js:12:6:12:48 | <script( \|\\n\|\\w\|=\|'\|")*?>.*?<\\/script[^>]*> | This regular expression does not match script tags where tabs are used between attributes. |
| tst.js:13:6:13:34 | <script.*?>.*?<\\/script[^>]*> | This regular expression does not match upper case <SCRIPT> tags. |
| tst.js:14:6:14:52 | <(script\|SCRIPT).*?>.*?<\\/(script\|SCRIPT)[^>]*> | This regular expression does not match mixed case <sCrIpT> tags. |
| tst.js:15:6:15:39 | <script[^>]*?>[\\s\\S]*?<\\/script.*> | This regular expression does not match script end tags like </script\\t\\n bar>. |
| tst.js:17:6:17:40 | <script\\b[^>]*>([\\s\\S]*?)<\\/script> | This regular expression does not match script end tags like </script >. |
| tst.js:18:6:18:48 | <(?:!--([\\S\|\\s]*?)-->)\|([^\\/\\s>]+)[\\S\\s]*?> | Comments ending with --> are matched differently from comments ending with --!>. The first is matched with capture group 1 and comments ending with --!> are matched with capture group 2. |
| tst.js:19:6:19:147 | <(?:(?:\\/([^>]+)>)\|(?:!--([\\S\|\\s]*?)-->)\|(?:([^\\/\\s>]+)((?:\\s+[\\w\\-:.]+(?:\\s*=\\s*?(?:(?:"[^"]*")\|(?:'[^']*')\|[^\\s"'\\/>]+))?)*)[\\S\\s]*?(\\/?)>)) | Comments ending with --> are matched differently from comments ending with --!>. The first is matched with capture group 2 and comments ending with --!> are matched with capture group 3, 4. |
| tst.js:20:3:20:57 | (<[a-z\\/!$]("[^"]*"\|'[^']*'\|[^'">])*>\|<!(--.*?--\\s*)+>) | Comments ending with --> are matched differently from comments ending with --!>. The first is matched with capture group 3 and comments ending with --!> are matched with capture group 1. |
| tst.js:21:6:21:249 | <(?:(?:!--([\\w\\W]*?)-->)\|(?:!\\[CDATA\\[([\\w\\W]*?)\\]\\]>)\|(?:!DOCTYPE([\\w\\W]*?)>)\|(?:\\?([^\\s\\/<>]+) ?([\\w\\W]*?)[?/]>)\|(?:\\/([A-Za-z][A-Za-z0-9\\-_\\:\\.]*)>)\|(?:([A-Za-z][A-Za-z0-9\\-_\\:\\.]*)((?:\\s+[^"'>]+(?:(?:"[^"]*")\|(?:'[^']*')\|[^>]*))*\|\\/\|\\s+)>)) | This regular expression only parses --> (capture group 1) and not --!> as a HTML comment end tag. |
| tst.js:22:6:22:33 | <!--([\\w\\W]*?)-->\|<([^>]*?)> | Comments ending with --> are matched differently from comments ending with --!>. The first is matched with capture group 1 and comments ending with --!> are matched with capture group 2. |

View File

@@ -0,0 +1 @@
Security/CWE-116/BadTagFilter.ql

View File

@@ -0,0 +1,28 @@
var filters = [
/<script.*?>.*?<\/script>/i, // NOT OK - doesn't match newlines or `</script >`
/<script.*?>.*?<\/script>/is, // NOT OK - doesn't match `</script >`
/<script.*?>.*?<\/script[^>]*>/is, // OK
/<!--.*-->/is, // OK - we don't care regexps that only match comments
/<!--.*--!?>/is, // OK
/<!--.*--!?>/i, // NOT OK, does not match newlines
/<script.*?>(.|\s)*?<\/script[^>]*>/i, // NOT OK - doesn't match inside the script tag
/<script[^>]*?>.*?<\/script[^>]*>/i, // NOT OK - doesn't match newlines inside the content
/<script(\s|\w|=|")*?>.*?<\/script[^>]*>/is, // NOT OK - does not match single quotes for attribute values
/<script(\s|\w|=|')*?>.*?<\/script[^>]*>/is, // NOT OK - does not match double quotes for attribute values
/<script( |\n|\w|=|'|")*?>.*?<\/script[^>]*>/is, // NOT OK - does not match tabs between attributes
/<script.*?>.*?<\/script[^>]*>/s, // NOT OK - does not match uppercase SCRIPT tags
/<(script|SCRIPT).*?>.*?<\/(script|SCRIPT)[^>]*>/s, // NOT OK - does not match mixed case script tags
/<script[^>]*?>[\s\S]*?<\/script.*>/i, // NOT OK - doesn't match newlines in the end tag
/<script[^>]*?>[\s\S]*?<\/script[^>]*?>/i, // OK
/<script\b[^>]*>([\s\S]*?)<\/script>/gi, // NOT OK - too strict matching on the end tag
/<(?:!--([\S|\s]*?)-->)|([^\/\s>]+)[\S\s]*?>/, // NOT OK - doesn't match comments with the right capture groups
/<(?:(?:\/([^>]+)>)|(?:!--([\S|\s]*?)-->)|(?:([^\/\s>]+)((?:\s+[\w\-:.]+(?:\s*=\s*?(?:(?:"[^"]*")|(?:'[^']*')|[^\s"'\/>]+))?)*)[\S\s]*?(\/?)>))/, // NOT OK - capture groups
/(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|<!(--.*?--\s*)+>)/gi, // NOT OK - capture groups
/<(?:(?:!--([\w\W]*?)-->)|(?:!\[CDATA\[([\w\W]*?)\]\]>)|(?:!DOCTYPE([\w\W]*?)>)|(?:\?([^\s\/<>]+) ?([\w\W]*?)[?/]>)|(?:\/([A-Za-z][A-Za-z0-9\-_\:\.]*)>)|(?:([A-Za-z][A-Za-z0-9\-_\:\.]*)((?:\s+[^"'>]+(?:(?:"[^"]*")|(?:'[^']*')|[^>]*))*|\/|\s+)>))/g, // NOT OK - capture groups
/<!--([\w\W]*?)-->|<([^>]*?)>/g, // NOT OK - capture groups
]
doFilters(filters)
var strip = '<script([^>]*)>([\\S\\s]*?)<\/script([^>]*)>'; // OK - it's used with the ignorecase flag
new RegExp(strip, 'gi');

View File

@@ -0,0 +1,4 @@
lgtm,codescanning
* A new query, `py/bad-tag-filter`, has been added to the query suite,
highlighting regular expressions that only match a subset of the HTML tags
it is supposed to match.

View File

@@ -467,9 +467,10 @@ class RegExpEscape extends RegExpNormalChar {
or
this.getUnescaped() = "t" and result = "\t"
or
// TODO: Find a way to include a formfeed character
// this.getUnescaped() = "f" and result = " "
// or
this.getUnescaped() = "f" and result = 12.toUnicode()
or
this.getUnescaped() = "v" and result = 11.toUnicode()
or
this.isUnicode() and
result = this.getUnicode()
}
@@ -480,7 +481,7 @@ class RegExpEscape extends RegExpNormalChar {
override string getPrimaryQLClass() { result = "RegExpEscape" }
/** Gets the part of the term following the escape character. That is e.g. "w" if the term is "\w". */
private string getUnescaped() { result = this.getText().suffix(1) }
string getUnescaped() { result = this.getText().suffix(1) }
/**
* Gets the text for this escape. That is e.g. "\w".
@@ -535,6 +536,13 @@ private int toHex(string hex) {
result = 15 and hex = ["f", "F"]
}
/**
* A word boundary, that is, a regular expression term of the form `\b`.
*/
class RegExpWordBoundary extends RegExpEscape {
RegExpWordBoundary() { this.getUnescaped() = "b" }
}
/**
* A character class escape in a regular expression.
* That is, an escaped charachter that denotes multiple characters.

Some files were not shown because too many files have changed in this diff Show More