Compare commits

..

62 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
4181855d09 Add test case with MISSING tag demonstrating instance-across-call shortcoming 2026-06-30 20:24:31 +00:00
copilot-swe-agent[bot]
de8f489812 Add change note for instance-attribute type-tracking performance fix 2026-06-30 18:47:32 +00:00
copilot-swe-agent[bot]
4d327c8499 Python: reformulate instanceFieldStep to avoid classInstanceTracker recursion 2026-06-30 18:38:00 +00:00
Asger F
11e75c12a8 Merge pull request #22090 from asgerf/unified/inline-test-expectations
unified: Add inline expectation test library
2026-06-30 19:55:15 +02:00
Mathias Vorreiter Pedersen
c045da01a1 Merge pull request #22088 from MathiasVP/cpp-support-fully-qualified-field-names-in-mad
C++: Support fully qualified field names in MaD
2026-06-30 15:02:16 +01:00
Asger F
a9617f18a1 Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-30 15:48:15 +02:00
Asger F
8a46f03308 Merge pull request #22083 from asgerf/unified/suites
Unified: add default_queries and standard qls files and a dummy query
2026-06-30 15:37:53 +02:00
Asger F
fc94d1c035 unified: Add a dummy query
This is just to test DCA
2026-06-30 15:26:22 +02:00
Michael Nebel
a93501a1eb Merge pull request #22033 from michaelnebel/csharp/usefeedmanager
C#: Use the feed manager in the `NugetExeWrapper`.
2026-06-30 15:03:25 +02:00
Mathias Vorreiter Pedersen
06f54d1bbb C++: Add a TODO comment to remove support for unqualified field names. 2026-06-30 13:55:26 +01:00
Mathias Vorreiter Pedersen
396bea6e6a Update cpp/ql/lib/semmle/code/cpp/dataflow/internal/FlowSummaryImpl.qll
Co-authored-by: Tom Hvitved <hvitved@github.com>
2026-06-30 13:44:14 +01:00
Asger F
a43c5cee61 unified: Add inline expectation test library 2026-06-30 14:29:04 +02:00
Mathias Vorreiter Pedersen
81ed5c59d7 C++: Add change note. 2026-06-30 11:54:58 +01:00
Asger F
8d564d31e6 unified: Add default_queries 2026-06-30 12:34:45 +02:00
Asger F
cbcf85a953 unified: Add standard query suites
The suites include 'Unified' in their name. It sounds a bit off but
it might cause confusion if we don't include some kind of language name
in there.
2026-06-30 12:34:43 +02:00
Geoffrey White
c0871defe9 Merge pull request #22077 from geoffw0/javainline
Java: Address testFailures in inline expectations tests
2026-06-30 10:49:24 +01:00
Asger F
be39051c29 Merge pull request #22086 from asgerf/asgerf-unified-corpus-test-split
Unified: Split up corpus tests and their generated outputs
2026-06-30 11:49:10 +02:00
Owen Mansel-Chan
8447b76c12 Merge pull request #22006 from owen-mc/go/more-slog-models
Go: more models for `log.slog`
2026-06-30 10:39:48 +01:00
Owen Mansel-Chan
3d8991a4db Update change note 2026-06-30 09:35:23 +01:00
Owen Mansel-Chan
4a7afb7aeb Add data flow consistency test output 2026-06-30 09:35:19 +01:00
Tom Hvitved
37d2224b9d Merge pull request #22082 from hvitved/shared/final-tree-sitter-classes
Shared: Generate `final` tree-sitter classes
2026-06-30 09:09:42 +02:00
Owen Mansel-Chan
0a737c97f3 Expand log.slog models and add more tests 2026-06-30 08:01:06 +01:00
Asger F
28f0be5c67 Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-30 07:17:23 +02:00
Geoffrey White
f353a17431 Merge pull request #22081 from geoffw0/rubyinline2
Ruby: Address testFailures in inline expectations tests (part 2)
2026-06-29 19:37:28 +01:00
Mathias Vorreiter Pedersen
caaed72288 C++: Hide summary nodes that should be hidden and accept test changes. 2026-06-29 18:30:03 +01:00
Mathias Vorreiter Pedersen
08c383df6a C++: Accept test changes. 2026-06-29 18:20:10 +01:00
Mathias Vorreiter Pedersen
2625c304bf C++: Support fully qualified field names in MaD. 2026-06-29 18:02:20 +01:00
Mathias Vorreiter Pedersen
49bde567dd C++: Add tests with qualified names in MaD. 2026-06-29 18:02:17 +01:00
Geoffrey White
d519f79703 Update ruby/ql/lib/utils/test/internal/InlineExpectationsTestImpl.qll
Co-authored-by: Tom Hvitved <hvitved@github.com>
2026-06-29 15:37:45 +01:00
Asger F
12bd3e2860 unified: Bulk migrate all corpus tests to the new system 2026-06-29 15:01:22 +02:00
Asger F
3e1ca82cbf unified: Split corpus tests into source code and generated output
The corpus tests interleaved hand-written content (test cases) with
generated content (printed ASTs).

This made merge conflicts hard to resolve because you can't just
regnerate the printed ASTs without potentially throwing away new test
cases that came from either branch (or depending on whether the merge
conflict markers appeared, the corpus test could be ruined completely).

The old design did have one nice advantage: Reviewers could see the
printed ASTs alongside the source code from which it was generated.

To preserve this feature, the source code for the test case is itself
included in the generated output file.
2026-06-29 15:01:20 +02:00
Taus
f1cc1e5c47 Merge pull request #22084 from github/tausbn/yeast-miscellaneous-cleanup
yeast: Miscellaneous cleanup
2026-06-29 14:14:24 +02:00
Tom Hvitved
f14a5678be Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-29 13:32:14 +02:00
copilot-swe-agent[bot]
041a8e6adc Fix source_text call in @@raw_lhs documentation example 2026-06-29 11:26:07 +00:00
Taus
fb424020af yeast: Delete the Cursor trait, inline its methods on AstCursor
The trait had a single implementor (`AstCursor`), three type parameters
of which one (`T`) was never used in any method signature, and one
external consumer that needed `use yeast::Cursor;` in scope just to
call methods on the cursor. The abstraction was overhead without a
second implementor to justify it.

Move the six trait methods to an inherent `impl AstCursor` block;
delete `shared/yeast/src/cursor.rs`, the `pub mod cursor;` and
`pub use cursor::Cursor;` lines in `lib.rs`, and the `use yeast::Cursor;`
in `tree-sitter-extractor`'s `traverse_yeast`.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-29 10:34:36 +00:00
Taus
bda8e7dae1 yeast-macros: Remove unused .map and .reduce_left chain syntax
The `{expr}.map(p -> tpl)` and `{expr}.reduce_left(first -> init, acc,
elem -> fold)` post-fix chains on `{expr}` placeholders had no
remaining users in the codebase: `.map` was never used, and the
4 `.reduce_left` sites in `swift.rs` were rewritten to plain
`Iterator::reduce` via an `and_chain` helper in an earlier commit.

Removes the entire `parse_chain_suffix` function (~90 lines) and the
`has_chain` detection / dispatch branches at the two call sites
(field-position in `parse_direct_node_inner` and body-position in
`parse_direct_list`). The remaining `{expr}` path is the
trait-dispatched one introduced by the splice-syntax cleanup, which
handles single ids and iterables uniformly via `IntoFieldIds`.

Also strips the chain syntax from the `tree!` macro doc comment.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-29 10:34:36 +00:00
Taus
37c8111c18 yeast-macros: Add error message to defensive expect_ident in parse_ctx_or_implicit
The empty error string passed to `expect_ident` was dead code (the
preceding lookahead has already confirmed the token is an ident),
but it would have been a confusing message if it ever fired. Replace
with an explicit "unreachable" string that makes the intent
clearer to readers.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-29 10:34:36 +00:00
Taus
807bb51df7 yeast: Unify Node::kind() and Node::kind_name()
Both accessors returned the same private `kind_name: &'static str`
field; `kind_name()` is widely used (mainly by dump.rs and schema
diagnostics) and `kind()` had only 2 internal callers in lib.rs and
a handful in tests. Pick the more descriptive name and update the
callers.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-29 10:34:36 +00:00
Taus
b6abfe6e5c yeast: Remove dead prepend_field / prepend_field_child
`BuildCtx::prepend_field` and the underlying `Ast::prepend_field_child`
existed to support the create-then-mutate pattern in swift.rs (build
an output node, then prepend modifiers to its `modifier:` field). The
SwiftContext-based refactor on the previous branches eliminated all
such call sites: every emitted declaration now carries its modifiers
from birth, so the in-place prepend operation has no users.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-29 10:34:35 +00:00
Taus
b3dc7009a4 yeast: Remove dead BuildCtx::translate_opt
`translate_opt` was a convenience for the manual_rule! body code,
collapsing `Option<I>` to `Option<Id>` via `translate`. Since the
`@@` raw-capture migration replaced manual_rule! with rule!, no
callers remain — the auto-translate prefix handles `Option<Id>`
captures directly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-29 10:34:35 +00:00
Taus
e59f646870 yeast: Remove dead Captures methods
`Captures::map_captures`, `Captures::map_captures_to`, and
`Captures::try_map_all_captures` had no callers. The last one was
subsumed by `try_map_captures_except` (which takes a skip list and
degenerates to the old behaviour when the list is empty).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-29 10:34:35 +00:00
Taus
cc3c232631 yeast: Replace {..expr} splice syntax with trait-dispatched {expr}
In the initial implementation of yeast, the splice syntax was needed do
distinguish between splicing multiple nodes or just a single node.
However, this was always an ugly "wart" in the syntax, since the user
shouldn't have to worry about these things.

To fix this, we add an `IntoFieldIds` trait that dispatches on the
value's type: `Id` pushes a single id, and a blanket impl for
`IntoIterator<Item: Into<Id>>` handles `Vec<Id>`, `Option<Id>`, and
arbitrary iterator chains.

With this, we no longer need to use the special splice syntax, and hence
we can get rid of it.
2026-06-29 10:34:35 +00:00
Taus
9a5cc3c5e3 yeast: Make Id a newtype, delete NodeRef
Previously, the `Id` type  was a bare usize alias. The `NodeRef` newtype
existed solely to carry the AST-aware `YeastDisplay` /
`YeastSourceRange` impls (so that `#{captured_node}` rendered source
text rather than the numeric id) without colliding with the impls for
raw integer types.

This commit promotes `Id` itself to a (transparent) newtype struct and
moves the AST-aware trait impls directly onto it. With `Id` and `usize`
now being different types, the integer-display impl (for `usize`) and
the source-text impl (for `Id`) coexist without conflict, and `NodeRef`
becomes redundant (and so we remove it).
2026-06-29 10:33:32 +00:00
Geoffrey White
72f1a0d89b Ruby: Clean up the CodeQL a little more. 2026-06-29 11:22:02 +01:00
Geoffrey White
96e88a1f9a Ruby: Inline AnyComment class into ExpectationComment. 2026-06-29 11:21:42 +01:00
Tom Hvitved
d985c48e84 Unified: Regenerate Ast.qll 2026-06-29 12:06:09 +02:00
Tom Hvitved
330bb17d69 QL4QL: Regenerate TreeSitter.qll 2026-06-29 12:05:42 +02:00
Tom Hvitved
818a25b64e Ruby: Regenerate TreeSitter.qll 2026-06-29 12:05:41 +02:00
Tom Hvitved
4237a76251 Shared: Generate final tree-sitter classes 2026-06-29 12:05:39 +02:00
Geoffrey White
727f7d2afa Fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-29 10:58:45 +01:00
Geoffrey White
3c5f70de11 Ruby: And another missing tag. 2026-06-29 10:37:21 +01:00
Geoffrey White
c0c8958db1 Ruby: Implement inline expectation comments for .erb files. 2026-06-26 19:14:03 +01:00
Geoffrey White
0ee40417ea Ruby: Add inline expectation comment to .erb file. 2026-06-26 19:14:01 +01:00
Geoffrey White
897d16929b Java: Add missing $ Source annotations. 2026-06-26 16:22:05 +01:00
Geoffrey White
6f997ae15c Java: Label spurious results. 2026-06-26 16:22:03 +01:00
Geoffrey White
300e48e48e Java: Move $ Source annotations that were incorrectly placed. 2026-06-26 16:21:49 +01:00
Geoffrey White
f840f6104a Java: Make some $ Source annotations query specific. 2026-06-26 16:21:46 +01:00
Michael Nebel
18913ce4b8 C#: Add change-note. 2026-06-25 11:50:49 +02:00
Michael Nebel
a45ef5845a C#: Address review comments. 2026-06-25 11:50:47 +02:00
Michael Nebel
d32c4d838d C#: Make the NuGetExeWrapper respect the CheckFeeds flag, private registries configuration and provide sources via the command line instead of creating a file. 2026-06-25 11:50:44 +02:00
Michael Nebel
8042fba94a C#: Inject the feed manager into the NugetExeWrapper. 2026-06-25 11:50:42 +02:00
Michael Nebel
bbad4f6069 C#: Take a the feed logic out of the try/catch for NuGet downloading. 2026-06-25 11:50:40 +02:00
577 changed files with 13058 additions and 18086 deletions

View File

@@ -0,0 +1,4 @@
---
category: deprecated
---
* Models-as-data flow summaries now use fully qualified field names (for example, `MyNamespace::MyStruct::myField`) instead of unqualified field names such as `myField`. We recommend updating existing flow summaries to use fully qualified field names. Unqualified field names are still supported, but that support will be removed in a future release.

View File

@@ -40,12 +40,24 @@ module Input implements InputSig<Location, DataFlowImplSpecific::CppDataFlow> {
arg = repeatStars(rk.(NormalReturnKind).getIndirectionIndex()) arg = repeatStars(rk.(NormalReturnKind).getIndirectionIndex())
} }
bindingset[namespace, type, base]
private string formatQualifiedName(string namespace, string type, string base) {
if namespace = ""
then result = type + "::" + base
else result = namespace + "::" + type + "::" + base
}
string encodeContent(ContentSet cs, string arg) { string encodeContent(ContentSet cs, string arg) {
exists(FieldContent c | exists(FieldContent c, string namespace, string type, string base |
cs.isSingleton(c) and cs.isSingleton(c) and
// FieldContent indices have 0 for the address, 1 for content, so we need to subtract one. // FieldContent indices have 0 for the address, 1 for content, so we need to subtract one.
result = "Field" and result = "Field" and
arg = repeatStars(c.getIndirectionIndex() - 1) + c.getField().getName() c.getField().hasQualifiedName(namespace, type, base)
|
arg = repeatStars(c.getIndirectionIndex() - 1) + formatQualifiedName(namespace, type, base)
or
// TODO: This disjunct can be removed once we stop supporting unqualified field names.
arg = repeatStars(c.getIndirectionIndex() - 1) + base
) )
or or
exists(ElementContent ec | exists(ElementContent ec |

View File

@@ -1378,6 +1378,8 @@ predicate nodeIsHidden(Node n) {
n instanceof InitialGlobalValue n instanceof InitialGlobalValue
or or
n instanceof SsaSynthNode n instanceof SsaSynthNode
or
n.(FlowSummaryNode).getSummaryNode().isHidden()
} }
predicate neverSkipInPathGraph(Node n) { predicate neverSkipInPathGraph(Node n) {

View File

@@ -48,19 +48,20 @@ models
| 47 | Summary: ; ; false; callWithArgument; ; ; Argument[1]; Argument[0].Parameter[0]; value; manual | | 47 | Summary: ; ; false; callWithArgument; ; ; Argument[1]; Argument[0].Parameter[0]; value; manual |
| 48 | Summary: ; ; false; callWithNonTypeTemplate<T>; (const T &); ; Argument[*0]; ReturnValue; value; manual | | 48 | Summary: ; ; false; callWithNonTypeTemplate<T>; (const T &); ; Argument[*0]; ReturnValue; value; manual |
| 49 | Summary: ; ; false; pthread_create; ; ; Argument[@3]; Argument[2].Parameter[@0]; value; manual | | 49 | Summary: ; ; false; pthread_create; ; ; Argument[@3]; Argument[2].Parameter[@0]; value; manual |
| 50 | Summary: ; ; false; ymlStepGenerated; ; ; Argument[0]; ReturnValue; taint; df-generated | | 50 | Summary: ; ; false; read_field_from_struct; ; ; Argument[*0].Field[MyNamespace::MyStructInNamespace::myField]; ReturnValue; value; manual |
| 51 | Summary: ; ; false; ymlStepManual; ; ; Argument[0]; ReturnValue; taint; manual | | 51 | Summary: ; ; false; read_field_from_struct_2; ; ; Argument[*0].Field[MyGlobalStruct::myField]; ReturnValue; value; manual |
| 52 | Summary: ; ; false; ymlStepManual_with_body; ; ; Argument[0]; ReturnValue; taint; manual | | 52 | Summary: ; ; false; ymlStepGenerated; ; ; Argument[0]; ReturnValue; taint; df-generated |
| 53 | Summary: ; TemplateClass1; true; templateFunction2<U,V>; (U,V); ; Argument[1]; ReturnValue; value; manual | | 53 | Summary: ; ; false; ymlStepManual; ; ; Argument[0]; ReturnValue; taint; manual |
| 54 | Summary: ; TemplateClass1<T>; false; templateFunction<U>; (T,U); ; Argument[0]; ReturnValue; value; manual | | 54 | Summary: ; ; false; ymlStepManual_with_body; ; ; Argument[0]; ReturnValue; taint; manual |
| 55 | Summary: ; TemplateClass2<T,U>; true; function; (U,T); ; Argument[1]; ReturnValue; value; manual | | 55 | Summary: ; TemplateClass1; true; templateFunction2<U,V>; (U,V); ; Argument[1]; ReturnValue; value; manual |
| 56 | Summary: Azure::Core::IO; BodyStream; true; Read; ; ; Argument[-1]; Argument[*0]; taint; manual | | 56 | Summary: ; TemplateClass1<T>; false; templateFunction<U>; (T,U); ; Argument[0]; ReturnValue; value; manual |
| 57 | Summary: Azure::Core::IO; BodyStream; true; ReadToCount; ; ; Argument[-1]; Argument[*0]; taint; manual | | 57 | Summary: ; TemplateClass2<T,U>; true; function; (U,T); ; Argument[1]; ReturnValue; value; manual |
| 58 | Summary: Azure::Core::IO; BodyStream; true; ReadToEnd; ; ; Argument[-1]; ReturnValue.Element; taint; manual | | 58 | Summary: Azure::Core::IO; BodyStream; true; Read; ; ; Argument[-1]; Argument[*0]; taint; manual |
| 59 | Summary: Azure; Nullable; true; Value; ; ; Argument[-1]; ReturnValue[*]; taint; manual | | 59 | Summary: Azure::Core::IO; BodyStream; true; ReadToCount; ; ; Argument[-1]; Argument[*0]; taint; manual |
| 60 | Summary: boost::asio; ; false; buffer; ; ; Argument[*0]; ReturnValue; taint; manual | | 60 | Summary: Azure::Core::IO; BodyStream; true; ReadToEnd; ; ; Argument[-1]; ReturnValue.Element; taint; manual |
| 61 | Summary: Azure; Nullable; true; Value; ; ; Argument[-1]; ReturnValue[*]; taint; manual |
| 62 | Summary: boost::asio; ; false; buffer; ; ; Argument[*0]; ReturnValue; taint; manual |
edges edges
| asio_streams.cpp:56:18:56:23 | [summary param] *0 in buffer | asio_streams.cpp:56:18:56:23 | [summary] to write: ReturnValue in buffer | provenance | MaD:60 |
| asio_streams.cpp:87:34:87:44 | read_until output argument | asio_streams.cpp:91:7:91:17 | recv_buffer | provenance | Src:MaD:32 | | asio_streams.cpp:87:34:87:44 | read_until output argument | asio_streams.cpp:91:7:91:17 | recv_buffer | provenance | Src:MaD:32 |
| asio_streams.cpp:87:34:87:44 | read_until output argument | asio_streams.cpp:93:29:93:39 | *recv_buffer | provenance | Src:MaD:32 Sink:MaD:2 | | asio_streams.cpp:87:34:87:44 | read_until output argument | asio_streams.cpp:93:29:93:39 | *recv_buffer | provenance | Src:MaD:32 Sink:MaD:2 |
| asio_streams.cpp:97:37:97:44 | call to source | asio_streams.cpp:98:7:98:14 | send_str | provenance | TaintFunction | | asio_streams.cpp:97:37:97:44 | call to source | asio_streams.cpp:98:7:98:14 | send_str | provenance | TaintFunction |
@@ -68,25 +69,16 @@ edges
| asio_streams.cpp:100:44:100:62 | call to buffer | asio_streams.cpp:100:44:100:62 | call to buffer | provenance | | | asio_streams.cpp:100:44:100:62 | call to buffer | asio_streams.cpp:100:44:100:62 | call to buffer | provenance | |
| asio_streams.cpp:100:44:100:62 | call to buffer | asio_streams.cpp:101:7:101:17 | send_buffer | provenance | | | asio_streams.cpp:100:44:100:62 | call to buffer | asio_streams.cpp:101:7:101:17 | send_buffer | provenance | |
| asio_streams.cpp:100:44:100:62 | call to buffer | asio_streams.cpp:103:29:103:39 | *send_buffer | provenance | Sink:MaD:2 | | asio_streams.cpp:100:44:100:62 | call to buffer | asio_streams.cpp:103:29:103:39 | *send_buffer | provenance | Sink:MaD:2 |
| asio_streams.cpp:100:64:100:71 | *send_str | asio_streams.cpp:56:18:56:23 | [summary param] *0 in buffer | provenance | | | asio_streams.cpp:100:64:100:71 | *send_str | asio_streams.cpp:100:44:100:62 | call to buffer | provenance | MaD:62 |
| asio_streams.cpp:100:64:100:71 | *send_str | asio_streams.cpp:100:44:100:62 | call to buffer | provenance | MaD:60 |
| azure.cpp:62:10:62:14 | [summary param] this in Value | azure.cpp:62:10:62:14 | [summary] to write: ReturnValue[*] in Value | provenance | MaD:59 |
| azure.cpp:113:16:113:19 | [summary param] this in Read | azure.cpp:113:16:113:19 | [summary param] *0 in Read [Return] | provenance | MaD:56 |
| azure.cpp:114:16:114:26 | [summary param] this in ReadToCount | azure.cpp:114:16:114:26 | [summary param] *0 in ReadToCount [Return] | provenance | MaD:57 |
| azure.cpp:115:30:115:38 | [summary param] this in ReadToEnd | azure.cpp:115:30:115:38 | [summary] to write: ReturnValue.Element in ReadToEnd | provenance | MaD:58 |
| azure.cpp:115:30:115:38 | [summary] to write: ReturnValue.Element in ReadToEnd | azure.cpp:115:30:115:38 | [summary] to write: ReturnValue in ReadToEnd [element] | provenance | |
| azure.cpp:253:48:253:60 | *call to GetBodyStream | azure.cpp:253:48:253:60 | *call to GetBodyStream | provenance | Src:MaD:29 | | azure.cpp:253:48:253:60 | *call to GetBodyStream | azure.cpp:253:48:253:60 | *call to GetBodyStream | provenance | Src:MaD:29 |
| azure.cpp:253:48:253:60 | *call to GetBodyStream | azure.cpp:257:5:257:8 | *resp | provenance | | | azure.cpp:253:48:253:60 | *call to GetBodyStream | azure.cpp:257:5:257:8 | *resp | provenance | |
| azure.cpp:253:48:253:60 | *call to GetBodyStream | azure.cpp:262:5:262:8 | *resp | provenance | | | azure.cpp:253:48:253:60 | *call to GetBodyStream | azure.cpp:262:5:262:8 | *resp | provenance | |
| azure.cpp:253:48:253:60 | *call to GetBodyStream | azure.cpp:266:38:266:41 | *resp | provenance | | | azure.cpp:253:48:253:60 | *call to GetBodyStream | azure.cpp:266:38:266:41 | *resp | provenance | |
| azure.cpp:257:5:257:8 | *resp | azure.cpp:113:16:113:19 | [summary param] this in Read | provenance | | | azure.cpp:257:5:257:8 | *resp | azure.cpp:257:16:257:21 | Read output argument | provenance | MaD:58 |
| azure.cpp:257:5:257:8 | *resp | azure.cpp:257:16:257:21 | Read output argument | provenance | MaD:56 |
| azure.cpp:257:16:257:21 | Read output argument | azure.cpp:258:10:258:16 | * ... | provenance | | | azure.cpp:257:16:257:21 | Read output argument | azure.cpp:258:10:258:16 | * ... | provenance | |
| azure.cpp:262:5:262:8 | *resp | azure.cpp:114:16:114:26 | [summary param] this in ReadToCount | provenance | | | azure.cpp:262:5:262:8 | *resp | azure.cpp:262:23:262:28 | ReadToCount output argument | provenance | MaD:59 |
| azure.cpp:262:5:262:8 | *resp | azure.cpp:262:23:262:28 | ReadToCount output argument | provenance | MaD:57 |
| azure.cpp:262:23:262:28 | ReadToCount output argument | azure.cpp:263:10:263:16 | * ... | provenance | | | azure.cpp:262:23:262:28 | ReadToCount output argument | azure.cpp:263:10:263:16 | * ... | provenance | |
| azure.cpp:266:38:266:41 | *resp | azure.cpp:115:30:115:38 | [summary param] this in ReadToEnd | provenance | | | azure.cpp:266:38:266:41 | *resp | azure.cpp:266:44:266:52 | call to ReadToEnd [element] | provenance | MaD:60 |
| azure.cpp:266:38:266:41 | *resp | azure.cpp:266:44:266:52 | call to ReadToEnd [element] | provenance | MaD:58 |
| azure.cpp:266:44:266:52 | call to ReadToEnd [element] | azure.cpp:266:44:266:52 | call to ReadToEnd [element] | provenance | | | azure.cpp:266:44:266:52 | call to ReadToEnd [element] | azure.cpp:266:44:266:52 | call to ReadToEnd [element] | provenance | |
| azure.cpp:266:44:266:52 | call to ReadToEnd [element] | azure.cpp:267:10:267:12 | vec [element] | provenance | | | azure.cpp:266:44:266:52 | call to ReadToEnd [element] | azure.cpp:267:10:267:12 | vec [element] | provenance | |
| azure.cpp:267:10:267:12 | vec [element] | azure.cpp:267:10:267:12 | vec | provenance | | | azure.cpp:267:10:267:12 | vec [element] | azure.cpp:267:10:267:12 | vec | provenance | |
@@ -102,12 +94,10 @@ edges
| azure.cpp:278:10:278:13 | body | azure.cpp:278:10:278:13 | body | provenance | | | azure.cpp:278:10:278:13 | body | azure.cpp:278:10:278:13 | body | provenance | |
| azure.cpp:281:68:281:84 | *call to ExtractBodyStream | azure.cpp:281:68:281:84 | *call to ExtractBodyStream | provenance | Src:MaD:26 | | azure.cpp:281:68:281:84 | *call to ExtractBodyStream | azure.cpp:281:68:281:84 | *call to ExtractBodyStream | provenance | Src:MaD:26 |
| azure.cpp:281:68:281:84 | *call to ExtractBodyStream | azure.cpp:282:21:282:23 | *call to get | provenance | | | azure.cpp:281:68:281:84 | *call to ExtractBodyStream | azure.cpp:282:21:282:23 | *call to get | provenance | |
| azure.cpp:282:21:282:23 | *call to get | azure.cpp:115:30:115:38 | [summary param] this in ReadToEnd | provenance | | | azure.cpp:282:21:282:23 | *call to get | azure.cpp:282:28:282:36 | call to ReadToEnd [element] | provenance | MaD:60 |
| azure.cpp:282:21:282:23 | *call to get | azure.cpp:282:28:282:36 | call to ReadToEnd [element] | provenance | MaD:58 |
| azure.cpp:282:28:282:36 | call to ReadToEnd [element] | azure.cpp:282:10:282:38 | call to ReadToEnd | provenance | | | azure.cpp:282:28:282:36 | call to ReadToEnd [element] | azure.cpp:282:10:282:38 | call to ReadToEnd | provenance | |
| azure.cpp:282:28:282:36 | call to ReadToEnd [element] | azure.cpp:282:28:282:36 | call to ReadToEnd [element] | provenance | | | azure.cpp:282:28:282:36 | call to ReadToEnd [element] | azure.cpp:282:28:282:36 | call to ReadToEnd [element] | provenance | |
| azure.cpp:289:24:289:56 | call to GetHeader | azure.cpp:62:10:62:14 | [summary param] this in Value | provenance | | | azure.cpp:289:24:289:56 | call to GetHeader | azure.cpp:289:63:289:65 | call to Value | provenance | MaD:61 |
| azure.cpp:289:24:289:56 | call to GetHeader | azure.cpp:289:63:289:65 | call to Value | provenance | MaD:59 |
| azure.cpp:289:32:289:40 | call to GetHeader | azure.cpp:289:24:289:56 | call to GetHeader | provenance | | | azure.cpp:289:32:289:40 | call to GetHeader | azure.cpp:289:24:289:56 | call to GetHeader | provenance | |
| azure.cpp:289:32:289:40 | call to GetHeader | azure.cpp:289:32:289:40 | call to GetHeader | provenance | Src:MaD:30 | | azure.cpp:289:32:289:40 | call to GetHeader | azure.cpp:289:32:289:40 | call to GetHeader | provenance | Src:MaD:30 |
| azure.cpp:289:63:289:65 | call to Value | azure.cpp:289:63:289:65 | call to Value | provenance | | | azure.cpp:289:63:289:65 | call to Value | azure.cpp:289:63:289:65 | call to Value | provenance | |
@@ -119,9 +109,6 @@ edges
| azure.cpp:294:38:294:53 | call to operator[] | azure.cpp:295:10:295:20 | contentType | provenance | | | azure.cpp:294:38:294:53 | call to operator[] | azure.cpp:295:10:295:20 | contentType | provenance | |
| azure.cpp:294:38:294:53 | call to operator[] | azure.cpp:295:10:295:20 | contentType | provenance | | | azure.cpp:294:38:294:53 | call to operator[] | azure.cpp:295:10:295:20 | contentType | provenance | |
| azure.cpp:295:10:295:20 | contentType | azure.cpp:295:10:295:20 | contentType | provenance | | | azure.cpp:295:10:295:20 | contentType | azure.cpp:295:10:295:20 | contentType | provenance | |
| test.cpp:4:5:4:17 | [summary param] 0 in ymlStepManual | test.cpp:4:5:4:17 | [summary] to write: ReturnValue in ymlStepManual | provenance | MaD:51 |
| test.cpp:5:5:5:20 | [summary param] 0 in ymlStepGenerated | test.cpp:5:5:5:20 | [summary] to write: ReturnValue in ymlStepGenerated | provenance | MaD:50 |
| test.cpp:6:5:6:27 | [summary param] 0 in ymlStepManual_with_body | test.cpp:6:5:6:27 | [summary] to write: ReturnValue in ymlStepManual_with_body | provenance | MaD:52 |
| test.cpp:7:47:7:52 | value2 | test.cpp:7:64:7:69 | value2 | provenance | | | test.cpp:7:47:7:52 | value2 | test.cpp:7:64:7:69 | value2 | provenance | |
| test.cpp:7:64:7:69 | value2 | test.cpp:7:5:7:30 | *ymlStepGenerated_with_body | provenance | | | test.cpp:7:64:7:69 | value2 | test.cpp:7:5:7:30 | *ymlStepGenerated_with_body | provenance | |
| test.cpp:10:10:10:18 | call to ymlSource | test.cpp:10:10:10:18 | call to ymlSource | provenance | Src:MaD:25 | | test.cpp:10:10:10:18 | call to ymlSource | test.cpp:10:10:10:18 | call to ymlSource | provenance | Src:MaD:25 |
@@ -132,16 +119,13 @@ edges
| test.cpp:10:10:10:18 | call to ymlSource | test.cpp:32:41:32:41 | x | provenance | | | test.cpp:10:10:10:18 | call to ymlSource | test.cpp:32:41:32:41 | x | provenance | |
| test.cpp:17:10:17:22 | call to ymlStepManual | test.cpp:17:10:17:22 | call to ymlStepManual | provenance | | | test.cpp:17:10:17:22 | call to ymlStepManual | test.cpp:17:10:17:22 | call to ymlStepManual | provenance | |
| test.cpp:17:10:17:22 | call to ymlStepManual | test.cpp:18:10:18:10 | y | provenance | Sink:MaD:1 | | test.cpp:17:10:17:22 | call to ymlStepManual | test.cpp:18:10:18:10 | y | provenance | Sink:MaD:1 |
| test.cpp:17:24:17:24 | x | test.cpp:4:5:4:17 | [summary param] 0 in ymlStepManual | provenance | | | test.cpp:17:24:17:24 | x | test.cpp:17:10:17:22 | call to ymlStepManual | provenance | MaD:53 |
| test.cpp:17:24:17:24 | x | test.cpp:17:10:17:22 | call to ymlStepManual | provenance | MaD:51 |
| test.cpp:21:10:21:25 | call to ymlStepGenerated | test.cpp:21:10:21:25 | call to ymlStepGenerated | provenance | | | test.cpp:21:10:21:25 | call to ymlStepGenerated | test.cpp:21:10:21:25 | call to ymlStepGenerated | provenance | |
| test.cpp:21:10:21:25 | call to ymlStepGenerated | test.cpp:22:10:22:10 | z | provenance | Sink:MaD:1 | | test.cpp:21:10:21:25 | call to ymlStepGenerated | test.cpp:22:10:22:10 | z | provenance | Sink:MaD:1 |
| test.cpp:21:27:21:27 | x | test.cpp:5:5:5:20 | [summary param] 0 in ymlStepGenerated | provenance | | | test.cpp:21:27:21:27 | x | test.cpp:21:10:21:25 | call to ymlStepGenerated | provenance | MaD:52 |
| test.cpp:21:27:21:27 | x | test.cpp:21:10:21:25 | call to ymlStepGenerated | provenance | MaD:50 |
| test.cpp:25:11:25:33 | call to ymlStepManual_with_body | test.cpp:25:11:25:33 | call to ymlStepManual_with_body | provenance | | | test.cpp:25:11:25:33 | call to ymlStepManual_with_body | test.cpp:25:11:25:33 | call to ymlStepManual_with_body | provenance | |
| test.cpp:25:11:25:33 | call to ymlStepManual_with_body | test.cpp:26:10:26:11 | y2 | provenance | Sink:MaD:1 | | test.cpp:25:11:25:33 | call to ymlStepManual_with_body | test.cpp:26:10:26:11 | y2 | provenance | Sink:MaD:1 |
| test.cpp:25:35:25:35 | x | test.cpp:6:5:6:27 | [summary param] 0 in ymlStepManual_with_body | provenance | | | test.cpp:25:35:25:35 | x | test.cpp:25:11:25:33 | call to ymlStepManual_with_body | provenance | MaD:54 |
| test.cpp:25:35:25:35 | x | test.cpp:25:11:25:33 | call to ymlStepManual_with_body | provenance | MaD:52 |
| test.cpp:32:11:32:36 | call to ymlStepGenerated_with_body | test.cpp:32:11:32:36 | call to ymlStepGenerated_with_body | provenance | | | test.cpp:32:11:32:36 | call to ymlStepGenerated_with_body | test.cpp:32:11:32:36 | call to ymlStepGenerated_with_body | provenance | |
| test.cpp:32:11:32:36 | call to ymlStepGenerated_with_body | test.cpp:33:10:33:11 | z2 | provenance | Sink:MaD:1 | | test.cpp:32:11:32:36 | call to ymlStepGenerated_with_body | test.cpp:33:10:33:11 | z2 | provenance | Sink:MaD:1 |
| test.cpp:32:41:32:41 | x | test.cpp:7:47:7:52 | value2 | provenance | | | test.cpp:32:41:32:41 | x | test.cpp:7:47:7:52 | value2 | provenance | |
@@ -149,20 +133,10 @@ edges
| test.cpp:46:30:46:32 | *arg [x] | test.cpp:47:12:47:19 | *arg [x] | provenance | | | test.cpp:46:30:46:32 | *arg [x] | test.cpp:47:12:47:19 | *arg [x] | provenance | |
| test.cpp:47:12:47:19 | *arg [x] | test.cpp:48:13:48:13 | *s [x] | provenance | | | test.cpp:47:12:47:19 | *arg [x] | test.cpp:48:13:48:13 | *s [x] | provenance | |
| test.cpp:48:13:48:13 | *s [x] | test.cpp:48:16:48:16 | x | provenance | Sink:MaD:1 | | test.cpp:48:13:48:13 | *s [x] | test.cpp:48:16:48:16 | x | provenance | Sink:MaD:1 |
| test.cpp:52:5:52:18 | [summary param] *3 in pthread_create [x] | test.cpp:52:5:52:18 | [summary] to write: Argument[2].Parameter[*0] in pthread_create [x] | provenance | MaD:49 |
| test.cpp:52:5:52:18 | [summary] to write: Argument[2].Parameter[*0] in pthread_create [x] | test.cpp:46:30:46:32 | *arg [x] | provenance | |
| test.cpp:56:2:56:2 | *s [post update] [x] | test.cpp:59:55:59:64 | *& ... [x] | provenance | | | test.cpp:56:2:56:2 | *s [post update] [x] | test.cpp:59:55:59:64 | *& ... [x] | provenance | |
| test.cpp:56:2:56:18 | ... = ... | test.cpp:56:2:56:2 | *s [post update] [x] | provenance | | | test.cpp:56:2:56:18 | ... = ... | test.cpp:56:2:56:2 | *s [post update] [x] | provenance | |
| test.cpp:56:8:56:16 | call to ymlSource | test.cpp:56:2:56:18 | ... = ... | provenance | Src:MaD:25 | | test.cpp:56:8:56:16 | call to ymlSource | test.cpp:56:2:56:18 | ... = ... | provenance | Src:MaD:25 |
| test.cpp:59:55:59:64 | *& ... [x] | test.cpp:52:5:52:18 | [summary param] *3 in pthread_create [x] | provenance | | | test.cpp:59:55:59:64 | *& ... [x] | test.cpp:46:30:46:32 | *arg [x] | provenance | MaD:49 |
| test.cpp:63:6:63:21 | [summary param] 1 in callWithArgument | test.cpp:63:6:63:21 | [summary] to write: Argument[0].Parameter[0] in callWithArgument | provenance | MaD:47 |
| test.cpp:63:6:63:21 | [summary param] 1 in callWithArgument | test.cpp:63:6:63:21 | [summary] to write: Argument[0].Parameter[0] in callWithArgument | provenance | MaD:47 |
| test.cpp:63:6:63:21 | [summary param] 1 in callWithArgument | test.cpp:63:6:63:21 | [summary] to write: Argument[0].Parameter[0] in callWithArgument | provenance | MaD:47 |
| test.cpp:63:6:63:21 | [summary param] 1 in callWithArgument | test.cpp:63:6:63:21 | [summary] to write: Argument[0].Parameter[0] in callWithArgument | provenance | MaD:47 |
| test.cpp:63:6:63:21 | [summary] to write: Argument[0].Parameter[0] in callWithArgument | test.cpp:68:22:68:22 | y | provenance | |
| test.cpp:63:6:63:21 | [summary] to write: Argument[0].Parameter[0] in callWithArgument | test.cpp:74:22:74:22 | y | provenance | |
| test.cpp:63:6:63:21 | [summary] to write: Argument[0].Parameter[0] in callWithArgument | test.cpp:82:22:82:22 | y | provenance | |
| test.cpp:63:6:63:21 | [summary] to write: Argument[0].Parameter[0] in callWithArgument | test.cpp:88:22:88:22 | y | provenance | |
| test.cpp:68:22:68:22 | y | test.cpp:69:11:69:11 | y | provenance | Sink:MaD:1 | | test.cpp:68:22:68:22 | y | test.cpp:69:11:69:11 | y | provenance | Sink:MaD:1 |
| test.cpp:74:22:74:22 | y | test.cpp:75:11:75:11 | y | provenance | Sink:MaD:1 | | test.cpp:74:22:74:22 | y | test.cpp:75:11:75:11 | y | provenance | Sink:MaD:1 |
| test.cpp:82:22:82:22 | y | test.cpp:83:11:83:11 | y | provenance | Sink:MaD:1 | | test.cpp:82:22:82:22 | y | test.cpp:83:11:83:11 | y | provenance | Sink:MaD:1 |
@@ -172,69 +146,61 @@ edges
| test.cpp:94:10:94:18 | call to ymlSource | test.cpp:101:26:101:26 | x | provenance | | | test.cpp:94:10:94:18 | call to ymlSource | test.cpp:101:26:101:26 | x | provenance | |
| test.cpp:94:10:94:18 | call to ymlSource | test.cpp:103:63:103:63 | x | provenance | | | test.cpp:94:10:94:18 | call to ymlSource | test.cpp:103:63:103:63 | x | provenance | |
| test.cpp:94:10:94:18 | call to ymlSource | test.cpp:104:62:104:62 | x | provenance | | | test.cpp:94:10:94:18 | call to ymlSource | test.cpp:104:62:104:62 | x | provenance | |
| test.cpp:97:26:97:26 | x | test.cpp:63:6:63:21 | [summary param] 1 in callWithArgument | provenance | | | test.cpp:97:26:97:26 | x | test.cpp:68:22:68:22 | y | provenance | MaD:47 |
| test.cpp:101:26:101:26 | x | test.cpp:63:6:63:21 | [summary param] 1 in callWithArgument | provenance | | | test.cpp:101:26:101:26 | x | test.cpp:74:22:74:22 | y | provenance | MaD:47 |
| test.cpp:103:63:103:63 | x | test.cpp:63:6:63:21 | [summary param] 1 in callWithArgument | provenance | | | test.cpp:103:63:103:63 | x | test.cpp:82:22:82:22 | y | provenance | MaD:47 |
| test.cpp:104:62:104:62 | x | test.cpp:63:6:63:21 | [summary param] 1 in callWithArgument | provenance | | | test.cpp:104:62:104:62 | x | test.cpp:88:22:88:22 | y | provenance | MaD:47 |
| test.cpp:111:3:111:25 | [summary param] *0 in callWithNonTypeTemplate | test.cpp:111:3:111:25 | [summary] to write: ReturnValue in callWithNonTypeTemplate | provenance | MaD:48 |
| test.cpp:114:10:114:18 | call to ymlSource | test.cpp:114:10:114:18 | call to ymlSource | provenance | Src:MaD:25 | | test.cpp:114:10:114:18 | call to ymlSource | test.cpp:114:10:114:18 | call to ymlSource | provenance | Src:MaD:25 |
| test.cpp:114:10:114:18 | call to ymlSource | test.cpp:118:44:118:44 | *x | provenance | | | test.cpp:114:10:114:18 | call to ymlSource | test.cpp:118:44:118:44 | *x | provenance | |
| test.cpp:118:11:118:42 | call to callWithNonTypeTemplate | test.cpp:118:11:118:42 | call to callWithNonTypeTemplate | provenance | | | test.cpp:118:11:118:42 | call to callWithNonTypeTemplate | test.cpp:118:11:118:42 | call to callWithNonTypeTemplate | provenance | |
| test.cpp:118:11:118:42 | call to callWithNonTypeTemplate | test.cpp:119:10:119:11 | y2 | provenance | Sink:MaD:1 | | test.cpp:118:11:118:42 | call to callWithNonTypeTemplate | test.cpp:119:10:119:11 | y2 | provenance | Sink:MaD:1 |
| test.cpp:118:44:118:44 | *x | test.cpp:111:3:111:25 | [summary param] *0 in callWithNonTypeTemplate | provenance | |
| test.cpp:118:44:118:44 | *x | test.cpp:118:11:118:42 | call to callWithNonTypeTemplate | provenance | MaD:48 | | test.cpp:118:44:118:44 | *x | test.cpp:118:11:118:42 | call to callWithNonTypeTemplate | provenance | MaD:48 |
| test.cpp:125:5:125:20 | [summary param] 0 in templateFunction | test.cpp:125:5:125:20 | [summary] to write: ReturnValue in templateFunction | provenance | MaD:54 |
| test.cpp:128:5:128:21 | [summary param] 1 in templateFunction2 | test.cpp:128:5:128:21 | [summary] to write: ReturnValue in templateFunction2 | provenance | MaD:53 |
| test.cpp:133:10:133:18 | call to ymlSource | test.cpp:133:10:133:18 | call to ymlSource | provenance | Src:MaD:25 | | test.cpp:133:10:133:18 | call to ymlSource | test.cpp:133:10:133:18 | call to ymlSource | provenance | Src:MaD:25 |
| test.cpp:133:10:133:18 | call to ymlSource | test.cpp:134:45:134:45 | x | provenance | | | test.cpp:133:10:133:18 | call to ymlSource | test.cpp:134:45:134:45 | x | provenance | |
| test.cpp:134:13:134:43 | call to templateFunction | test.cpp:134:13:134:43 | call to templateFunction | provenance | | | test.cpp:134:13:134:43 | call to templateFunction | test.cpp:134:13:134:43 | call to templateFunction | provenance | |
| test.cpp:134:13:134:43 | call to templateFunction | test.cpp:135:10:135:10 | y | provenance | Sink:MaD:1 | | test.cpp:134:13:134:43 | call to templateFunction | test.cpp:135:10:135:10 | y | provenance | Sink:MaD:1 |
| test.cpp:134:45:134:45 | x | test.cpp:125:5:125:20 | [summary param] 0 in templateFunction | provenance | | | test.cpp:134:45:134:45 | x | test.cpp:134:13:134:43 | call to templateFunction | provenance | MaD:56 |
| test.cpp:134:45:134:45 | x | test.cpp:134:13:134:43 | call to templateFunction | provenance | MaD:54 |
| test.cpp:140:4:140:11 | [summary param] 1 in function | test.cpp:140:4:140:11 | [summary] to write: ReturnValue in function | provenance | MaD:55 |
| test.cpp:140:4:140:11 | [summary param] 1 in function | test.cpp:140:4:140:11 | [summary] to write: ReturnValue in function | provenance | MaD:55 |
| test.cpp:146:10:146:18 | call to ymlSource | test.cpp:146:10:146:18 | call to ymlSource | provenance | Src:MaD:25 | | test.cpp:146:10:146:18 | call to ymlSource | test.cpp:146:10:146:18 | call to ymlSource | provenance | Src:MaD:25 |
| test.cpp:146:10:146:18 | call to ymlSource | test.cpp:148:26:148:26 | x | provenance | | | test.cpp:146:10:146:18 | call to ymlSource | test.cpp:148:26:148:26 | x | provenance | |
| test.cpp:148:10:148:27 | call to function | test.cpp:148:10:148:27 | call to function | provenance | | | test.cpp:148:10:148:27 | call to function | test.cpp:148:10:148:27 | call to function | provenance | |
| test.cpp:148:10:148:27 | call to function | test.cpp:149:10:149:10 | z | provenance | Sink:MaD:1 | | test.cpp:148:10:148:27 | call to function | test.cpp:149:10:149:10 | z | provenance | Sink:MaD:1 |
| test.cpp:148:26:148:26 | x | test.cpp:140:4:140:11 | [summary param] 1 in function | provenance | | | test.cpp:148:26:148:26 | x | test.cpp:148:10:148:27 | call to function | provenance | MaD:57 |
| test.cpp:148:26:148:26 | x | test.cpp:148:10:148:27 | call to function | provenance | MaD:55 |
| test.cpp:155:10:155:18 | call to ymlSource | test.cpp:155:10:155:18 | call to ymlSource | provenance | Src:MaD:25 | | test.cpp:155:10:155:18 | call to ymlSource | test.cpp:155:10:155:18 | call to ymlSource | provenance | Src:MaD:25 |
| test.cpp:155:10:155:18 | call to ymlSource | test.cpp:157:26:157:26 | x | provenance | | | test.cpp:155:10:155:18 | call to ymlSource | test.cpp:157:26:157:26 | x | provenance | |
| test.cpp:157:13:157:20 | call to function | test.cpp:157:13:157:20 | call to function | provenance | | | test.cpp:157:13:157:20 | call to function | test.cpp:157:13:157:20 | call to function | provenance | |
| test.cpp:157:13:157:20 | call to function | test.cpp:158:10:158:10 | z | provenance | Sink:MaD:1 | | test.cpp:157:13:157:20 | call to function | test.cpp:158:10:158:10 | z | provenance | Sink:MaD:1 |
| test.cpp:157:26:157:26 | x | test.cpp:140:4:140:11 | [summary param] 1 in function | provenance | | | test.cpp:157:26:157:26 | x | test.cpp:157:13:157:20 | call to function | provenance | MaD:57 |
| test.cpp:157:26:157:26 | x | test.cpp:157:13:157:20 | call to function | provenance | MaD:55 |
| test.cpp:164:34:164:34 | x | test.cpp:165:69:165:69 | x | provenance | | | test.cpp:164:34:164:34 | x | test.cpp:165:69:165:69 | x | provenance | |
| test.cpp:165:12:165:64 | call to templateFunction2 | test.cpp:164:7:164:7 | *templateFunction3 | provenance | | | test.cpp:165:12:165:64 | call to templateFunction2 | test.cpp:164:7:164:7 | *templateFunction3 | provenance | |
| test.cpp:165:12:165:64 | call to templateFunction2 | test.cpp:165:12:165:64 | call to templateFunction2 | provenance | | | test.cpp:165:12:165:64 | call to templateFunction2 | test.cpp:165:12:165:64 | call to templateFunction2 | provenance | |
| test.cpp:165:69:165:69 | x | test.cpp:128:5:128:21 | [summary param] 1 in templateFunction2 | provenance | | | test.cpp:165:69:165:69 | x | test.cpp:165:12:165:64 | call to templateFunction2 | provenance | MaD:55 |
| test.cpp:165:69:165:69 | x | test.cpp:165:12:165:64 | call to templateFunction2 | provenance | MaD:53 |
| test.cpp:170:10:170:18 | call to ymlSource | test.cpp:170:10:170:18 | call to ymlSource | provenance | Src:MaD:25 | | test.cpp:170:10:170:18 | call to ymlSource | test.cpp:170:10:170:18 | call to ymlSource | provenance | Src:MaD:25 |
| test.cpp:170:10:170:18 | call to ymlSource | test.cpp:172:51:172:51 | x | provenance | | | test.cpp:170:10:170:18 | call to ymlSource | test.cpp:172:51:172:51 | x | provenance | |
| test.cpp:172:13:172:44 | call to templateFunction3 | test.cpp:172:13:172:44 | call to templateFunction3 | provenance | | | test.cpp:172:13:172:44 | call to templateFunction3 | test.cpp:172:13:172:44 | call to templateFunction3 | provenance | |
| test.cpp:172:13:172:44 | call to templateFunction3 | test.cpp:173:10:173:10 | y | provenance | Sink:MaD:1 | | test.cpp:172:13:172:44 | call to templateFunction3 | test.cpp:173:10:173:10 | y | provenance | Sink:MaD:1 |
| test.cpp:172:51:172:51 | x | test.cpp:164:34:164:34 | x | provenance | | | test.cpp:172:51:172:51 | x | test.cpp:164:34:164:34 | x | provenance | |
| test.cpp:172:51:172:51 | x | test.cpp:172:13:172:44 | call to templateFunction3 | provenance | MaD:53 | | test.cpp:172:51:172:51 | x | test.cpp:172:13:172:44 | call to templateFunction3 | provenance | MaD:55 |
| windows.cpp:17:8:17:25 | [summary param] *0 in CommandLineToArgvA | windows.cpp:17:8:17:25 | [summary] to write: ReturnValue[**] in CommandLineToArgvA | provenance | MaD:33 | | test.cpp:186:2:186:2 | *s [post update] [myField] | test.cpp:187:33:187:34 | *& ... [myField] | provenance | |
| test.cpp:186:2:186:24 | ... = ... | test.cpp:186:2:186:2 | *s [post update] [myField] | provenance | |
| test.cpp:186:14:186:22 | call to ymlSource | test.cpp:186:2:186:24 | ... = ... | provenance | Src:MaD:25 |
| test.cpp:187:10:187:31 | call to read_field_from_struct | test.cpp:187:10:187:31 | call to read_field_from_struct | provenance | |
| test.cpp:187:10:187:31 | call to read_field_from_struct | test.cpp:188:10:188:10 | x | provenance | Sink:MaD:1 |
| test.cpp:187:33:187:34 | *& ... [myField] | test.cpp:187:10:187:31 | call to read_field_from_struct | provenance | MaD:50 |
| test.cpp:199:2:199:2 | *s [post update] [myField] | test.cpp:200:35:200:36 | *& ... [myField] | provenance | |
| test.cpp:199:2:199:24 | ... = ... | test.cpp:199:2:199:2 | *s [post update] [myField] | provenance | |
| test.cpp:199:14:199:22 | call to ymlSource | test.cpp:199:2:199:24 | ... = ... | provenance | Src:MaD:25 |
| test.cpp:200:10:200:33 | call to read_field_from_struct_2 | test.cpp:200:10:200:33 | call to read_field_from_struct_2 | provenance | |
| test.cpp:200:10:200:33 | call to read_field_from_struct_2 | test.cpp:201:10:201:10 | x | provenance | Sink:MaD:1 |
| test.cpp:200:35:200:36 | *& ... [myField] | test.cpp:200:10:200:33 | call to read_field_from_struct_2 | provenance | MaD:51 |
| windows.cpp:22:15:22:29 | *call to GetCommandLineA | windows.cpp:22:15:22:29 | *call to GetCommandLineA | provenance | Src:MaD:3 | | windows.cpp:22:15:22:29 | *call to GetCommandLineA | windows.cpp:22:15:22:29 | *call to GetCommandLineA | provenance | Src:MaD:3 |
| windows.cpp:22:15:22:29 | *call to GetCommandLineA | windows.cpp:24:8:24:11 | * ... | provenance | | | windows.cpp:22:15:22:29 | *call to GetCommandLineA | windows.cpp:24:8:24:11 | * ... | provenance | |
| windows.cpp:22:15:22:29 | *call to GetCommandLineA | windows.cpp:27:36:27:38 | *cmd | provenance | | | windows.cpp:22:15:22:29 | *call to GetCommandLineA | windows.cpp:27:36:27:38 | *cmd | provenance | |
| windows.cpp:27:17:27:34 | **call to CommandLineToArgvA | windows.cpp:27:17:27:34 | **call to CommandLineToArgvA | provenance | | | windows.cpp:27:17:27:34 | **call to CommandLineToArgvA | windows.cpp:27:17:27:34 | **call to CommandLineToArgvA | provenance | |
| windows.cpp:27:17:27:34 | **call to CommandLineToArgvA | windows.cpp:30:8:30:15 | * ... | provenance | | | windows.cpp:27:17:27:34 | **call to CommandLineToArgvA | windows.cpp:30:8:30:15 | * ... | provenance | |
| windows.cpp:27:36:27:38 | *cmd | windows.cpp:17:8:17:25 | [summary param] *0 in CommandLineToArgvA | provenance | |
| windows.cpp:27:36:27:38 | *cmd | windows.cpp:27:17:27:34 | **call to CommandLineToArgvA | provenance | MaD:33 | | windows.cpp:27:36:27:38 | *cmd | windows.cpp:27:17:27:34 | **call to CommandLineToArgvA | provenance | MaD:33 |
| windows.cpp:34:17:34:38 | *call to GetEnvironmentStringsA | windows.cpp:34:17:34:38 | *call to GetEnvironmentStringsA | provenance | Src:MaD:4 | | windows.cpp:34:17:34:38 | *call to GetEnvironmentStringsA | windows.cpp:34:17:34:38 | *call to GetEnvironmentStringsA | provenance | Src:MaD:4 |
| windows.cpp:34:17:34:38 | *call to GetEnvironmentStringsA | windows.cpp:36:10:36:13 | * ... | provenance | | | windows.cpp:34:17:34:38 | *call to GetEnvironmentStringsA | windows.cpp:36:10:36:13 | * ... | provenance | |
| windows.cpp:39:36:39:38 | GetEnvironmentVariableA output argument | windows.cpp:41:10:41:13 | * ... | provenance | Src:MaD:5 | | windows.cpp:39:36:39:38 | GetEnvironmentVariableA output argument | windows.cpp:41:10:41:13 | * ... | provenance | Src:MaD:5 |
| windows.cpp:90:6:90:15 | [summary param] *3 in ReadFileEx [*hEvent] | windows.cpp:90:6:90:15 | [summary] read: Argument[*3].Field[*hEvent] in ReadFileEx | provenance | |
| windows.cpp:90:6:90:15 | [summary param] *3 in ReadFileEx [hEvent] | windows.cpp:90:6:90:15 | [summary] read: Argument[*3].Field[hEvent] in ReadFileEx | provenance | |
| windows.cpp:90:6:90:15 | [summary] read: Argument[*3].Field[*hEvent] in ReadFileEx | windows.cpp:90:6:90:15 | [summary] to write: Argument[4].Parameter[*2].Field[*hEvent] in ReadFileEx | provenance | MaD:37 |
| windows.cpp:90:6:90:15 | [summary] read: Argument[*3].Field[hEvent] in ReadFileEx | windows.cpp:90:6:90:15 | [summary] to write: Argument[4].Parameter[*2].Field[hEvent] in ReadFileEx | provenance | MaD:37 |
| windows.cpp:90:6:90:15 | [summary] to write: Argument[4].Parameter[*2] in ReadFileEx [*hEvent] | windows.cpp:147:16:147:27 | *lpOverlapped [*hEvent] | provenance | |
| windows.cpp:90:6:90:15 | [summary] to write: Argument[4].Parameter[*2] in ReadFileEx [hEvent] | windows.cpp:157:16:157:27 | *lpOverlapped [hEvent] | provenance | |
| windows.cpp:90:6:90:15 | [summary] to write: Argument[4].Parameter[*2].Field[*hEvent] in ReadFileEx | windows.cpp:90:6:90:15 | [summary] to write: Argument[4].Parameter[*2] in ReadFileEx [*hEvent] | provenance | |
| windows.cpp:90:6:90:15 | [summary] to write: Argument[4].Parameter[*2].Field[hEvent] in ReadFileEx | windows.cpp:90:6:90:15 | [summary] to write: Argument[4].Parameter[*2] in ReadFileEx [hEvent] | provenance | |
| windows.cpp:147:16:147:27 | *lpOverlapped [*hEvent] | windows.cpp:149:42:149:53 | *lpOverlapped [*hEvent] | provenance | | | windows.cpp:147:16:147:27 | *lpOverlapped [*hEvent] | windows.cpp:149:42:149:53 | *lpOverlapped [*hEvent] | provenance | |
| windows.cpp:149:18:149:62 | *hEvent | windows.cpp:149:18:149:62 | *hEvent | provenance | | | windows.cpp:149:18:149:62 | *hEvent | windows.cpp:149:18:149:62 | *hEvent | provenance | |
| windows.cpp:149:18:149:62 | *hEvent | windows.cpp:151:8:151:14 | * ... | provenance | | | windows.cpp:149:18:149:62 | *hEvent | windows.cpp:151:8:151:14 | * ... | provenance | |
@@ -251,11 +217,11 @@ edges
| windows.cpp:189:21:189:26 | ReadFile output argument | windows.cpp:190:5:190:56 | *... = ... | provenance | Src:MaD:17 | | windows.cpp:189:21:189:26 | ReadFile output argument | windows.cpp:190:5:190:56 | *... = ... | provenance | Src:MaD:17 |
| windows.cpp:190:5:190:14 | *overlapped [post update] [*hEvent] | windows.cpp:192:53:192:63 | *& ... [*hEvent] | provenance | | | windows.cpp:190:5:190:14 | *overlapped [post update] [*hEvent] | windows.cpp:192:53:192:63 | *& ... [*hEvent] | provenance | |
| windows.cpp:190:5:190:56 | *... = ... | windows.cpp:190:5:190:14 | *overlapped [post update] [*hEvent] | provenance | | | windows.cpp:190:5:190:56 | *... = ... | windows.cpp:190:5:190:14 | *overlapped [post update] [*hEvent] | provenance | |
| windows.cpp:192:53:192:63 | *& ... [*hEvent] | windows.cpp:90:6:90:15 | [summary param] *3 in ReadFileEx [*hEvent] | provenance | | | windows.cpp:192:53:192:63 | *& ... [*hEvent] | windows.cpp:147:16:147:27 | *lpOverlapped [*hEvent] | provenance | MaD:37 |
| windows.cpp:198:21:198:26 | ReadFile output argument | windows.cpp:199:5:199:57 | ... = ... | provenance | Src:MaD:17 | | windows.cpp:198:21:198:26 | ReadFile output argument | windows.cpp:199:5:199:57 | ... = ... | provenance | Src:MaD:17 |
| windows.cpp:199:5:199:14 | *overlapped [post update] [hEvent] | windows.cpp:201:53:201:63 | *& ... [hEvent] | provenance | | | windows.cpp:199:5:199:14 | *overlapped [post update] [hEvent] | windows.cpp:201:53:201:63 | *& ... [hEvent] | provenance | |
| windows.cpp:199:5:199:57 | ... = ... | windows.cpp:199:5:199:14 | *overlapped [post update] [hEvent] | provenance | | | windows.cpp:199:5:199:57 | ... = ... | windows.cpp:199:5:199:14 | *overlapped [post update] [hEvent] | provenance | |
| windows.cpp:201:53:201:63 | *& ... [hEvent] | windows.cpp:90:6:90:15 | [summary param] *3 in ReadFileEx [hEvent] | provenance | | | windows.cpp:201:53:201:63 | *& ... [hEvent] | windows.cpp:157:16:157:27 | *lpOverlapped [hEvent] | provenance | MaD:37 |
| windows.cpp:209:84:209:89 | NtReadFile output argument | windows.cpp:211:10:211:16 | * ... | provenance | Src:MaD:16 | | windows.cpp:209:84:209:89 | NtReadFile output argument | windows.cpp:211:10:211:16 | * ... | provenance | Src:MaD:16 |
| windows.cpp:286:23:286:35 | *call to MapViewOfFile | windows.cpp:286:23:286:35 | *call to MapViewOfFile | provenance | Src:MaD:12 | | windows.cpp:286:23:286:35 | *call to MapViewOfFile | windows.cpp:286:23:286:35 | *call to MapViewOfFile | provenance | Src:MaD:12 |
| windows.cpp:286:23:286:35 | *call to MapViewOfFile | windows.cpp:287:20:287:52 | *pMapView | provenance | | | windows.cpp:286:23:286:35 | *call to MapViewOfFile | windows.cpp:287:20:287:52 | *pMapView | provenance | |
@@ -278,12 +244,6 @@ edges
| windows.cpp:332:23:332:40 | *call to MapViewOfFileNuma2 | windows.cpp:332:23:332:40 | *call to MapViewOfFileNuma2 | provenance | Src:MaD:15 | | windows.cpp:332:23:332:40 | *call to MapViewOfFileNuma2 | windows.cpp:332:23:332:40 | *call to MapViewOfFileNuma2 | provenance | Src:MaD:15 |
| windows.cpp:332:23:332:40 | *call to MapViewOfFileNuma2 | windows.cpp:333:20:333:52 | *pMapView | provenance | | | windows.cpp:332:23:332:40 | *call to MapViewOfFileNuma2 | windows.cpp:333:20:333:52 | *pMapView | provenance | |
| windows.cpp:333:20:333:52 | *pMapView | windows.cpp:335:10:335:16 | * ... | provenance | | | windows.cpp:333:20:333:52 | *pMapView | windows.cpp:335:10:335:16 | * ... | provenance | |
| windows.cpp:349:8:349:19 | [summary param] *3 in CreateThread [x] | windows.cpp:349:8:349:19 | [summary] to write: Argument[2].Parameter[*0] in CreateThread [x] | provenance | MaD:36 |
| windows.cpp:349:8:349:19 | [summary] to write: Argument[2].Parameter[*0] in CreateThread [x] | windows.cpp:403:26:403:36 | *lpParameter [x] | provenance | |
| windows.cpp:357:8:357:25 | [summary param] *4 in CreateRemoteThread [x] | windows.cpp:357:8:357:25 | [summary] to write: Argument[3].Parameter[*0] in CreateRemoteThread [x] | provenance | MaD:34 |
| windows.cpp:357:8:357:25 | [summary] to write: Argument[3].Parameter[*0] in CreateRemoteThread [x] | windows.cpp:410:26:410:36 | *lpParameter [x] | provenance | |
| windows.cpp:387:8:387:27 | [summary param] *4 in CreateRemoteThreadEx [x] | windows.cpp:387:8:387:27 | [summary] to write: Argument[3].Parameter[*0] in CreateRemoteThreadEx [x] | provenance | MaD:35 |
| windows.cpp:387:8:387:27 | [summary] to write: Argument[3].Parameter[*0] in CreateRemoteThreadEx [x] | windows.cpp:417:26:417:36 | *lpParameter [x] | provenance | |
| windows.cpp:403:26:403:36 | *lpParameter [x] | windows.cpp:405:10:405:25 | *lpParameter [x] | provenance | | | windows.cpp:403:26:403:36 | *lpParameter [x] | windows.cpp:405:10:405:25 | *lpParameter [x] | provenance | |
| windows.cpp:405:10:405:25 | *lpParameter [x] | windows.cpp:406:8:406:8 | *s [x] | provenance | | | windows.cpp:405:10:405:25 | *lpParameter [x] | windows.cpp:406:8:406:8 | *s [x] | provenance | |
| windows.cpp:406:8:406:8 | *s [x] | windows.cpp:406:8:406:11 | x | provenance | | | windows.cpp:406:8:406:8 | *s [x] | windows.cpp:406:8:406:11 | x | provenance | |
@@ -298,22 +258,9 @@ edges
| windows.cpp:431:3:431:3 | *s [post update] [x] | windows.cpp:464:7:464:8 | *& ... [x] | provenance | | | windows.cpp:431:3:431:3 | *s [post update] [x] | windows.cpp:464:7:464:8 | *& ... [x] | provenance | |
| windows.cpp:431:3:431:16 | ... = ... | windows.cpp:431:3:431:3 | *s [post update] [x] | provenance | | | windows.cpp:431:3:431:16 | ... = ... | windows.cpp:431:3:431:3 | *s [post update] [x] | provenance | |
| windows.cpp:431:9:431:14 | call to source | windows.cpp:431:3:431:16 | ... = ... | provenance | | | windows.cpp:431:9:431:14 | call to source | windows.cpp:431:3:431:16 | ... = ... | provenance | |
| windows.cpp:439:7:439:8 | *& ... [x] | windows.cpp:349:8:349:19 | [summary param] *3 in CreateThread [x] | provenance | | | windows.cpp:439:7:439:8 | *& ... [x] | windows.cpp:403:26:403:36 | *lpParameter [x] | provenance | MaD:36 |
| windows.cpp:451:7:451:8 | *& ... [x] | windows.cpp:357:8:357:25 | [summary param] *4 in CreateRemoteThread [x] | provenance | | | windows.cpp:451:7:451:8 | *& ... [x] | windows.cpp:410:26:410:36 | *lpParameter [x] | provenance | MaD:34 |
| windows.cpp:464:7:464:8 | *& ... [x] | windows.cpp:387:8:387:27 | [summary param] *4 in CreateRemoteThreadEx [x] | provenance | | | windows.cpp:464:7:464:8 | *& ... [x] | windows.cpp:417:26:417:36 | *lpParameter [x] | provenance | MaD:35 |
| windows.cpp:473:17:473:37 | [summary param] *1 in RtlCopyVolatileMemory | windows.cpp:473:17:473:37 | [summary param] *0 in RtlCopyVolatileMemory [Return] | provenance | MaD:42 |
| windows.cpp:479:17:479:35 | [summary param] *1 in RtlCopyDeviceMemory | windows.cpp:479:17:479:35 | [summary param] *0 in RtlCopyDeviceMemory [Return] | provenance | MaD:38 |
| windows.cpp:485:6:485:18 | [summary param] *1 in RtlCopyMemory | windows.cpp:485:6:485:18 | [summary param] *0 in RtlCopyMemory [Return] | provenance | MaD:39 |
| windows.cpp:493:6:493:29 | [summary param] *1 in RtlCopyMemoryNonTemporal | windows.cpp:493:6:493:29 | [summary param] *0 in RtlCopyMemoryNonTemporal [Return] | provenance | MaD:40 |
| windows.cpp:510:6:510:25 | [summary param] *1 in RtlCopyUnicodeString [*Buffer] | windows.cpp:510:6:510:25 | [summary] read: Argument[*1].Field[*Buffer] in RtlCopyUnicodeString | provenance | |
| windows.cpp:510:6:510:25 | [summary] read: Argument[*1].Field[*Buffer] in RtlCopyUnicodeString | windows.cpp:510:6:510:25 | [summary] to write: Argument[*0].Field[*Buffer] in RtlCopyUnicodeString | provenance | MaD:41 |
| windows.cpp:510:6:510:25 | [summary] to write: Argument[*0] in RtlCopyUnicodeString [*Buffer] | windows.cpp:510:6:510:25 | [summary param] *0 in RtlCopyUnicodeString [Return] [*Buffer] | provenance | |
| windows.cpp:510:6:510:25 | [summary] to write: Argument[*0].Field[*Buffer] in RtlCopyUnicodeString | windows.cpp:510:6:510:25 | [summary] to write: Argument[*0] in RtlCopyUnicodeString [*Buffer] | provenance | |
| windows.cpp:515:6:515:18 | [summary param] *1 in RtlMoveMemory | windows.cpp:515:6:515:18 | [summary param] *0 in RtlMoveMemory [Return] | provenance | MaD:44 |
| windows.cpp:521:17:521:37 | [summary param] *1 in RtlMoveVolatileMemory | windows.cpp:521:17:521:37 | [summary param] *0 in RtlMoveVolatileMemory [Return] | provenance | MaD:45 |
| windows.cpp:527:6:527:25 | [summary param] *1 in RtlInitUnicodeString | windows.cpp:527:6:527:25 | [summary] to write: Argument[*0].Field[*Buffer] in RtlInitUnicodeString | provenance | MaD:43 |
| windows.cpp:527:6:527:25 | [summary] to write: Argument[*0] in RtlInitUnicodeString [*Buffer] | windows.cpp:527:6:527:25 | [summary param] *0 in RtlInitUnicodeString [Return] [*Buffer] | provenance | |
| windows.cpp:527:6:527:25 | [summary] to write: Argument[*0].Field[*Buffer] in RtlInitUnicodeString | windows.cpp:527:6:527:25 | [summary] to write: Argument[*0] in RtlInitUnicodeString [*Buffer] | provenance | |
| windows.cpp:533:11:533:16 | call to source | windows.cpp:533:11:533:16 | call to source | provenance | | | windows.cpp:533:11:533:16 | call to source | windows.cpp:533:11:533:16 | call to source | provenance | |
| windows.cpp:533:11:533:16 | call to source | windows.cpp:537:40:537:41 | *& ... | provenance | | | windows.cpp:533:11:533:16 | call to source | windows.cpp:537:40:537:41 | *& ... | provenance | |
| windows.cpp:533:11:533:16 | call to source | windows.cpp:542:38:542:39 | *& ... | provenance | | | windows.cpp:533:11:533:16 | call to source | windows.cpp:542:38:542:39 | *& ... | provenance | |
@@ -322,37 +269,29 @@ edges
| windows.cpp:533:11:533:16 | call to source | windows.cpp:568:32:568:33 | *& ... | provenance | | | windows.cpp:533:11:533:16 | call to source | windows.cpp:568:32:568:33 | *& ... | provenance | |
| windows.cpp:533:11:533:16 | call to source | windows.cpp:573:40:573:41 | *& ... | provenance | | | windows.cpp:533:11:533:16 | call to source | windows.cpp:573:40:573:41 | *& ... | provenance | |
| windows.cpp:537:27:537:37 | RtlCopyVolatileMemory output argument | windows.cpp:538:10:538:23 | access to array | provenance | | | windows.cpp:537:27:537:37 | RtlCopyVolatileMemory output argument | windows.cpp:538:10:538:23 | access to array | provenance | |
| windows.cpp:537:40:537:41 | *& ... | windows.cpp:473:17:473:37 | [summary param] *1 in RtlCopyVolatileMemory | provenance | |
| windows.cpp:537:40:537:41 | *& ... | windows.cpp:537:27:537:37 | RtlCopyVolatileMemory output argument | provenance | MaD:42 | | windows.cpp:537:40:537:41 | *& ... | windows.cpp:537:27:537:37 | RtlCopyVolatileMemory output argument | provenance | MaD:42 |
| windows.cpp:542:25:542:35 | RtlCopyDeviceMemory output argument | windows.cpp:543:10:543:23 | access to array | provenance | | | windows.cpp:542:25:542:35 | RtlCopyDeviceMemory output argument | windows.cpp:543:10:543:23 | access to array | provenance | |
| windows.cpp:542:38:542:39 | *& ... | windows.cpp:479:17:479:35 | [summary param] *1 in RtlCopyDeviceMemory | provenance | |
| windows.cpp:542:38:542:39 | *& ... | windows.cpp:542:25:542:35 | RtlCopyDeviceMemory output argument | provenance | MaD:38 | | windows.cpp:542:38:542:39 | *& ... | windows.cpp:542:25:542:35 | RtlCopyDeviceMemory output argument | provenance | MaD:38 |
| windows.cpp:547:19:547:29 | RtlCopyMemory output argument | windows.cpp:548:10:548:23 | access to array | provenance | | | windows.cpp:547:19:547:29 | RtlCopyMemory output argument | windows.cpp:548:10:548:23 | access to array | provenance | |
| windows.cpp:547:32:547:33 | *& ... | windows.cpp:485:6:485:18 | [summary param] *1 in RtlCopyMemory | provenance | |
| windows.cpp:547:32:547:33 | *& ... | windows.cpp:547:19:547:29 | RtlCopyMemory output argument | provenance | MaD:39 | | windows.cpp:547:32:547:33 | *& ... | windows.cpp:547:19:547:29 | RtlCopyMemory output argument | provenance | MaD:39 |
| windows.cpp:552:30:552:40 | RtlCopyMemoryNonTemporal output argument | windows.cpp:553:10:553:23 | access to array | provenance | | | windows.cpp:552:30:552:40 | RtlCopyMemoryNonTemporal output argument | windows.cpp:553:10:553:23 | access to array | provenance | |
| windows.cpp:552:43:552:44 | *& ... | windows.cpp:493:6:493:29 | [summary param] *1 in RtlCopyMemoryNonTemporal | provenance | |
| windows.cpp:552:43:552:44 | *& ... | windows.cpp:552:30:552:40 | RtlCopyMemoryNonTemporal output argument | provenance | MaD:40 | | windows.cpp:552:43:552:44 | *& ... | windows.cpp:552:30:552:40 | RtlCopyMemoryNonTemporal output argument | provenance | MaD:40 |
| windows.cpp:559:5:559:24 | ... = ... | windows.cpp:561:39:561:44 | *buffer | provenance | | | windows.cpp:559:5:559:24 | ... = ... | windows.cpp:561:39:561:44 | *buffer | provenance | |
| windows.cpp:559:17:559:24 | call to source | windows.cpp:559:5:559:24 | ... = ... | provenance | | | windows.cpp:559:17:559:24 | call to source | windows.cpp:559:5:559:24 | ... = ... | provenance | |
| windows.cpp:561:26:561:36 | RtlInitUnicodeString output argument [*Buffer] | windows.cpp:562:10:562:19 | *src_string [*Buffer] | provenance | | | windows.cpp:561:26:561:36 | RtlInitUnicodeString output argument [*Buffer] | windows.cpp:562:10:562:19 | *src_string [*Buffer] | provenance | |
| windows.cpp:561:26:561:36 | RtlInitUnicodeString output argument [*Buffer] | windows.cpp:563:40:563:50 | *& ... [*Buffer] | provenance | | | windows.cpp:561:26:561:36 | RtlInitUnicodeString output argument [*Buffer] | windows.cpp:563:40:563:50 | *& ... [*Buffer] | provenance | |
| windows.cpp:561:39:561:44 | *buffer | windows.cpp:527:6:527:25 | [summary param] *1 in RtlInitUnicodeString | provenance | |
| windows.cpp:561:39:561:44 | *buffer | windows.cpp:561:26:561:36 | RtlInitUnicodeString output argument [*Buffer] | provenance | MaD:43 | | windows.cpp:561:39:561:44 | *buffer | windows.cpp:561:26:561:36 | RtlInitUnicodeString output argument [*Buffer] | provenance | MaD:43 |
| windows.cpp:562:10:562:19 | *src_string [*Buffer] | windows.cpp:562:10:562:29 | access to array | provenance | | | windows.cpp:562:10:562:19 | *src_string [*Buffer] | windows.cpp:562:10:562:29 | access to array | provenance | |
| windows.cpp:562:10:562:19 | *src_string [*Buffer] | windows.cpp:562:21:562:26 | *Buffer | provenance | | | windows.cpp:562:10:562:19 | *src_string [*Buffer] | windows.cpp:562:21:562:26 | *Buffer | provenance | |
| windows.cpp:562:21:562:26 | *Buffer | windows.cpp:562:10:562:29 | access to array | provenance | | | windows.cpp:562:21:562:26 | *Buffer | windows.cpp:562:10:562:29 | access to array | provenance | |
| windows.cpp:563:26:563:37 | RtlCopyUnicodeString output argument [*Buffer] | windows.cpp:564:10:564:20 | *dest_string [*Buffer] | provenance | | | windows.cpp:563:26:563:37 | RtlCopyUnicodeString output argument [*Buffer] | windows.cpp:564:10:564:20 | *dest_string [*Buffer] | provenance | |
| windows.cpp:563:40:563:50 | *& ... [*Buffer] | windows.cpp:510:6:510:25 | [summary param] *1 in RtlCopyUnicodeString [*Buffer] | provenance | |
| windows.cpp:563:40:563:50 | *& ... [*Buffer] | windows.cpp:563:26:563:37 | RtlCopyUnicodeString output argument [*Buffer] | provenance | MaD:41 | | windows.cpp:563:40:563:50 | *& ... [*Buffer] | windows.cpp:563:26:563:37 | RtlCopyUnicodeString output argument [*Buffer] | provenance | MaD:41 |
| windows.cpp:564:10:564:20 | *dest_string [*Buffer] | windows.cpp:564:10:564:30 | access to array | provenance | | | windows.cpp:564:10:564:20 | *dest_string [*Buffer] | windows.cpp:564:10:564:30 | access to array | provenance | |
| windows.cpp:564:10:564:20 | *dest_string [*Buffer] | windows.cpp:564:22:564:27 | *Buffer | provenance | | | windows.cpp:564:10:564:20 | *dest_string [*Buffer] | windows.cpp:564:22:564:27 | *Buffer | provenance | |
| windows.cpp:564:22:564:27 | *Buffer | windows.cpp:564:10:564:30 | access to array | provenance | | | windows.cpp:564:22:564:27 | *Buffer | windows.cpp:564:10:564:30 | access to array | provenance | |
| windows.cpp:568:19:568:29 | RtlMoveMemory output argument | windows.cpp:569:10:569:23 | access to array | provenance | | | windows.cpp:568:19:568:29 | RtlMoveMemory output argument | windows.cpp:569:10:569:23 | access to array | provenance | |
| windows.cpp:568:32:568:33 | *& ... | windows.cpp:515:6:515:18 | [summary param] *1 in RtlMoveMemory | provenance | |
| windows.cpp:568:32:568:33 | *& ... | windows.cpp:568:19:568:29 | RtlMoveMemory output argument | provenance | MaD:44 | | windows.cpp:568:32:568:33 | *& ... | windows.cpp:568:19:568:29 | RtlMoveMemory output argument | provenance | MaD:44 |
| windows.cpp:573:27:573:37 | RtlMoveVolatileMemory output argument | windows.cpp:574:10:574:23 | access to array | provenance | | | windows.cpp:573:27:573:37 | RtlMoveVolatileMemory output argument | windows.cpp:574:10:574:23 | access to array | provenance | |
| windows.cpp:573:40:573:41 | *& ... | windows.cpp:521:17:521:37 | [summary param] *1 in RtlMoveVolatileMemory | provenance | |
| windows.cpp:573:40:573:41 | *& ... | windows.cpp:573:27:573:37 | RtlMoveVolatileMemory output argument | provenance | MaD:45 | | windows.cpp:573:40:573:41 | *& ... | windows.cpp:573:27:573:37 | RtlMoveVolatileMemory output argument | provenance | MaD:45 |
| windows.cpp:645:45:645:50 | WinHttpReadData output argument | windows.cpp:647:10:647:16 | * ... | provenance | Src:MaD:23 | | windows.cpp:645:45:645:50 | WinHttpReadData output argument | windows.cpp:647:10:647:16 | * ... | provenance | Src:MaD:23 |
| windows.cpp:652:48:652:53 | WinHttpReadDataEx output argument | windows.cpp:654:10:654:16 | * ... | provenance | Src:MaD:24 | | windows.cpp:652:48:652:53 | WinHttpReadDataEx output argument | windows.cpp:654:10:654:16 | * ... | provenance | Src:MaD:24 |
@@ -360,10 +299,8 @@ edges
| windows.cpp:669:70:669:79 | WinHttpQueryHeadersEx output argument | windows.cpp:673:10:673:29 | * ... | provenance | Src:MaD:21 | | windows.cpp:669:70:669:79 | WinHttpQueryHeadersEx output argument | windows.cpp:673:10:673:29 | * ... | provenance | Src:MaD:21 |
| windows.cpp:669:82:669:87 | WinHttpQueryHeadersEx output argument | windows.cpp:671:10:671:16 | * ... | provenance | Src:MaD:22 | | windows.cpp:669:82:669:87 | WinHttpQueryHeadersEx output argument | windows.cpp:671:10:671:16 | * ... | provenance | Src:MaD:22 |
| windows.cpp:669:105:669:112 | WinHttpQueryHeadersEx output argument | windows.cpp:675:10:675:27 | * ... | provenance | Src:MaD:20 | | windows.cpp:669:105:669:112 | WinHttpQueryHeadersEx output argument | windows.cpp:675:10:675:27 | * ... | provenance | Src:MaD:20 |
| windows.cpp:714:6:714:20 | [summary param] *0 in WinHttpCrackUrl | windows.cpp:714:6:714:20 | [summary param] *3 in WinHttpCrackUrl [Return] | provenance | MaD:46 |
| windows.cpp:728:5:728:28 | ... = ... | windows.cpp:729:35:729:35 | *x | provenance | | | windows.cpp:728:5:728:28 | ... = ... | windows.cpp:729:35:729:35 | *x | provenance | |
| windows.cpp:728:12:728:28 | call to source | windows.cpp:728:5:728:28 | ... = ... | provenance | | | windows.cpp:728:12:728:28 | call to source | windows.cpp:728:5:728:28 | ... = ... | provenance | |
| windows.cpp:729:35:729:35 | *x | windows.cpp:714:6:714:20 | [summary param] *0 in WinHttpCrackUrl | provenance | |
| windows.cpp:729:35:729:35 | *x | windows.cpp:729:44:729:57 | WinHttpCrackUrl output argument | provenance | MaD:46 | | windows.cpp:729:35:729:35 | *x | windows.cpp:729:44:729:57 | WinHttpCrackUrl output argument | provenance | MaD:46 |
| windows.cpp:729:44:729:57 | WinHttpCrackUrl output argument | windows.cpp:731:10:731:36 | * ... | provenance | | | windows.cpp:729:44:729:57 | WinHttpCrackUrl output argument | windows.cpp:731:10:731:36 | * ... | provenance | |
| windows.cpp:729:44:729:57 | WinHttpCrackUrl output argument | windows.cpp:733:10:733:35 | * ... | provenance | | | windows.cpp:729:44:729:57 | WinHttpCrackUrl output argument | windows.cpp:733:10:733:35 | * ... | provenance | |
@@ -386,8 +323,6 @@ edges
| windows.cpp:936:70:936:78 | HttpReceiveClientCertificate output argument | windows.cpp:941:10:941:31 | * ... | provenance | Src:MaD:6 | | windows.cpp:936:70:936:78 | HttpReceiveClientCertificate output argument | windows.cpp:941:10:941:31 | * ... | provenance | Src:MaD:6 |
| windows.cpp:937:15:937:48 | *& ... | windows.cpp:939:10:939:11 | * ... | provenance | | | windows.cpp:937:15:937:48 | *& ... | windows.cpp:939:10:939:11 | * ... | provenance | |
nodes nodes
| asio_streams.cpp:56:18:56:23 | [summary param] *0 in buffer | semmle.label | [summary param] *0 in buffer |
| asio_streams.cpp:56:18:56:23 | [summary] to write: ReturnValue in buffer | semmle.label | [summary] to write: ReturnValue in buffer |
| asio_streams.cpp:87:34:87:44 | read_until output argument | semmle.label | read_until output argument | | asio_streams.cpp:87:34:87:44 | read_until output argument | semmle.label | read_until output argument |
| asio_streams.cpp:91:7:91:17 | recv_buffer | semmle.label | recv_buffer | | asio_streams.cpp:91:7:91:17 | recv_buffer | semmle.label | recv_buffer |
| asio_streams.cpp:93:29:93:39 | *recv_buffer | semmle.label | *recv_buffer | | asio_streams.cpp:93:29:93:39 | *recv_buffer | semmle.label | *recv_buffer |
@@ -398,15 +333,6 @@ nodes
| asio_streams.cpp:100:64:100:71 | *send_str | semmle.label | *send_str | | asio_streams.cpp:100:64:100:71 | *send_str | semmle.label | *send_str |
| asio_streams.cpp:101:7:101:17 | send_buffer | semmle.label | send_buffer | | asio_streams.cpp:101:7:101:17 | send_buffer | semmle.label | send_buffer |
| asio_streams.cpp:103:29:103:39 | *send_buffer | semmle.label | *send_buffer | | asio_streams.cpp:103:29:103:39 | *send_buffer | semmle.label | *send_buffer |
| azure.cpp:62:10:62:14 | [summary param] this in Value | semmle.label | [summary param] this in Value |
| azure.cpp:62:10:62:14 | [summary] to write: ReturnValue[*] in Value | semmle.label | [summary] to write: ReturnValue[*] in Value |
| azure.cpp:113:16:113:19 | [summary param] *0 in Read [Return] | semmle.label | [summary param] *0 in Read [Return] |
| azure.cpp:113:16:113:19 | [summary param] this in Read | semmle.label | [summary param] this in Read |
| azure.cpp:114:16:114:26 | [summary param] *0 in ReadToCount [Return] | semmle.label | [summary param] *0 in ReadToCount [Return] |
| azure.cpp:114:16:114:26 | [summary param] this in ReadToCount | semmle.label | [summary param] this in ReadToCount |
| azure.cpp:115:30:115:38 | [summary param] this in ReadToEnd | semmle.label | [summary param] this in ReadToEnd |
| azure.cpp:115:30:115:38 | [summary] to write: ReturnValue in ReadToEnd [element] | semmle.label | [summary] to write: ReturnValue in ReadToEnd [element] |
| azure.cpp:115:30:115:38 | [summary] to write: ReturnValue.Element in ReadToEnd | semmle.label | [summary] to write: ReturnValue.Element in ReadToEnd |
| azure.cpp:253:48:253:60 | *call to GetBodyStream | semmle.label | *call to GetBodyStream | | azure.cpp:253:48:253:60 | *call to GetBodyStream | semmle.label | *call to GetBodyStream |
| azure.cpp:253:48:253:60 | *call to GetBodyStream | semmle.label | *call to GetBodyStream | | azure.cpp:253:48:253:60 | *call to GetBodyStream | semmle.label | *call to GetBodyStream |
| azure.cpp:257:5:257:8 | *resp | semmle.label | *resp | | azure.cpp:257:5:257:8 | *resp | semmle.label | *resp |
@@ -451,12 +377,6 @@ nodes
| azure.cpp:295:10:295:20 | contentType | semmle.label | contentType | | azure.cpp:295:10:295:20 | contentType | semmle.label | contentType |
| azure.cpp:295:10:295:20 | contentType | semmle.label | contentType | | azure.cpp:295:10:295:20 | contentType | semmle.label | contentType |
| azure.cpp:295:10:295:20 | contentType | semmle.label | contentType | | azure.cpp:295:10:295:20 | contentType | semmle.label | contentType |
| test.cpp:4:5:4:17 | [summary param] 0 in ymlStepManual | semmle.label | [summary param] 0 in ymlStepManual |
| test.cpp:4:5:4:17 | [summary] to write: ReturnValue in ymlStepManual | semmle.label | [summary] to write: ReturnValue in ymlStepManual |
| test.cpp:5:5:5:20 | [summary param] 0 in ymlStepGenerated | semmle.label | [summary param] 0 in ymlStepGenerated |
| test.cpp:5:5:5:20 | [summary] to write: ReturnValue in ymlStepGenerated | semmle.label | [summary] to write: ReturnValue in ymlStepGenerated |
| test.cpp:6:5:6:27 | [summary param] 0 in ymlStepManual_with_body | semmle.label | [summary param] 0 in ymlStepManual_with_body |
| test.cpp:6:5:6:27 | [summary] to write: ReturnValue in ymlStepManual_with_body | semmle.label | [summary] to write: ReturnValue in ymlStepManual_with_body |
| test.cpp:7:5:7:30 | *ymlStepGenerated_with_body | semmle.label | *ymlStepGenerated_with_body | | test.cpp:7:5:7:30 | *ymlStepGenerated_with_body | semmle.label | *ymlStepGenerated_with_body |
| test.cpp:7:47:7:52 | value2 | semmle.label | value2 | | test.cpp:7:47:7:52 | value2 | semmle.label | value2 |
| test.cpp:7:64:7:69 | value2 | semmle.label | value2 | | test.cpp:7:64:7:69 | value2 | semmle.label | value2 |
@@ -483,20 +403,10 @@ nodes
| test.cpp:47:12:47:19 | *arg [x] | semmle.label | *arg [x] | | test.cpp:47:12:47:19 | *arg [x] | semmle.label | *arg [x] |
| test.cpp:48:13:48:13 | *s [x] | semmle.label | *s [x] | | test.cpp:48:13:48:13 | *s [x] | semmle.label | *s [x] |
| test.cpp:48:16:48:16 | x | semmle.label | x | | test.cpp:48:16:48:16 | x | semmle.label | x |
| test.cpp:52:5:52:18 | [summary param] *3 in pthread_create [x] | semmle.label | [summary param] *3 in pthread_create [x] |
| test.cpp:52:5:52:18 | [summary] to write: Argument[2].Parameter[*0] in pthread_create [x] | semmle.label | [summary] to write: Argument[2].Parameter[*0] in pthread_create [x] |
| test.cpp:56:2:56:2 | *s [post update] [x] | semmle.label | *s [post update] [x] | | test.cpp:56:2:56:2 | *s [post update] [x] | semmle.label | *s [post update] [x] |
| test.cpp:56:2:56:18 | ... = ... | semmle.label | ... = ... | | test.cpp:56:2:56:18 | ... = ... | semmle.label | ... = ... |
| test.cpp:56:8:56:16 | call to ymlSource | semmle.label | call to ymlSource | | test.cpp:56:8:56:16 | call to ymlSource | semmle.label | call to ymlSource |
| test.cpp:59:55:59:64 | *& ... [x] | semmle.label | *& ... [x] | | test.cpp:59:55:59:64 | *& ... [x] | semmle.label | *& ... [x] |
| test.cpp:63:6:63:21 | [summary param] 1 in callWithArgument | semmle.label | [summary param] 1 in callWithArgument |
| test.cpp:63:6:63:21 | [summary param] 1 in callWithArgument | semmle.label | [summary param] 1 in callWithArgument |
| test.cpp:63:6:63:21 | [summary param] 1 in callWithArgument | semmle.label | [summary param] 1 in callWithArgument |
| test.cpp:63:6:63:21 | [summary param] 1 in callWithArgument | semmle.label | [summary param] 1 in callWithArgument |
| test.cpp:63:6:63:21 | [summary] to write: Argument[0].Parameter[0] in callWithArgument | semmle.label | [summary] to write: Argument[0].Parameter[0] in callWithArgument |
| test.cpp:63:6:63:21 | [summary] to write: Argument[0].Parameter[0] in callWithArgument | semmle.label | [summary] to write: Argument[0].Parameter[0] in callWithArgument |
| test.cpp:63:6:63:21 | [summary] to write: Argument[0].Parameter[0] in callWithArgument | semmle.label | [summary] to write: Argument[0].Parameter[0] in callWithArgument |
| test.cpp:63:6:63:21 | [summary] to write: Argument[0].Parameter[0] in callWithArgument | semmle.label | [summary] to write: Argument[0].Parameter[0] in callWithArgument |
| test.cpp:68:22:68:22 | y | semmle.label | y | | test.cpp:68:22:68:22 | y | semmle.label | y |
| test.cpp:69:11:69:11 | y | semmle.label | y | | test.cpp:69:11:69:11 | y | semmle.label | y |
| test.cpp:74:22:74:22 | y | semmle.label | y | | test.cpp:74:22:74:22 | y | semmle.label | y |
@@ -511,28 +421,18 @@ nodes
| test.cpp:101:26:101:26 | x | semmle.label | x | | test.cpp:101:26:101:26 | x | semmle.label | x |
| test.cpp:103:63:103:63 | x | semmle.label | x | | test.cpp:103:63:103:63 | x | semmle.label | x |
| test.cpp:104:62:104:62 | x | semmle.label | x | | test.cpp:104:62:104:62 | x | semmle.label | x |
| test.cpp:111:3:111:25 | [summary param] *0 in callWithNonTypeTemplate | semmle.label | [summary param] *0 in callWithNonTypeTemplate |
| test.cpp:111:3:111:25 | [summary] to write: ReturnValue in callWithNonTypeTemplate | semmle.label | [summary] to write: ReturnValue in callWithNonTypeTemplate |
| test.cpp:114:10:114:18 | call to ymlSource | semmle.label | call to ymlSource | | test.cpp:114:10:114:18 | call to ymlSource | semmle.label | call to ymlSource |
| test.cpp:114:10:114:18 | call to ymlSource | semmle.label | call to ymlSource | | test.cpp:114:10:114:18 | call to ymlSource | semmle.label | call to ymlSource |
| test.cpp:118:11:118:42 | call to callWithNonTypeTemplate | semmle.label | call to callWithNonTypeTemplate | | test.cpp:118:11:118:42 | call to callWithNonTypeTemplate | semmle.label | call to callWithNonTypeTemplate |
| test.cpp:118:11:118:42 | call to callWithNonTypeTemplate | semmle.label | call to callWithNonTypeTemplate | | test.cpp:118:11:118:42 | call to callWithNonTypeTemplate | semmle.label | call to callWithNonTypeTemplate |
| test.cpp:118:44:118:44 | *x | semmle.label | *x | | test.cpp:118:44:118:44 | *x | semmle.label | *x |
| test.cpp:119:10:119:11 | y2 | semmle.label | y2 | | test.cpp:119:10:119:11 | y2 | semmle.label | y2 |
| test.cpp:125:5:125:20 | [summary param] 0 in templateFunction | semmle.label | [summary param] 0 in templateFunction |
| test.cpp:125:5:125:20 | [summary] to write: ReturnValue in templateFunction | semmle.label | [summary] to write: ReturnValue in templateFunction |
| test.cpp:128:5:128:21 | [summary param] 1 in templateFunction2 | semmle.label | [summary param] 1 in templateFunction2 |
| test.cpp:128:5:128:21 | [summary] to write: ReturnValue in templateFunction2 | semmle.label | [summary] to write: ReturnValue in templateFunction2 |
| test.cpp:133:10:133:18 | call to ymlSource | semmle.label | call to ymlSource | | test.cpp:133:10:133:18 | call to ymlSource | semmle.label | call to ymlSource |
| test.cpp:133:10:133:18 | call to ymlSource | semmle.label | call to ymlSource | | test.cpp:133:10:133:18 | call to ymlSource | semmle.label | call to ymlSource |
| test.cpp:134:13:134:43 | call to templateFunction | semmle.label | call to templateFunction | | test.cpp:134:13:134:43 | call to templateFunction | semmle.label | call to templateFunction |
| test.cpp:134:13:134:43 | call to templateFunction | semmle.label | call to templateFunction | | test.cpp:134:13:134:43 | call to templateFunction | semmle.label | call to templateFunction |
| test.cpp:134:45:134:45 | x | semmle.label | x | | test.cpp:134:45:134:45 | x | semmle.label | x |
| test.cpp:135:10:135:10 | y | semmle.label | y | | test.cpp:135:10:135:10 | y | semmle.label | y |
| test.cpp:140:4:140:11 | [summary param] 1 in function | semmle.label | [summary param] 1 in function |
| test.cpp:140:4:140:11 | [summary param] 1 in function | semmle.label | [summary param] 1 in function |
| test.cpp:140:4:140:11 | [summary] to write: ReturnValue in function | semmle.label | [summary] to write: ReturnValue in function |
| test.cpp:140:4:140:11 | [summary] to write: ReturnValue in function | semmle.label | [summary] to write: ReturnValue in function |
| test.cpp:146:10:146:18 | call to ymlSource | semmle.label | call to ymlSource | | test.cpp:146:10:146:18 | call to ymlSource | semmle.label | call to ymlSource |
| test.cpp:146:10:146:18 | call to ymlSource | semmle.label | call to ymlSource | | test.cpp:146:10:146:18 | call to ymlSource | semmle.label | call to ymlSource |
| test.cpp:148:10:148:27 | call to function | semmle.label | call to function | | test.cpp:148:10:148:27 | call to function | semmle.label | call to function |
@@ -556,8 +456,20 @@ nodes
| test.cpp:172:13:172:44 | call to templateFunction3 | semmle.label | call to templateFunction3 | | test.cpp:172:13:172:44 | call to templateFunction3 | semmle.label | call to templateFunction3 |
| test.cpp:172:51:172:51 | x | semmle.label | x | | test.cpp:172:51:172:51 | x | semmle.label | x |
| test.cpp:173:10:173:10 | y | semmle.label | y | | test.cpp:173:10:173:10 | y | semmle.label | y |
| windows.cpp:17:8:17:25 | [summary param] *0 in CommandLineToArgvA | semmle.label | [summary param] *0 in CommandLineToArgvA | | test.cpp:186:2:186:2 | *s [post update] [myField] | semmle.label | *s [post update] [myField] |
| windows.cpp:17:8:17:25 | [summary] to write: ReturnValue[**] in CommandLineToArgvA | semmle.label | [summary] to write: ReturnValue[**] in CommandLineToArgvA | | test.cpp:186:2:186:24 | ... = ... | semmle.label | ... = ... |
| test.cpp:186:14:186:22 | call to ymlSource | semmle.label | call to ymlSource |
| test.cpp:187:10:187:31 | call to read_field_from_struct | semmle.label | call to read_field_from_struct |
| test.cpp:187:10:187:31 | call to read_field_from_struct | semmle.label | call to read_field_from_struct |
| test.cpp:187:33:187:34 | *& ... [myField] | semmle.label | *& ... [myField] |
| test.cpp:188:10:188:10 | x | semmle.label | x |
| test.cpp:199:2:199:2 | *s [post update] [myField] | semmle.label | *s [post update] [myField] |
| test.cpp:199:2:199:24 | ... = ... | semmle.label | ... = ... |
| test.cpp:199:14:199:22 | call to ymlSource | semmle.label | call to ymlSource |
| test.cpp:200:10:200:33 | call to read_field_from_struct_2 | semmle.label | call to read_field_from_struct_2 |
| test.cpp:200:10:200:33 | call to read_field_from_struct_2 | semmle.label | call to read_field_from_struct_2 |
| test.cpp:200:35:200:36 | *& ... [myField] | semmle.label | *& ... [myField] |
| test.cpp:201:10:201:10 | x | semmle.label | x |
| windows.cpp:22:15:22:29 | *call to GetCommandLineA | semmle.label | *call to GetCommandLineA | | windows.cpp:22:15:22:29 | *call to GetCommandLineA | semmle.label | *call to GetCommandLineA |
| windows.cpp:22:15:22:29 | *call to GetCommandLineA | semmle.label | *call to GetCommandLineA | | windows.cpp:22:15:22:29 | *call to GetCommandLineA | semmle.label | *call to GetCommandLineA |
| windows.cpp:24:8:24:11 | * ... | semmle.label | * ... | | windows.cpp:24:8:24:11 | * ... | semmle.label | * ... |
@@ -570,14 +482,6 @@ nodes
| windows.cpp:36:10:36:13 | * ... | semmle.label | * ... | | windows.cpp:36:10:36:13 | * ... | semmle.label | * ... |
| windows.cpp:39:36:39:38 | GetEnvironmentVariableA output argument | semmle.label | GetEnvironmentVariableA output argument | | windows.cpp:39:36:39:38 | GetEnvironmentVariableA output argument | semmle.label | GetEnvironmentVariableA output argument |
| windows.cpp:41:10:41:13 | * ... | semmle.label | * ... | | windows.cpp:41:10:41:13 | * ... | semmle.label | * ... |
| windows.cpp:90:6:90:15 | [summary param] *3 in ReadFileEx [*hEvent] | semmle.label | [summary param] *3 in ReadFileEx [*hEvent] |
| windows.cpp:90:6:90:15 | [summary param] *3 in ReadFileEx [hEvent] | semmle.label | [summary param] *3 in ReadFileEx [hEvent] |
| windows.cpp:90:6:90:15 | [summary] read: Argument[*3].Field[*hEvent] in ReadFileEx | semmle.label | [summary] read: Argument[*3].Field[*hEvent] in ReadFileEx |
| windows.cpp:90:6:90:15 | [summary] read: Argument[*3].Field[hEvent] in ReadFileEx | semmle.label | [summary] read: Argument[*3].Field[hEvent] in ReadFileEx |
| windows.cpp:90:6:90:15 | [summary] to write: Argument[4].Parameter[*2] in ReadFileEx [*hEvent] | semmle.label | [summary] to write: Argument[4].Parameter[*2] in ReadFileEx [*hEvent] |
| windows.cpp:90:6:90:15 | [summary] to write: Argument[4].Parameter[*2] in ReadFileEx [hEvent] | semmle.label | [summary] to write: Argument[4].Parameter[*2] in ReadFileEx [hEvent] |
| windows.cpp:90:6:90:15 | [summary] to write: Argument[4].Parameter[*2].Field[*hEvent] in ReadFileEx | semmle.label | [summary] to write: Argument[4].Parameter[*2].Field[*hEvent] in ReadFileEx |
| windows.cpp:90:6:90:15 | [summary] to write: Argument[4].Parameter[*2].Field[hEvent] in ReadFileEx | semmle.label | [summary] to write: Argument[4].Parameter[*2].Field[hEvent] in ReadFileEx |
| windows.cpp:147:16:147:27 | *lpOverlapped [*hEvent] | semmle.label | *lpOverlapped [*hEvent] | | windows.cpp:147:16:147:27 | *lpOverlapped [*hEvent] | semmle.label | *lpOverlapped [*hEvent] |
| windows.cpp:149:18:149:62 | *hEvent | semmle.label | *hEvent | | windows.cpp:149:18:149:62 | *hEvent | semmle.label | *hEvent |
| windows.cpp:149:18:149:62 | *hEvent | semmle.label | *hEvent | | windows.cpp:149:18:149:62 | *hEvent | semmle.label | *hEvent |
@@ -631,12 +535,6 @@ nodes
| windows.cpp:332:23:332:40 | *call to MapViewOfFileNuma2 | semmle.label | *call to MapViewOfFileNuma2 | | windows.cpp:332:23:332:40 | *call to MapViewOfFileNuma2 | semmle.label | *call to MapViewOfFileNuma2 |
| windows.cpp:333:20:333:52 | *pMapView | semmle.label | *pMapView | | windows.cpp:333:20:333:52 | *pMapView | semmle.label | *pMapView |
| windows.cpp:335:10:335:16 | * ... | semmle.label | * ... | | windows.cpp:335:10:335:16 | * ... | semmle.label | * ... |
| windows.cpp:349:8:349:19 | [summary param] *3 in CreateThread [x] | semmle.label | [summary param] *3 in CreateThread [x] |
| windows.cpp:349:8:349:19 | [summary] to write: Argument[2].Parameter[*0] in CreateThread [x] | semmle.label | [summary] to write: Argument[2].Parameter[*0] in CreateThread [x] |
| windows.cpp:357:8:357:25 | [summary param] *4 in CreateRemoteThread [x] | semmle.label | [summary param] *4 in CreateRemoteThread [x] |
| windows.cpp:357:8:357:25 | [summary] to write: Argument[3].Parameter[*0] in CreateRemoteThread [x] | semmle.label | [summary] to write: Argument[3].Parameter[*0] in CreateRemoteThread [x] |
| windows.cpp:387:8:387:27 | [summary param] *4 in CreateRemoteThreadEx [x] | semmle.label | [summary param] *4 in CreateRemoteThreadEx [x] |
| windows.cpp:387:8:387:27 | [summary] to write: Argument[3].Parameter[*0] in CreateRemoteThreadEx [x] | semmle.label | [summary] to write: Argument[3].Parameter[*0] in CreateRemoteThreadEx [x] |
| windows.cpp:403:26:403:36 | *lpParameter [x] | semmle.label | *lpParameter [x] | | windows.cpp:403:26:403:36 | *lpParameter [x] | semmle.label | *lpParameter [x] |
| windows.cpp:405:10:405:25 | *lpParameter [x] | semmle.label | *lpParameter [x] | | windows.cpp:405:10:405:25 | *lpParameter [x] | semmle.label | *lpParameter [x] |
| windows.cpp:406:8:406:8 | *s [x] | semmle.label | *s [x] | | windows.cpp:406:8:406:8 | *s [x] | semmle.label | *s [x] |
@@ -655,27 +553,6 @@ nodes
| windows.cpp:439:7:439:8 | *& ... [x] | semmle.label | *& ... [x] | | windows.cpp:439:7:439:8 | *& ... [x] | semmle.label | *& ... [x] |
| windows.cpp:451:7:451:8 | *& ... [x] | semmle.label | *& ... [x] | | windows.cpp:451:7:451:8 | *& ... [x] | semmle.label | *& ... [x] |
| windows.cpp:464:7:464:8 | *& ... [x] | semmle.label | *& ... [x] | | windows.cpp:464:7:464:8 | *& ... [x] | semmle.label | *& ... [x] |
| windows.cpp:473:17:473:37 | [summary param] *0 in RtlCopyVolatileMemory [Return] | semmle.label | [summary param] *0 in RtlCopyVolatileMemory [Return] |
| windows.cpp:473:17:473:37 | [summary param] *1 in RtlCopyVolatileMemory | semmle.label | [summary param] *1 in RtlCopyVolatileMemory |
| windows.cpp:479:17:479:35 | [summary param] *0 in RtlCopyDeviceMemory [Return] | semmle.label | [summary param] *0 in RtlCopyDeviceMemory [Return] |
| windows.cpp:479:17:479:35 | [summary param] *1 in RtlCopyDeviceMemory | semmle.label | [summary param] *1 in RtlCopyDeviceMemory |
| windows.cpp:485:6:485:18 | [summary param] *0 in RtlCopyMemory [Return] | semmle.label | [summary param] *0 in RtlCopyMemory [Return] |
| windows.cpp:485:6:485:18 | [summary param] *1 in RtlCopyMemory | semmle.label | [summary param] *1 in RtlCopyMemory |
| windows.cpp:493:6:493:29 | [summary param] *0 in RtlCopyMemoryNonTemporal [Return] | semmle.label | [summary param] *0 in RtlCopyMemoryNonTemporal [Return] |
| windows.cpp:493:6:493:29 | [summary param] *1 in RtlCopyMemoryNonTemporal | semmle.label | [summary param] *1 in RtlCopyMemoryNonTemporal |
| windows.cpp:510:6:510:25 | [summary param] *0 in RtlCopyUnicodeString [Return] [*Buffer] | semmle.label | [summary param] *0 in RtlCopyUnicodeString [Return] [*Buffer] |
| windows.cpp:510:6:510:25 | [summary param] *1 in RtlCopyUnicodeString [*Buffer] | semmle.label | [summary param] *1 in RtlCopyUnicodeString [*Buffer] |
| windows.cpp:510:6:510:25 | [summary] read: Argument[*1].Field[*Buffer] in RtlCopyUnicodeString | semmle.label | [summary] read: Argument[*1].Field[*Buffer] in RtlCopyUnicodeString |
| windows.cpp:510:6:510:25 | [summary] to write: Argument[*0] in RtlCopyUnicodeString [*Buffer] | semmle.label | [summary] to write: Argument[*0] in RtlCopyUnicodeString [*Buffer] |
| windows.cpp:510:6:510:25 | [summary] to write: Argument[*0].Field[*Buffer] in RtlCopyUnicodeString | semmle.label | [summary] to write: Argument[*0].Field[*Buffer] in RtlCopyUnicodeString |
| windows.cpp:515:6:515:18 | [summary param] *0 in RtlMoveMemory [Return] | semmle.label | [summary param] *0 in RtlMoveMemory [Return] |
| windows.cpp:515:6:515:18 | [summary param] *1 in RtlMoveMemory | semmle.label | [summary param] *1 in RtlMoveMemory |
| windows.cpp:521:17:521:37 | [summary param] *0 in RtlMoveVolatileMemory [Return] | semmle.label | [summary param] *0 in RtlMoveVolatileMemory [Return] |
| windows.cpp:521:17:521:37 | [summary param] *1 in RtlMoveVolatileMemory | semmle.label | [summary param] *1 in RtlMoveVolatileMemory |
| windows.cpp:527:6:527:25 | [summary param] *0 in RtlInitUnicodeString [Return] [*Buffer] | semmle.label | [summary param] *0 in RtlInitUnicodeString [Return] [*Buffer] |
| windows.cpp:527:6:527:25 | [summary param] *1 in RtlInitUnicodeString | semmle.label | [summary param] *1 in RtlInitUnicodeString |
| windows.cpp:527:6:527:25 | [summary] to write: Argument[*0] in RtlInitUnicodeString [*Buffer] | semmle.label | [summary] to write: Argument[*0] in RtlInitUnicodeString [*Buffer] |
| windows.cpp:527:6:527:25 | [summary] to write: Argument[*0].Field[*Buffer] in RtlInitUnicodeString | semmle.label | [summary] to write: Argument[*0].Field[*Buffer] in RtlInitUnicodeString |
| windows.cpp:533:11:533:16 | call to source | semmle.label | call to source | | windows.cpp:533:11:533:16 | call to source | semmle.label | call to source |
| windows.cpp:533:11:533:16 | call to source | semmle.label | call to source | | windows.cpp:533:11:533:16 | call to source | semmle.label | call to source |
| windows.cpp:537:27:537:37 | RtlCopyVolatileMemory output argument | semmle.label | RtlCopyVolatileMemory output argument | | windows.cpp:537:27:537:37 | RtlCopyVolatileMemory output argument | semmle.label | RtlCopyVolatileMemory output argument |
@@ -720,8 +597,6 @@ nodes
| windows.cpp:671:10:671:16 | * ... | semmle.label | * ... | | windows.cpp:671:10:671:16 | * ... | semmle.label | * ... |
| windows.cpp:673:10:673:29 | * ... | semmle.label | * ... | | windows.cpp:673:10:673:29 | * ... | semmle.label | * ... |
| windows.cpp:675:10:675:27 | * ... | semmle.label | * ... | | windows.cpp:675:10:675:27 | * ... | semmle.label | * ... |
| windows.cpp:714:6:714:20 | [summary param] *0 in WinHttpCrackUrl | semmle.label | [summary param] *0 in WinHttpCrackUrl |
| windows.cpp:714:6:714:20 | [summary param] *3 in WinHttpCrackUrl [Return] | semmle.label | [summary param] *3 in WinHttpCrackUrl [Return] |
| windows.cpp:728:5:728:28 | ... = ... | semmle.label | ... = ... | | windows.cpp:728:5:728:28 | ... = ... | semmle.label | ... = ... |
| windows.cpp:728:12:728:28 | call to source | semmle.label | call to source | | windows.cpp:728:12:728:28 | call to source | semmle.label | call to source |
| windows.cpp:729:35:729:35 | *x | semmle.label | *x | | windows.cpp:729:35:729:35 | *x | semmle.label | *x |
@@ -750,30 +625,6 @@ nodes
| windows.cpp:939:10:939:11 | * ... | semmle.label | * ... | | windows.cpp:939:10:939:11 | * ... | semmle.label | * ... |
| windows.cpp:941:10:941:31 | * ... | semmle.label | * ... | | windows.cpp:941:10:941:31 | * ... | semmle.label | * ... |
subpaths subpaths
| asio_streams.cpp:100:64:100:71 | *send_str | asio_streams.cpp:56:18:56:23 | [summary param] *0 in buffer | asio_streams.cpp:56:18:56:23 | [summary] to write: ReturnValue in buffer | asio_streams.cpp:100:44:100:62 | call to buffer |
| azure.cpp:257:5:257:8 | *resp | azure.cpp:113:16:113:19 | [summary param] this in Read | azure.cpp:113:16:113:19 | [summary param] *0 in Read [Return] | azure.cpp:257:16:257:21 | Read output argument |
| azure.cpp:262:5:262:8 | *resp | azure.cpp:114:16:114:26 | [summary param] this in ReadToCount | azure.cpp:114:16:114:26 | [summary param] *0 in ReadToCount [Return] | azure.cpp:262:23:262:28 | ReadToCount output argument |
| azure.cpp:266:38:266:41 | *resp | azure.cpp:115:30:115:38 | [summary param] this in ReadToEnd | azure.cpp:115:30:115:38 | [summary] to write: ReturnValue in ReadToEnd [element] | azure.cpp:266:44:266:52 | call to ReadToEnd [element] |
| azure.cpp:282:21:282:23 | *call to get | azure.cpp:115:30:115:38 | [summary param] this in ReadToEnd | azure.cpp:115:30:115:38 | [summary] to write: ReturnValue in ReadToEnd [element] | azure.cpp:282:28:282:36 | call to ReadToEnd [element] |
| azure.cpp:289:24:289:56 | call to GetHeader | azure.cpp:62:10:62:14 | [summary param] this in Value | azure.cpp:62:10:62:14 | [summary] to write: ReturnValue[*] in Value | azure.cpp:289:63:289:65 | call to Value |
| test.cpp:17:24:17:24 | x | test.cpp:4:5:4:17 | [summary param] 0 in ymlStepManual | test.cpp:4:5:4:17 | [summary] to write: ReturnValue in ymlStepManual | test.cpp:17:10:17:22 | call to ymlStepManual |
| test.cpp:21:27:21:27 | x | test.cpp:5:5:5:20 | [summary param] 0 in ymlStepGenerated | test.cpp:5:5:5:20 | [summary] to write: ReturnValue in ymlStepGenerated | test.cpp:21:10:21:25 | call to ymlStepGenerated |
| test.cpp:25:35:25:35 | x | test.cpp:6:5:6:27 | [summary param] 0 in ymlStepManual_with_body | test.cpp:6:5:6:27 | [summary] to write: ReturnValue in ymlStepManual_with_body | test.cpp:25:11:25:33 | call to ymlStepManual_with_body |
| test.cpp:32:41:32:41 | x | test.cpp:7:47:7:52 | value2 | test.cpp:7:5:7:30 | *ymlStepGenerated_with_body | test.cpp:32:11:32:36 | call to ymlStepGenerated_with_body | | test.cpp:32:41:32:41 | x | test.cpp:7:47:7:52 | value2 | test.cpp:7:5:7:30 | *ymlStepGenerated_with_body | test.cpp:32:11:32:36 | call to ymlStepGenerated_with_body |
| test.cpp:118:44:118:44 | *x | test.cpp:111:3:111:25 | [summary param] *0 in callWithNonTypeTemplate | test.cpp:111:3:111:25 | [summary] to write: ReturnValue in callWithNonTypeTemplate | test.cpp:118:11:118:42 | call to callWithNonTypeTemplate |
| test.cpp:134:45:134:45 | x | test.cpp:125:5:125:20 | [summary param] 0 in templateFunction | test.cpp:125:5:125:20 | [summary] to write: ReturnValue in templateFunction | test.cpp:134:13:134:43 | call to templateFunction |
| test.cpp:148:26:148:26 | x | test.cpp:140:4:140:11 | [summary param] 1 in function | test.cpp:140:4:140:11 | [summary] to write: ReturnValue in function | test.cpp:148:10:148:27 | call to function |
| test.cpp:157:26:157:26 | x | test.cpp:140:4:140:11 | [summary param] 1 in function | test.cpp:140:4:140:11 | [summary] to write: ReturnValue in function | test.cpp:157:13:157:20 | call to function |
| test.cpp:165:69:165:69 | x | test.cpp:128:5:128:21 | [summary param] 1 in templateFunction2 | test.cpp:128:5:128:21 | [summary] to write: ReturnValue in templateFunction2 | test.cpp:165:12:165:64 | call to templateFunction2 |
| test.cpp:172:51:172:51 | x | test.cpp:164:34:164:34 | x | test.cpp:164:7:164:7 | *templateFunction3 | test.cpp:172:13:172:44 | call to templateFunction3 | | test.cpp:172:51:172:51 | x | test.cpp:164:34:164:34 | x | test.cpp:164:7:164:7 | *templateFunction3 | test.cpp:172:13:172:44 | call to templateFunction3 |
| windows.cpp:27:36:27:38 | *cmd | windows.cpp:17:8:17:25 | [summary param] *0 in CommandLineToArgvA | windows.cpp:17:8:17:25 | [summary] to write: ReturnValue[**] in CommandLineToArgvA | windows.cpp:27:17:27:34 | **call to CommandLineToArgvA |
| windows.cpp:537:40:537:41 | *& ... | windows.cpp:473:17:473:37 | [summary param] *1 in RtlCopyVolatileMemory | windows.cpp:473:17:473:37 | [summary param] *0 in RtlCopyVolatileMemory [Return] | windows.cpp:537:27:537:37 | RtlCopyVolatileMemory output argument |
| windows.cpp:542:38:542:39 | *& ... | windows.cpp:479:17:479:35 | [summary param] *1 in RtlCopyDeviceMemory | windows.cpp:479:17:479:35 | [summary param] *0 in RtlCopyDeviceMemory [Return] | windows.cpp:542:25:542:35 | RtlCopyDeviceMemory output argument |
| windows.cpp:547:32:547:33 | *& ... | windows.cpp:485:6:485:18 | [summary param] *1 in RtlCopyMemory | windows.cpp:485:6:485:18 | [summary param] *0 in RtlCopyMemory [Return] | windows.cpp:547:19:547:29 | RtlCopyMemory output argument |
| windows.cpp:552:43:552:44 | *& ... | windows.cpp:493:6:493:29 | [summary param] *1 in RtlCopyMemoryNonTemporal | windows.cpp:493:6:493:29 | [summary param] *0 in RtlCopyMemoryNonTemporal [Return] | windows.cpp:552:30:552:40 | RtlCopyMemoryNonTemporal output argument |
| windows.cpp:561:39:561:44 | *buffer | windows.cpp:527:6:527:25 | [summary param] *1 in RtlInitUnicodeString | windows.cpp:527:6:527:25 | [summary param] *0 in RtlInitUnicodeString [Return] [*Buffer] | windows.cpp:561:26:561:36 | RtlInitUnicodeString output argument [*Buffer] |
| windows.cpp:563:40:563:50 | *& ... [*Buffer] | windows.cpp:510:6:510:25 | [summary param] *1 in RtlCopyUnicodeString [*Buffer] | windows.cpp:510:6:510:25 | [summary param] *0 in RtlCopyUnicodeString [Return] [*Buffer] | windows.cpp:563:26:563:37 | RtlCopyUnicodeString output argument [*Buffer] |
| windows.cpp:568:32:568:33 | *& ... | windows.cpp:515:6:515:18 | [summary param] *1 in RtlMoveMemory | windows.cpp:515:6:515:18 | [summary param] *0 in RtlMoveMemory [Return] | windows.cpp:568:19:568:29 | RtlMoveMemory output argument |
| windows.cpp:573:40:573:41 | *& ... | windows.cpp:521:17:521:37 | [summary param] *1 in RtlMoveVolatileMemory | windows.cpp:521:17:521:37 | [summary param] *0 in RtlMoveVolatileMemory [Return] | windows.cpp:573:27:573:37 | RtlMoveVolatileMemory output argument |
| windows.cpp:729:35:729:35 | *x | windows.cpp:714:6:714:20 | [summary param] *0 in WinHttpCrackUrl | windows.cpp:714:6:714:20 | [summary param] *3 in WinHttpCrackUrl [Return] | windows.cpp:729:44:729:57 | WinHttpCrackUrl output argument |
testFailures testFailures

View File

@@ -22,3 +22,5 @@ extensions:
- ["", "TemplateClass1<T>", False, "templateFunction<U>", "(T,U)", "", "Argument[0]", "ReturnValue", "value", "manual"] - ["", "TemplateClass1<T>", False, "templateFunction<U>", "(T,U)", "", "Argument[0]", "ReturnValue", "value", "manual"]
- ["", "TemplateClass1", True, "templateFunction2<U,V>", "(U,V)", "", "Argument[1]", "ReturnValue", "value", "manual"] - ["", "TemplateClass1", True, "templateFunction2<U,V>", "(U,V)", "", "Argument[1]", "ReturnValue", "value", "manual"]
- ["", "TemplateClass2<T,U>", True, "function", "(U,T)", "", "Argument[1]", "ReturnValue", "value", "manual"] - ["", "TemplateClass2<T,U>", True, "function", "(U,T)", "", "Argument[1]", "ReturnValue", "value", "manual"]
- ["", "", False, "read_field_from_struct", "", "", "Argument[*0].Field[MyNamespace::MyStructInNamespace::myField]", "ReturnValue", "value", "manual"]
- ["", "", False, "read_field_from_struct_2", "", "", "Argument[*0].Field[MyGlobalStruct::myField]", "ReturnValue", "value", "manual"]

View File

@@ -19,3 +19,5 @@
| test.cpp:149:10:149:10 | z | test-sink | | test.cpp:149:10:149:10 | z | test-sink |
| test.cpp:158:10:158:10 | z | test-sink | | test.cpp:158:10:158:10 | z | test-sink |
| test.cpp:173:10:173:10 | y | test-sink | | test.cpp:173:10:173:10 | y | test-sink |
| test.cpp:188:10:188:10 | x | test-sink |
| test.cpp:201:10:201:10 | x | test-sink |

View File

@@ -13,6 +13,8 @@
| test.cpp:146:10:146:18 | call to ymlSource | local | | test.cpp:146:10:146:18 | call to ymlSource | local |
| test.cpp:155:10:155:18 | call to ymlSource | local | | test.cpp:155:10:155:18 | call to ymlSource | local |
| test.cpp:170:10:170:18 | call to ymlSource | local | | test.cpp:170:10:170:18 | call to ymlSource | local |
| test.cpp:186:14:186:22 | call to ymlSource | local |
| test.cpp:199:14:199:22 | call to ymlSource | local |
| windows.cpp:22:15:22:29 | *call to GetCommandLineA | local | | windows.cpp:22:15:22:29 | *call to GetCommandLineA | local |
| windows.cpp:34:17:34:38 | *call to GetEnvironmentStringsA | local | | windows.cpp:34:17:34:38 | *call to GetEnvironmentStringsA | local |
| windows.cpp:39:36:39:38 | GetEnvironmentVariableA output argument | local | | windows.cpp:39:36:39:38 | GetEnvironmentVariableA output argument | local |

View File

@@ -172,3 +172,31 @@ void test_class1() {
auto y = c.templateFunction3<unsigned long>(0UL, x); auto y = c.templateFunction3<unsigned long>(0UL, x);
ymlSink(y); // $ ir ymlSink(y); // $ ir
} }
namespace MyNamespace {
struct MyStructInNamespace {
int myField;
};
}
int read_field_from_struct(MyNamespace::MyStructInNamespace* s);
void test_fully_qualified_field_test() {
MyNamespace::MyStructInNamespace s;
s.myField = ymlSource();
int x = read_field_from_struct(&s);
ymlSink(x); // $ ir
}
struct MyGlobalStruct {
int myField;
};
int read_field_from_struct_2(MyGlobalStruct* s);
void test_fully_qualified_field_test_2() {
MyGlobalStruct s;
s.myField = ymlSource();
int x = read_field_from_struct_2(&s);
ymlSink(x); // $ ir
}

View File

@@ -321,23 +321,23 @@ flowSummaryNode
| tests.cpp:155:5:155:28 | [summary param] 2 in madAndImplementedComplex | ParameterNode | madAndImplementedComplex | madAndImplementedComplex | | tests.cpp:155:5:155:28 | [summary param] 2 in madAndImplementedComplex | ParameterNode | madAndImplementedComplex | madAndImplementedComplex |
| tests.cpp:155:5:155:28 | [summary] to write: ReturnValue in madAndImplementedComplex | ReturnNode | madAndImplementedComplex | madAndImplementedComplex | | tests.cpp:155:5:155:28 | [summary] to write: ReturnValue in madAndImplementedComplex | ReturnNode | madAndImplementedComplex | madAndImplementedComplex |
| tests.cpp:160:5:160:24 | [summary param] 0 in madArg0FieldToReturn | ParameterNode | madArg0FieldToReturn | madArg0FieldToReturn | | tests.cpp:160:5:160:24 | [summary param] 0 in madArg0FieldToReturn | ParameterNode | madArg0FieldToReturn | madArg0FieldToReturn |
| tests.cpp:160:5:160:24 | [summary] read: Argument[0].Field[value] in madArg0FieldToReturn | | madArg0FieldToReturn | madArg0FieldToReturn | | tests.cpp:160:5:160:24 | [summary] read: Argument[0].Field[MyContainer::value]/Field[value] in madArg0FieldToReturn | | madArg0FieldToReturn | madArg0FieldToReturn |
| tests.cpp:160:5:160:24 | [summary] to write: ReturnValue in madArg0FieldToReturn | ReturnNode | madArg0FieldToReturn | madArg0FieldToReturn | | tests.cpp:160:5:160:24 | [summary] to write: ReturnValue in madArg0FieldToReturn | ReturnNode | madArg0FieldToReturn | madArg0FieldToReturn |
| tests.cpp:161:5:161:32 | [summary param] *0 in madArg0IndirectFieldToReturn | ParameterNode | madArg0IndirectFieldToReturn | madArg0IndirectFieldToReturn | | tests.cpp:161:5:161:32 | [summary param] *0 in madArg0IndirectFieldToReturn | ParameterNode | madArg0IndirectFieldToReturn | madArg0IndirectFieldToReturn |
| tests.cpp:161:5:161:32 | [summary] read: Argument[*0].Field[value] in madArg0IndirectFieldToReturn | | madArg0IndirectFieldToReturn | madArg0IndirectFieldToReturn | | tests.cpp:161:5:161:32 | [summary] read: Argument[*0].Field[MyContainer::value]/Field[value] in madArg0IndirectFieldToReturn | | madArg0IndirectFieldToReturn | madArg0IndirectFieldToReturn |
| tests.cpp:161:5:161:32 | [summary] to write: ReturnValue in madArg0IndirectFieldToReturn | ReturnNode | madArg0IndirectFieldToReturn | madArg0IndirectFieldToReturn | | tests.cpp:161:5:161:32 | [summary] to write: ReturnValue in madArg0IndirectFieldToReturn | ReturnNode | madArg0IndirectFieldToReturn | madArg0IndirectFieldToReturn |
| tests.cpp:162:5:162:32 | [summary param] 0 in madArg0FieldIndirectToReturn | ParameterNode | madArg0FieldIndirectToReturn | madArg0FieldIndirectToReturn | | tests.cpp:162:5:162:32 | [summary param] 0 in madArg0FieldIndirectToReturn | ParameterNode | madArg0FieldIndirectToReturn | madArg0FieldIndirectToReturn |
| tests.cpp:162:5:162:32 | [summary] read: Argument[0].Field[*ptr] in madArg0FieldIndirectToReturn | | madArg0FieldIndirectToReturn | madArg0FieldIndirectToReturn | | tests.cpp:162:5:162:32 | [summary] read: Argument[0].Field[*MyContainer::ptr]/Field[*ptr] in madArg0FieldIndirectToReturn | | madArg0FieldIndirectToReturn | madArg0FieldIndirectToReturn |
| tests.cpp:162:5:162:32 | [summary] to write: ReturnValue in madArg0FieldIndirectToReturn | ReturnNode | madArg0FieldIndirectToReturn | madArg0FieldIndirectToReturn | | tests.cpp:162:5:162:32 | [summary] to write: ReturnValue in madArg0FieldIndirectToReturn | ReturnNode | madArg0FieldIndirectToReturn | madArg0FieldIndirectToReturn |
| tests.cpp:163:13:163:32 | [summary param] 0 in madArg0ToReturnField | ParameterNode | madArg0ToReturnField | madArg0ToReturnField | | tests.cpp:163:13:163:32 | [summary param] 0 in madArg0ToReturnField | ParameterNode | madArg0ToReturnField | madArg0ToReturnField |
| tests.cpp:163:13:163:32 | [summary] to write: ReturnValue in madArg0ToReturnField | ReturnNode | madArg0ToReturnField | madArg0ToReturnField | | tests.cpp:163:13:163:32 | [summary] to write: ReturnValue in madArg0ToReturnField | ReturnNode | madArg0ToReturnField | madArg0ToReturnField |
| tests.cpp:163:13:163:32 | [summary] to write: ReturnValue.Field[value] in madArg0ToReturnField | | madArg0ToReturnField | madArg0ToReturnField | | tests.cpp:163:13:163:32 | [summary] to write: ReturnValue.Field[MyContainer::value]/Field[value] in madArg0ToReturnField | | madArg0ToReturnField | madArg0ToReturnField |
| tests.cpp:164:14:164:41 | [summary param] 0 in madArg0ToReturnIndirectField | ParameterNode | madArg0ToReturnIndirectField | madArg0ToReturnIndirectField | | tests.cpp:164:14:164:41 | [summary param] 0 in madArg0ToReturnIndirectField | ParameterNode | madArg0ToReturnIndirectField | madArg0ToReturnIndirectField |
| tests.cpp:164:14:164:41 | [summary] to write: ReturnValue[*] in madArg0ToReturnIndirectField | ReturnNode | madArg0ToReturnIndirectField | madArg0ToReturnIndirectField | | tests.cpp:164:14:164:41 | [summary] to write: ReturnValue[*] in madArg0ToReturnIndirectField | ReturnNode | madArg0ToReturnIndirectField | madArg0ToReturnIndirectField |
| tests.cpp:164:14:164:41 | [summary] to write: ReturnValue[*].Field[value] in madArg0ToReturnIndirectField | | madArg0ToReturnIndirectField | madArg0ToReturnIndirectField | | tests.cpp:164:14:164:41 | [summary] to write: ReturnValue[*].Field[MyContainer::value]/Field[value] in madArg0ToReturnIndirectField | | madArg0ToReturnIndirectField | madArg0ToReturnIndirectField |
| tests.cpp:165:13:165:40 | [summary param] 0 in madArg0ToReturnFieldIndirect | ParameterNode | madArg0ToReturnFieldIndirect | madArg0ToReturnFieldIndirect | | tests.cpp:165:13:165:40 | [summary param] 0 in madArg0ToReturnFieldIndirect | ParameterNode | madArg0ToReturnFieldIndirect | madArg0ToReturnFieldIndirect |
| tests.cpp:165:13:165:40 | [summary] to write: ReturnValue in madArg0ToReturnFieldIndirect | ReturnNode | madArg0ToReturnFieldIndirect | madArg0ToReturnFieldIndirect | | tests.cpp:165:13:165:40 | [summary] to write: ReturnValue in madArg0ToReturnFieldIndirect | ReturnNode | madArg0ToReturnFieldIndirect | madArg0ToReturnFieldIndirect |
| tests.cpp:165:13:165:40 | [summary] to write: ReturnValue.Field[*ptr] in madArg0ToReturnFieldIndirect | | madArg0ToReturnFieldIndirect | madArg0ToReturnFieldIndirect | | tests.cpp:165:13:165:40 | [summary] to write: ReturnValue.Field[*MyContainer::ptr]/Field[*ptr] in madArg0ToReturnFieldIndirect | | madArg0ToReturnFieldIndirect | madArg0ToReturnFieldIndirect |
| tests.cpp:284:7:284:19 | [summary param] 0 in madArg0ToSelf | ParameterNode | madArg0ToSelf | madArg0ToSelf | | tests.cpp:284:7:284:19 | [summary param] 0 in madArg0ToSelf | ParameterNode | madArg0ToSelf | madArg0ToSelf |
| tests.cpp:284:7:284:19 | [summary param] this in madArg0ToSelf | ParameterNode | madArg0ToSelf | madArg0ToSelf | | tests.cpp:284:7:284:19 | [summary param] this in madArg0ToSelf | ParameterNode | madArg0ToSelf | madArg0ToSelf |
| tests.cpp:284:7:284:19 | [summary] to write: Argument[this] in madArg0ToSelf | PostUpdateNode | madArg0ToSelf | madArg0ToSelf | | tests.cpp:284:7:284:19 | [summary] to write: Argument[this] in madArg0ToSelf | PostUpdateNode | madArg0ToSelf | madArg0ToSelf |
@@ -346,9 +346,9 @@ flowSummaryNode
| tests.cpp:287:7:287:20 | [summary param] 0 in madArg0ToField | ParameterNode | madArg0ToField | madArg0ToField | | tests.cpp:287:7:287:20 | [summary param] 0 in madArg0ToField | ParameterNode | madArg0ToField | madArg0ToField |
| tests.cpp:287:7:287:20 | [summary param] this in madArg0ToField | ParameterNode | madArg0ToField | madArg0ToField | | tests.cpp:287:7:287:20 | [summary param] this in madArg0ToField | ParameterNode | madArg0ToField | madArg0ToField |
| tests.cpp:287:7:287:20 | [summary] to write: Argument[this] in madArg0ToField | PostUpdateNode | madArg0ToField | madArg0ToField | | tests.cpp:287:7:287:20 | [summary] to write: Argument[this] in madArg0ToField | PostUpdateNode | madArg0ToField | madArg0ToField |
| tests.cpp:287:7:287:20 | [summary] to write: Argument[this].Field[val] in madArg0ToField | | madArg0ToField | madArg0ToField | | tests.cpp:287:7:287:20 | [summary] to write: Argument[this].Field[MyClass::val]/Field[val] in madArg0ToField | | madArg0ToField | madArg0ToField |
| tests.cpp:288:6:288:21 | [summary param] this in madFieldToReturn | ParameterNode | madFieldToReturn | madFieldToReturn | | tests.cpp:288:6:288:21 | [summary param] this in madFieldToReturn | ParameterNode | madFieldToReturn | madFieldToReturn |
| tests.cpp:288:6:288:21 | [summary] read: Argument[this].Field[val] in madFieldToReturn | | madFieldToReturn | madFieldToReturn | | tests.cpp:288:6:288:21 | [summary] read: Argument[this].Field[MyClass::val]/Field[val] in madFieldToReturn | | madFieldToReturn | madFieldToReturn |
| tests.cpp:288:6:288:21 | [summary] to write: ReturnValue in madFieldToReturn | ReturnNode | madFieldToReturn | madFieldToReturn | | tests.cpp:288:6:288:21 | [summary] to write: ReturnValue in madFieldToReturn | ReturnNode | madFieldToReturn | madFieldToReturn |
| tests.cpp:313:7:313:30 | [summary param] this in namespaceMadSelfToReturn | ParameterNode | namespaceMadSelfToReturn | namespaceMadSelfToReturn | | tests.cpp:313:7:313:30 | [summary param] this in namespaceMadSelfToReturn | ParameterNode | namespaceMadSelfToReturn | namespaceMadSelfToReturn |
| tests.cpp:313:7:313:30 | [summary] to write: ReturnValue in namespaceMadSelfToReturn | ReturnNode | namespaceMadSelfToReturn | namespaceMadSelfToReturn | | tests.cpp:313:7:313:30 | [summary] to write: ReturnValue in namespaceMadSelfToReturn | ReturnNode | namespaceMadSelfToReturn | namespaceMadSelfToReturn |
@@ -362,7 +362,7 @@ flowSummaryNode
| tests.cpp:435:9:435:38 | [summary] read: Argument[0].ReturnValue in madCallArg0ReturnToReturnFirst | OutNode | madCallArg0ReturnToReturnFirst | madCallArg0ReturnToReturnFirst | | tests.cpp:435:9:435:38 | [summary] read: Argument[0].ReturnValue in madCallArg0ReturnToReturnFirst | OutNode | madCallArg0ReturnToReturnFirst | madCallArg0ReturnToReturnFirst |
| tests.cpp:435:9:435:38 | [summary] to write: Argument[0].Parameter[this pointer] in madCallArg0ReturnToReturnFirst | ArgumentNode | madCallArg0ReturnToReturnFirst | madCallArg0ReturnToReturnFirst | | tests.cpp:435:9:435:38 | [summary] to write: Argument[0].Parameter[this pointer] in madCallArg0ReturnToReturnFirst | ArgumentNode | madCallArg0ReturnToReturnFirst | madCallArg0ReturnToReturnFirst |
| tests.cpp:435:9:435:38 | [summary] to write: ReturnValue in madCallArg0ReturnToReturnFirst | ReturnNode | madCallArg0ReturnToReturnFirst | madCallArg0ReturnToReturnFirst | | tests.cpp:435:9:435:38 | [summary] to write: ReturnValue in madCallArg0ReturnToReturnFirst | ReturnNode | madCallArg0ReturnToReturnFirst | madCallArg0ReturnToReturnFirst |
| tests.cpp:435:9:435:38 | [summary] to write: ReturnValue.Field[first] in madCallArg0ReturnToReturnFirst | | madCallArg0ReturnToReturnFirst | madCallArg0ReturnToReturnFirst | | tests.cpp:435:9:435:38 | [summary] to write: ReturnValue.Field[first]/Field[intPair::first] in madCallArg0ReturnToReturnFirst | | madCallArg0ReturnToReturnFirst | madCallArg0ReturnToReturnFirst |
| tests.cpp:436:6:436:25 | [summary param] 0 in madCallArg0WithValue | ParameterNode | madCallArg0WithValue | madCallArg0WithValue | | tests.cpp:436:6:436:25 | [summary param] 0 in madCallArg0WithValue | ParameterNode | madCallArg0WithValue | madCallArg0WithValue |
| tests.cpp:436:6:436:25 | [summary param] 1 in madCallArg0WithValue | ParameterNode | madCallArg0WithValue | madCallArg0WithValue | | tests.cpp:436:6:436:25 | [summary param] 1 in madCallArg0WithValue | ParameterNode | madCallArg0WithValue | madCallArg0WithValue |
| tests.cpp:436:6:436:25 | [summary] read: Argument[0].Parameter[0] in madCallArg0WithValue | PostUpdateNode | madCallArg0WithValue | madCallArg0WithValue | | tests.cpp:436:6:436:25 | [summary] read: Argument[0].Parameter[0] in madCallArg0WithValue | PostUpdateNode | madCallArg0WithValue | madCallArg0WithValue |

View File

@@ -11,12 +11,10 @@ edges
| nested.cpp:86:19:86:46 | *call to __builtin_alloca | nested.cpp:87:18:87:20 | *fmt | provenance | | | nested.cpp:86:19:86:46 | *call to __builtin_alloca | nested.cpp:87:18:87:20 | *fmt | provenance | |
| test.cpp:46:27:46:30 | **argv | test.cpp:130:20:130:26 | *access to array | provenance | | | test.cpp:46:27:46:30 | **argv | test.cpp:130:20:130:26 | *access to array | provenance | |
| test.cpp:167:31:167:34 | *data | test.cpp:170:12:170:14 | *res | provenance | DataFlowFunction | | test.cpp:167:31:167:34 | *data | test.cpp:170:12:170:14 | *res | provenance | DataFlowFunction |
| test.cpp:179:6:179:21 | [summary param] *2 in StringCchPrintfW | test.cpp:179:6:179:21 | [summary param] *0 in StringCchPrintfW [Return] | provenance | MaD:403 |
| test.cpp:193:32:193:34 | *str | test.cpp:195:31:195:33 | *str | provenance | | | test.cpp:193:32:193:34 | *str | test.cpp:195:31:195:33 | *str | provenance | |
| test.cpp:193:32:193:34 | *str | test.cpp:195:31:195:33 | *str | provenance | | | test.cpp:193:32:193:34 | *str | test.cpp:195:31:195:33 | *str | provenance | |
| test.cpp:193:32:193:34 | *str | test.cpp:197:11:197:14 | *wstr | provenance | TaintFunction | | test.cpp:193:32:193:34 | *str | test.cpp:197:11:197:14 | *wstr | provenance | TaintFunction |
| test.cpp:195:20:195:23 | StringCchPrintfW output argument | test.cpp:197:11:197:14 | *wstr | provenance | | | test.cpp:195:20:195:23 | StringCchPrintfW output argument | test.cpp:197:11:197:14 | *wstr | provenance | |
| test.cpp:195:31:195:33 | *str | test.cpp:179:6:179:21 | [summary param] *2 in StringCchPrintfW | provenance | |
| test.cpp:195:31:195:33 | *str | test.cpp:195:20:195:23 | StringCchPrintfW output argument | provenance | MaD:403 | | test.cpp:195:31:195:33 | *str | test.cpp:195:20:195:23 | StringCchPrintfW output argument | provenance | MaD:403 |
| test.cpp:204:25:204:36 | *call to get_string | test.cpp:204:25:204:36 | *call to get_string | provenance | | | test.cpp:204:25:204:36 | *call to get_string | test.cpp:204:25:204:36 | *call to get_string | provenance | |
| test.cpp:204:25:204:36 | *call to get_string | test.cpp:205:12:205:20 | *... + ... | provenance | | | test.cpp:204:25:204:36 | *call to get_string | test.cpp:205:12:205:20 | *... + ... | provenance | |
@@ -60,8 +58,6 @@ nodes
| test.cpp:130:20:130:26 | *access to array | semmle.label | *access to array | | test.cpp:130:20:130:26 | *access to array | semmle.label | *access to array |
| test.cpp:167:31:167:34 | *data | semmle.label | *data | | test.cpp:167:31:167:34 | *data | semmle.label | *data |
| test.cpp:170:12:170:14 | *res | semmle.label | *res | | test.cpp:170:12:170:14 | *res | semmle.label | *res |
| test.cpp:179:6:179:21 | [summary param] *0 in StringCchPrintfW [Return] | semmle.label | [summary param] *0 in StringCchPrintfW [Return] |
| test.cpp:179:6:179:21 | [summary param] *2 in StringCchPrintfW | semmle.label | [summary param] *2 in StringCchPrintfW |
| test.cpp:193:32:193:34 | *str | semmle.label | *str | | test.cpp:193:32:193:34 | *str | semmle.label | *str |
| test.cpp:195:20:195:23 | StringCchPrintfW output argument | semmle.label | StringCchPrintfW output argument | | test.cpp:195:20:195:23 | StringCchPrintfW output argument | semmle.label | StringCchPrintfW output argument |
| test.cpp:195:31:195:33 | *str | semmle.label | *str | | test.cpp:195:31:195:33 | *str | semmle.label | *str |
@@ -97,7 +93,6 @@ nodes
| test.cpp:245:25:245:36 | *call to get_string | semmle.label | *call to get_string | | test.cpp:245:25:245:36 | *call to get_string | semmle.label | *call to get_string |
| test.cpp:247:12:247:16 | *hello | semmle.label | *hello | | test.cpp:247:12:247:16 | *hello | semmle.label | *hello |
subpaths subpaths
| test.cpp:195:31:195:33 | *str | test.cpp:179:6:179:21 | [summary param] *2 in StringCchPrintfW | test.cpp:179:6:179:21 | [summary param] *0 in StringCchPrintfW [Return] | test.cpp:195:20:195:23 | StringCchPrintfW output argument |
#select #select
| NonConstantFormat.c:30:10:30:16 | *access to array | NonConstantFormat.c:28:27:28:30 | **argv | NonConstantFormat.c:30:10:30:16 | *access to array | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | NonConstantFormat.c:30:3:30:8 | call to printf | printf | | NonConstantFormat.c:30:10:30:16 | *access to array | NonConstantFormat.c:28:27:28:30 | **argv | NonConstantFormat.c:30:10:30:16 | *access to array | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | NonConstantFormat.c:30:3:30:8 | call to printf | printf |
| NonConstantFormat.c:41:9:41:45 | *call to any_random_function | NonConstantFormat.c:41:9:41:45 | *call to any_random_function | NonConstantFormat.c:41:9:41:45 | *call to any_random_function | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | NonConstantFormat.c:41:2:41:7 | call to printf | printf | | NonConstantFormat.c:41:9:41:45 | *call to any_random_function | NonConstantFormat.c:41:9:41:45 | *call to any_random_function | NonConstantFormat.c:41:9:41:45 | *call to any_random_function | The format string argument to $@ has a source which cannot be verified to originate from a string literal. | NonConstantFormat.c:41:2:41:7 | call to printf | printf |

View File

@@ -33,7 +33,6 @@ edges
| tests2.cpp:111:14:111:15 | *c1 [*ptr] | tests2.cpp:111:14:111:19 | *ptr | provenance | | | tests2.cpp:111:14:111:15 | *c1 [*ptr] | tests2.cpp:111:14:111:19 | *ptr | provenance | |
| tests2.cpp:111:14:111:15 | *c1 [*ptr] | tests2.cpp:111:17:111:19 | *ptr | provenance | | | tests2.cpp:111:14:111:15 | *c1 [*ptr] | tests2.cpp:111:17:111:19 | *ptr | provenance | |
| tests2.cpp:111:17:111:19 | *ptr | tests2.cpp:111:14:111:19 | *ptr | provenance | | | tests2.cpp:111:17:111:19 | *ptr | tests2.cpp:111:14:111:19 | *ptr | provenance | |
| tests2.cpp:120:5:120:21 | [summary param] *1 in zmq_msg_init_data | tests2.cpp:120:5:120:21 | [summary param] *0 in zmq_msg_init_data [Return] | provenance | MaD:4 |
| tests2.cpp:134:2:134:30 | *... = ... | tests2.cpp:138:23:138:34 | *message_data | provenance | Sink:MaD:2 | | tests2.cpp:134:2:134:30 | *... = ... | tests2.cpp:138:23:138:34 | *message_data | provenance | Sink:MaD:2 |
| tests2.cpp:134:2:134:30 | *... = ... | tests2.cpp:143:34:143:45 | *message_data | provenance | | | tests2.cpp:134:2:134:30 | *... = ... | tests2.cpp:143:34:143:45 | *message_data | provenance | |
| tests2.cpp:134:17:134:22 | *call to getenv | tests2.cpp:134:2:134:30 | *... = ... | provenance | | | tests2.cpp:134:17:134:22 | *call to getenv | tests2.cpp:134:2:134:30 | *... = ... | provenance | |
@@ -41,7 +40,6 @@ edges
| tests2.cpp:143:24:143:31 | zmq_msg_init_data output argument | tests2.cpp:147:20:147:27 | *& ... | provenance | Sink:MaD:1 | | tests2.cpp:143:24:143:31 | zmq_msg_init_data output argument | tests2.cpp:147:20:147:27 | *& ... | provenance | Sink:MaD:1 |
| tests2.cpp:143:24:143:31 | zmq_msg_init_data output argument | tests2.cpp:155:32:155:39 | *& ... | provenance | Sink:MaD:3 | | tests2.cpp:143:24:143:31 | zmq_msg_init_data output argument | tests2.cpp:155:32:155:39 | *& ... | provenance | Sink:MaD:3 |
| tests2.cpp:143:24:143:31 | zmq_msg_init_data output argument | tests2.cpp:158:20:158:27 | *& ... | provenance | Sink:MaD:1 | | tests2.cpp:143:24:143:31 | zmq_msg_init_data output argument | tests2.cpp:158:20:158:27 | *& ... | provenance | Sink:MaD:1 |
| tests2.cpp:143:34:143:45 | *message_data | tests2.cpp:120:5:120:21 | [summary param] *1 in zmq_msg_init_data | provenance | |
| tests2.cpp:143:34:143:45 | *message_data | tests2.cpp:143:24:143:31 | zmq_msg_init_data output argument | provenance | MaD:4 | | tests2.cpp:143:34:143:45 | *message_data | tests2.cpp:143:24:143:31 | zmq_msg_init_data output argument | provenance | MaD:4 |
| tests_sockets.cpp:26:15:26:20 | *call to getenv | tests_sockets.cpp:26:15:26:20 | *call to getenv | provenance | | | tests_sockets.cpp:26:15:26:20 | *call to getenv | tests_sockets.cpp:26:15:26:20 | *call to getenv | provenance | |
| tests_sockets.cpp:26:15:26:20 | *call to getenv | tests_sockets.cpp:39:19:39:22 | *path | provenance | | | tests_sockets.cpp:26:15:26:20 | *call to getenv | tests_sockets.cpp:39:19:39:22 | *path | provenance | |
@@ -78,8 +76,6 @@ nodes
| tests2.cpp:111:14:111:15 | *c1 [*ptr] | semmle.label | *c1 [*ptr] | | tests2.cpp:111:14:111:15 | *c1 [*ptr] | semmle.label | *c1 [*ptr] |
| tests2.cpp:111:14:111:19 | *ptr | semmle.label | *ptr | | tests2.cpp:111:14:111:19 | *ptr | semmle.label | *ptr |
| tests2.cpp:111:17:111:19 | *ptr | semmle.label | *ptr | | tests2.cpp:111:17:111:19 | *ptr | semmle.label | *ptr |
| tests2.cpp:120:5:120:21 | [summary param] *0 in zmq_msg_init_data [Return] | semmle.label | [summary param] *0 in zmq_msg_init_data [Return] |
| tests2.cpp:120:5:120:21 | [summary param] *1 in zmq_msg_init_data | semmle.label | [summary param] *1 in zmq_msg_init_data |
| tests2.cpp:134:2:134:30 | *... = ... | semmle.label | *... = ... | | tests2.cpp:134:2:134:30 | *... = ... | semmle.label | *... = ... |
| tests2.cpp:134:17:134:22 | *call to getenv | semmle.label | *call to getenv | | tests2.cpp:134:17:134:22 | *call to getenv | semmle.label | *call to getenv |
| tests2.cpp:138:23:138:34 | *message_data | semmle.label | *message_data | | tests2.cpp:138:23:138:34 | *message_data | semmle.label | *message_data |
@@ -100,4 +96,3 @@ nodes
| tests_sysconf.cpp:36:21:36:27 | confstr output argument | semmle.label | confstr output argument | | tests_sysconf.cpp:36:21:36:27 | confstr output argument | semmle.label | confstr output argument |
| tests_sysconf.cpp:39:19:39:25 | *pathbuf | semmle.label | *pathbuf | | tests_sysconf.cpp:39:19:39:25 | *pathbuf | semmle.label | *pathbuf |
subpaths subpaths
| tests2.cpp:143:34:143:45 | *message_data | tests2.cpp:120:5:120:21 | [summary param] *1 in zmq_msg_init_data | tests2.cpp:120:5:120:21 | [summary param] *0 in zmq_msg_init_data [Return] | tests2.cpp:143:24:143:31 | zmq_msg_init_data output argument |

View File

@@ -88,12 +88,12 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
private IEnumerable<string> GetFeedsFromNugetConfig(string nugetConfigPath) => private IEnumerable<string> GetFeedsFromNugetConfig(string nugetConfigPath) =>
GetFeeds(() => dotnet.GetNugetFeeds(nugetConfigPath)); GetFeeds(() => dotnet.GetNugetFeeds(nugetConfigPath));
private string FeedsToRestoreArgument(IEnumerable<string> feeds) public string FeedsToRestoreArgument(IEnumerable<string> feeds, string sourceArgumentPrefix)
{ {
// If there are no feeds, we want to override any default feeds that `dotnet restore` would use by passing a dummy source argument. // If there are no feeds, we want to override any default feeds that `restore` would use by passing a dummy source argument.
if (!feeds.Any()) if (!feeds.Any())
{ {
return $" -s \"{emptyPackageDirectory.DirInfo.FullName}\""; return $" {sourceArgumentPrefix} \"{emptyPackageDirectory.DirInfo.FullName}\"";
} }
// Add package sources. If any are present, they override all sources specified in // Add package sources. If any are present, they override all sources specified in
@@ -101,7 +101,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
var feedArgs = new StringBuilder(); var feedArgs = new StringBuilder();
foreach (var feed in feeds) foreach (var feed in feeds)
{ {
feedArgs.Append($" -s \"{feed}\""); feedArgs.Append($" {sourceArgumentPrefix} \"{feed}\"");
} }
return feedArgs.ToString(); return feedArgs.ToString();
@@ -112,17 +112,11 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
/// (1) Use the feeds we get from `dotnet nuget list source` /// (1) Use the feeds we get from `dotnet nuget list source`
/// (2) Use private registries, if they are configured /// (2) Use private registries, if they are configured
/// </summary> /// </summary>
/// <param name="path">Path to project/solution</param> /// <param name="path">Path to project/solution/packages.config</param>
/// <param name="reachableFeeds">The set of reachable NuGet feeds.</param> /// <param name="reachableFeeds">The set of reachable NuGet feeds.</param>
/// <returns>A string representing the NuGet sources argument for the restore command.</returns> /// <returns>The list of NuGet feeds to use for this restore.</returns>
public string? MakeRestoreSourcesArgument(string path, HashSet<string> reachableFeeds) public IEnumerable<string> FeedsToUse(string path, HashSet<string> reachableFeeds)
{ {
// Do not construct a set of explicit NuGet sources to use for restore.
if (!CheckNugetFeedResponsiveness && !HasPrivateRegistryFeeds)
{
return null;
}
// Find the path specific feeds. // Find the path specific feeds.
var folder = GetDirectoryName(path); var folder = GetDirectoryName(path);
var feedsToConsider = folder is not null ? GetFeedsFromFolder(folder).ToHashSet() : new HashSet<string>(); var feedsToConsider = folder is not null ? GetFeedsFromFolder(folder).ToHashSet() : new HashSet<string>();
@@ -136,7 +130,28 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
? feedsToConsider.Where(reachableFeeds.Contains) ? feedsToConsider.Where(reachableFeeds.Contains)
: feedsToConsider; : feedsToConsider;
return FeedsToRestoreArgument(feedsToUse); return feedsToUse;
}
/// <summary>
/// Constructs the list of NuGet sources to use for dotnet restore.
/// (1) Use the feeds we get from `dotnet nuget list source`
/// (2) Use private registries, if they are configured
/// </summary>
/// <param name="path">Path to project/solution</param>
/// <param name="reachableFeeds">The set of reachable NuGet feeds.</param>
/// <returns>A string representing the NuGet sources argument for the restore command.</returns>
public string? MakeDotnetRestoreSourcesArgument(string path, HashSet<string> reachableFeeds)
{
// Do not construct a set of explicit NuGet sources to use for restore.
if (!CheckNugetFeedResponsiveness && !HasPrivateRegistryFeeds)
{
return null;
}
var feedsToUse = FeedsToUse(path, reachableFeeds);
return FeedsToRestoreArgument(feedsToUse, "-s");
} }
private (int initialTimeout, int tryCount) GetFeedRequestSettings(bool isFallback) private (int initialTimeout, int tryCount) GetFeedRequestSettings(bool isFallback)

View File

@@ -110,58 +110,55 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
logger.LogInfo($"Checking NuGet feed responsiveness: {feedManager.CheckNugetFeedResponsiveness}"); logger.LogInfo($"Checking NuGet feed responsiveness: {feedManager.CheckNugetFeedResponsiveness}");
compilationInfoContainer.CompilationInfos.Add(("NuGet feed responsiveness checked", feedManager.CheckNugetFeedResponsiveness ? "1" : "0")); compilationInfoContainer.CompilationInfos.Add(("NuGet feed responsiveness checked", feedManager.CheckNugetFeedResponsiveness ? "1" : "0"));
HashSet<string> explicitFeeds = [];
HashSet<string> reachableFeeds = []; HashSet<string> reachableFeeds = [];
EmitNugetConfigDiagnostics();
// Find feeds that are configured in NuGet.config files and divide them into ones that
// are explicitly configured for the project or by a private registry, and "all feeds"
// (including inherited ones) from other locations on the host outside of the working directory.
(var explicitFeeds, var allFeeds) = feedManager.GetAllFeeds();
if (feedManager.CheckNugetFeedResponsiveness)
{
var inheritedFeeds = allFeeds.Except(explicitFeeds).ToHashSet();
if (inheritedFeeds.Count > 0)
{
compilationInfoContainer.CompilationInfos.Add(("Inherited NuGet feed count", inheritedFeeds.Count.ToString()));
}
var timeout = feedManager.CheckSpecifiedFeeds(explicitFeeds, out var reachableExplicitFeeds);
reachableFeeds.UnionWith(reachableExplicitFeeds);
var allExplicitReachable = explicitFeeds.Count == reachableExplicitFeeds.Count;
EmitUnreachableFeedsDiagnostics(allExplicitReachable);
if (timeout)
{
// If we experience a timeout, we use this fallback.
// todo: we could also check the reachability of the inherited nuget feeds, but to use those in the fallback we would need to handle authentication too.
var unresponsiveMissingPackageLocation = DownloadMissingPackagesFromSpecificFeeds([], explicitFeeds);
return unresponsiveMissingPackageLocation is null
? []
: [unresponsiveMissingPackageLocation];
}
// Inherited feeds should only be used, if they are indeed reachable (as they may be environment specific).
feedManager.CheckSpecifiedFeeds(inheritedFeeds, out var reachableInheritedFeeds);
reachableFeeds.UnionWith(reachableInheritedFeeds);
}
try try
{ {
EmitNugetConfigDiagnostics(); var packagesConfigRestore = PackagesConfigRestoreFactory.Create(fileProvider, legacyPackageDirectory, logger, feedManager, reachableFeeds);
var count = packagesConfigRestore.InstallPackages();
// Find feeds that are configured in NuGet.config files and divide them into ones that if (packagesConfigRestore.PackageCount > 0)
// are explicitly configured for the project or by a private registry, and "all feeds"
// (including inherited ones) from other locations on the host outside of the working directory.
(explicitFeeds, var allFeeds) = feedManager.GetAllFeeds();
if (feedManager.CheckNugetFeedResponsiveness)
{ {
var inheritedFeeds = allFeeds.Except(explicitFeeds).ToHashSet(); compilationInfoContainer.CompilationInfos.Add(("packages.config files", packagesConfigRestore.PackageCount.ToString()));
compilationInfoContainer.CompilationInfos.Add(("Successfully restored packages.config files", count.ToString()));
if (inheritedFeeds.Count > 0)
{
compilationInfoContainer.CompilationInfos.Add(("Inherited NuGet feed count", inheritedFeeds.Count.ToString()));
}
var timeout = feedManager.CheckSpecifiedFeeds(explicitFeeds, out var reachableExplicitFeeds);
reachableFeeds.UnionWith(reachableExplicitFeeds);
var allExplicitReachable = explicitFeeds.Count == reachableExplicitFeeds.Count;
EmitUnreachableFeedsDiagnostics(allExplicitReachable);
if (timeout)
{
// If we experience a timeout, we use this fallback.
// todo: we could also check the reachability of the inherited nuget feeds, but to use those in the fallback we would need to handle authentication too.
var unresponsiveMissingPackageLocation = DownloadMissingPackagesFromSpecificFeeds([], explicitFeeds);
return unresponsiveMissingPackageLocation is null
? []
: [unresponsiveMissingPackageLocation];
}
// Inherited feeds should only be used, if they are indeed reachable (as they may be environment specific).
feedManager.CheckSpecifiedFeeds(inheritedFeeds, out var reachableInheritedFeeds);
reachableFeeds.UnionWith(reachableInheritedFeeds);
} }
using (var packagesConfigRestore = PackagesConfigRestoreFactory.Create(fileProvider, legacyPackageDirectory, logger, feedManager.IsDefaultFeedReachable))
{
var count = packagesConfigRestore.InstallPackages();
if (packagesConfigRestore.PackageCount > 0)
{
compilationInfoContainer.CompilationInfos.Add(("packages.config files", packagesConfigRestore.PackageCount.ToString()));
compilationInfoContainer.CompilationInfos.Add(("Successfully restored packages.config files", count.ToString()));
}
}
var nugetPackageDlls = legacyPackageDirectory.DirInfo.GetFiles("*.dll", new EnumerationOptions { RecurseSubdirectories = true }); var nugetPackageDlls = legacyPackageDirectory.DirInfo.GetFiles("*.dll", new EnumerationOptions { RecurseSubdirectories = true });
var nugetPackageDllPaths = nugetPackageDlls.Select(f => f.FullName).ToHashSet(); var nugetPackageDllPaths = nugetPackageDlls.Select(f => f.FullName).ToHashSet();
@@ -239,7 +236,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
var projects = fileProvider.Solutions.SelectMany(solution => var projects = fileProvider.Solutions.SelectMany(solution =>
{ {
logger.LogInfo($"Restoring solution {solution}..."); logger.LogInfo($"Restoring solution {solution}...");
var nugetSources = feedManager.MakeRestoreSourcesArgument(solution, reachableFeeds); var nugetSources = feedManager.MakeDotnetRestoreSourcesArgument(solution, reachableFeeds);
var res = dotnet.Restore(new(solution, PackageDirectory.DirInfo.FullName, ForceDotnetRefAssemblyFetching: true, NugetSources: nugetSources, TargetWindows: isWindows)); var res = dotnet.Restore(new(solution, PackageDirectory.DirInfo.FullName, ForceDotnetRefAssemblyFetching: true, NugetSources: nugetSources, TargetWindows: isWindows));
if (res.Success) if (res.Success)
{ {
@@ -288,7 +285,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
foreach (var project in projectGroup) foreach (var project in projectGroup)
{ {
logger.LogInfo($"Restoring project {project}..."); logger.LogInfo($"Restoring project {project}...");
var nugetSources = feedManager.MakeRestoreSourcesArgument(project, reachableFeeds); var nugetSources = feedManager.MakeDotnetRestoreSourcesArgument(project, reachableFeeds);
var res = dotnet.Restore(new(project, PackageDirectory.DirInfo.FullName, ForceDotnetRefAssemblyFetching: true, NugetSources: nugetSources, TargetWindows: isWindows)); var res = dotnet.Restore(new(project, PackageDirectory.DirInfo.FullName, ForceDotnetRefAssemblyFetching: true, NugetSources: nugetSources, TargetWindows: isWindows));
assets.AddDependenciesRange(res.AssetsFilePaths); assets.AddDependenciesRange(res.AssetsFilePaths);
lock (sync) lock (sync)

View File

@@ -7,7 +7,7 @@ using Semmle.Util;
namespace Semmle.Extraction.CSharp.DependencyFetching namespace Semmle.Extraction.CSharp.DependencyFetching
{ {
internal interface IPackagesConfigRestore : IDisposable internal interface IPackagesConfigRestore
{ {
/// <summary> /// <summary>
/// The number of packages.config files found in the source tree. /// The number of packages.config files found in the source tree.
@@ -33,11 +33,11 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
/// </summary> /// </summary>
internal class PackagesConfigRestoreFactory internal class PackagesConfigRestoreFactory
{ {
public static IPackagesConfigRestore Create(FileProvider fileProvider, DependencyDirectory packageDirectory, Semmle.Util.Logging.ILogger logger, Func<bool> useDefaultFeed) public static IPackagesConfigRestore Create(FileProvider fileProvider, DependencyDirectory packageDirectory, Semmle.Util.Logging.ILogger logger, FeedManager feedManager, HashSet<string> reachableFeeds)
{ {
if (SystemBuildActions.Instance.IsWindows() || SystemBuildActions.Instance.IsMonoInstalled()) if (SystemBuildActions.Instance.IsWindows() || SystemBuildActions.Instance.IsMonoInstalled())
{ {
return new NugetExeWrapper(fileProvider, packageDirectory, logger, useDefaultFeed); return new NugetExeWrapper(fileProvider, packageDirectory, logger, feedManager, reachableFeeds);
} }
return new NoOpPackagesConfig(fileProvider.PackagesConfigs, logger); return new NoOpPackagesConfig(fileProvider.PackagesConfigs, logger);
@@ -55,8 +55,6 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
public int PackageCount => fileProvider.PackagesConfigs.Count; public int PackageCount => fileProvider.PackagesConfigs.Count;
private readonly string? backupNugetConfig;
private readonly string? nugetConfigPath;
private readonly FileProvider fileProvider; private readonly FileProvider fileProvider;
/// <summary> /// <summary>
@@ -65,57 +63,30 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
/// so as to not trample the source tree. /// so as to not trample the source tree.
/// </summary> /// </summary>
private readonly DependencyDirectory packageDirectory; private readonly DependencyDirectory packageDirectory;
private readonly FeedManager feedManager;
private readonly HashSet<string> reachableFeeds;
private bool IsWindows => SystemBuildActions.Instance.IsWindows(); private bool IsWindows => SystemBuildActions.Instance.IsWindows();
private bool? isDefaultFeedReachable;
private bool IsDefaultFeedReachable =>
isDefaultFeedReachable ??= feedManager.IsDefaultFeedReachable();
/// <summary> /// <summary>
/// Create the package manager for a specified source tree. /// Create the package manager for a specified source tree.
/// </summary> /// </summary>
public NugetExeWrapper(FileProvider fileProvider, DependencyDirectory packageDirectory, Semmle.Util.Logging.ILogger logger, Func<bool> useDefaultFeed) public NugetExeWrapper(FileProvider fileProvider, DependencyDirectory packageDirectory, Semmle.Util.Logging.ILogger logger, FeedManager feedManager, HashSet<string> reachableFeeds)
{ {
this.fileProvider = fileProvider; this.fileProvider = fileProvider;
this.packageDirectory = packageDirectory; this.packageDirectory = packageDirectory;
this.logger = logger; this.logger = logger;
this.feedManager = feedManager;
this.reachableFeeds = reachableFeeds;
if (fileProvider.PackagesConfigs.Count > 0) if (fileProvider.PackagesConfigs.Count > 0)
{ {
logger.LogInfo($"Found packages.config files, trying to use nuget.exe for package restore"); logger.LogInfo($"Found packages.config files, trying to use nuget.exe for package restore");
nugetExe = ResolveNugetExe(); nugetExe = ResolveNugetExe();
if (!HasPackageSource() && useDefaultFeed())
{
// We only modify or add a top level nuget.config file
nugetConfigPath = Path.Join(fileProvider.SourceDir.FullName, "nuget.config");
try
{
if (File.Exists(nugetConfigPath))
{
var tempFolderPath = FileUtils.GetTemporaryWorkingDirectory(out _);
do
{
backupNugetConfig = Path.Join(tempFolderPath, Path.GetRandomFileName());
}
while (File.Exists(backupNugetConfig));
File.Copy(nugetConfigPath, backupNugetConfig, true);
}
else
{
File.WriteAllText(nugetConfigPath,
"""
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
</packageSources>
</configuration>
""");
}
AddDefaultPackageSource(nugetConfigPath);
}
catch (Exception e)
{
logger.LogError($"Failed to add default package source to {nugetConfigPath}: {e}");
}
}
} }
} }
@@ -198,6 +169,21 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
{ {
logger.LogInfo($"Restoring file \"{packagesConfig}\"..."); logger.LogInfo($"Restoring file \"{packagesConfig}\"...");
var sourcesArgument = "";
var feedsToUse = feedManager.FeedsToUse(packagesConfig, reachableFeeds).ToList();
var useDefaultFeed = feedsToUse.Count == 0 && IsDefaultFeedReachable;
// Explicitly construct the sources to be used for the restore command when checking feed
// responsiveness, using private registries, or falling back to nuget.org.
if (feedManager.CheckNugetFeedResponsiveness || feedManager.HasPrivateRegistryFeeds || useDefaultFeed)
{
if (useDefaultFeed)
{
feedsToUse.Add(FeedManager.PublicNugetOrgFeed);
}
sourcesArgument = feedManager.FeedsToRestoreArgument(feedsToUse, "-Source");
}
/* Use nuget.exe to install a package. /* Use nuget.exe to install a package.
* Note that there is a clutch of NuGet assemblies which could be used to * Note that there is a clutch of NuGet assemblies which could be used to
* invoke this directly, which would arguably be nicer. However they are * invoke this directly, which would arguably be nicer. However they are
@@ -208,12 +194,12 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
if (RunWithMono) if (RunWithMono)
{ {
exe = "mono"; exe = "mono";
args = $"\"{nugetExe}\" install -OutputDirectory \"{packageDirectory}\" \"{packagesConfig}\""; args = $"\"{nugetExe}\" install -OutputDirectory \"{packageDirectory}\" {sourcesArgument} \"{packagesConfig}\"";
} }
else else
{ {
exe = nugetExe!; exe = nugetExe!;
args = $"install -OutputDirectory \"{packageDirectory}\" \"{packagesConfig}\""; args = $"install -OutputDirectory \"{packageDirectory}\" {sourcesArgument} \"{packagesConfig}\"";
} }
var pi = new ProcessStartInfo(exe, args) var pi = new ProcessStartInfo(exe, args)
@@ -246,98 +232,6 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
{ {
return fileProvider.PackagesConfigs.Count(TryRestoreNugetPackage); return fileProvider.PackagesConfigs.Count(TryRestoreNugetPackage);
} }
private bool HasPackageSource()
{
if (IsWindows)
{
return true;
}
try
{
logger.LogInfo("Checking if default package source is available...");
RunMonoNugetCommand("sources list -ForceEnglishOutput", out var stdout);
if (stdout.All(line => line != "No sources found."))
{
return true;
}
return false;
}
catch (Exception e)
{
logger.LogWarning($"Failed to check if default package source is added: {e}");
return true;
}
}
private void RunMonoNugetCommand(string command, out IList<string> stdout)
{
string exe, args;
if (RunWithMono)
{
exe = "mono";
args = $"\"{nugetExe}\" {command}";
}
else
{
exe = nugetExe!;
args = command;
}
var pi = new ProcessStartInfo(exe, args)
{
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false
};
var threadId = Environment.CurrentManagedThreadId;
void onOut(string s) => logger.LogDebug(s, threadId);
void onError(string s) => logger.LogError(s, threadId);
pi.ReadOutput(out stdout, onOut, onError);
}
private void AddDefaultPackageSource(string nugetConfig)
{
logger.LogInfo("Adding default package source...");
RunMonoNugetCommand($"sources add -Name DefaultNugetOrg -Source {FeedManager.PublicNugetOrgFeed} -ConfigFile \"{nugetConfig}\"", out _);
}
public void Dispose()
{
if (nugetConfigPath is null)
{
return;
}
try
{
if (backupNugetConfig is null)
{
logger.LogInfo("Removing nuget.config file");
File.Delete(nugetConfigPath);
return;
}
logger.LogInfo("Reverting nuget.config file content");
// The content of the original nuget.config file is reverted without changing the file's attributes or casing:
using (var backup = File.OpenRead(backupNugetConfig))
using (var current = File.OpenWrite(nugetConfigPath))
{
current.SetLength(0); // Truncate file
backup.CopyTo(current); // Restore original content
}
logger.LogInfo("Deleting backup nuget.config file");
File.Delete(backupNugetConfig);
}
catch (Exception exc)
{
logger.LogError($"Failed to restore original nuget.config file: {exc}");
}
}
} }
private class NoOpPackagesConfig : IPackagesConfigRestore private class NoOpPackagesConfig : IPackagesConfigRestore
@@ -361,8 +255,6 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
} }
return 0; return 0;
} }
public void Dispose() { }
} }
} }
} }

View File

@@ -0,0 +1,4 @@
---
category: majorAnalysis
---
* Simplified and streamlined the use of NuGet sources when downloading dependencies via `[mono] nuget.exe` in `build-mode: none`: NuGet sources are now supplied via the `-Source` flag instead of moving or creating `nuget.config` files in the checked-out repository, private registries are used if configured, and only reachable feeds are used when NuGet feed checking is enabled (the default).

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Improved models for the `log/slog` package (Go 1.21+), including `*slog.Logger` methods, `With`/`WithGroup`, and `Attr`/`Value` helpers, improving coverage for the `go/log-injection` and `go/clear-text-logging` queries.

View File

@@ -27,3 +27,27 @@ extensions:
- ["log/slog", "Logger", True, "ErrorContext", "", "", "Argument[1..2]", "log-injection", "manual"] - ["log/slog", "Logger", True, "ErrorContext", "", "", "Argument[1..2]", "log-injection", "manual"]
- ["log/slog", "Logger", True, "Log", "", "", "Argument[2..3]", "log-injection", "manual"] - ["log/slog", "Logger", True, "Log", "", "", "Argument[2..3]", "log-injection", "manual"]
- ["log/slog", "Logger", True, "LogAttrs", "", "", "Argument[2..3]", "log-injection", "manual"] - ["log/slog", "Logger", True, "LogAttrs", "", "", "Argument[2..3]", "log-injection", "manual"]
# With/WithGroup add attributes that are included in every subsequent log call.
- ["log/slog", "", False, "With", "", "", "Argument[0]", "log-injection", "manual"]
- ["log/slog", "Logger", True, "With", "", "", "Argument[0]", "log-injection", "manual"]
- ["log/slog", "Logger", True, "WithGroup", "", "", "Argument[0]", "log-injection", "manual"]
- addsTo:
pack: codeql/go-all
extensible: summaryModel
data:
# Constructors for Attr that can carry a tainted string into the result.
- ["log/slog", "", False, "Any", "", "", "Argument[0..1]", "ReturnValue", "taint", "manual"]
- ["log/slog", "", False, "Group", "", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["log/slog", "", False, "Group", "", "", "Argument[1].ArrayElement", "ReturnValue", "taint", "manual"]
- ["log/slog", "", False, "GroupAttrs", "", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["log/slog", "", False, "GroupAttrs", "", "", "Argument[1].ArrayElement", "ReturnValue", "taint", "manual"]
- ["log/slog", "", False, "String", "", "", "Argument[0..1]", "ReturnValue", "taint", "manual"]
# Constructors for Value that can carry a tainted string into the result.
- ["log/slog", "", False, "AnyValue", "", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["log/slog", "", False, "GroupValue", "", "", "Argument[0].ArrayElement", "ReturnValue", "taint", "manual"]
- ["log/slog", "", False, "StringValue", "", "", "Argument[0]", "ReturnValue", "taint", "manual"]
# Methods that read a string back out of an Attr or Value.
- ["log/slog", "Attr", True, "String", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"]
- ["log/slog", "Value", True, "Any", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"]
- ["log/slog", "Value", True, "Group", "", "", "Argument[receiver]", "ReturnValue.ArrayElement", "taint", "manual"]
- ["log/slog", "Value", True, "String", "", "", "Argument[receiver]", "ReturnValue", "taint", "manual"]

View File

@@ -37,4 +37,9 @@ func slogTest() {
slog.InfoContext(ctx, text, key, v) // $ logger=text logger=key logger=v slog.InfoContext(ctx, text, key, v) // $ logger=text logger=key logger=v
slog.Log(ctx, slog.LevelInfo, text, key, v) // $ logger=text logger=key logger=v slog.Log(ctx, slog.LevelInfo, text, key, v) // $ logger=text logger=key logger=v
slog.LogAttrs(ctx, slog.LevelInfo, text, attr) // $ logger=text logger=attr slog.LogAttrs(ctx, slog.LevelInfo, text, attr) // $ logger=text logger=attr
// With/WithGroup add attributes that are included in every subsequent log call.
logger.With(key, v) // $ logger=key logger=v
logger.WithGroup(text) // $ logger=text
slog.With(key, v) // $ logger=key logger=v
} }

View File

@@ -0,0 +1,2 @@
reverseRead
| test.go:114:21:114:33 | call to Group | Origin of readStep is missing a PostUpdateNode. |

View File

@@ -0,0 +1,2 @@
invalidModelRow
testFailures

View File

@@ -0,0 +1,14 @@
import go
import semmle.go.dataflow.ExternalFlow
import ModelValidation
import utils.test.InlineFlowTest
module Config implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
source.(DataFlow::CallNode).getTarget().getName() = ["getUntrustedData", "getUntrustedString"]
}
predicate isSink(DataFlow::Node sink) { sink = any(LoggerCall log).getAMessageComponent() }
}
import FlowTest<Config, Config>

View File

@@ -0,0 +1,3 @@
module codeql-go-tests/frameworks/slog
go 1.26

View File

@@ -0,0 +1,115 @@
package main
import (
"context"
"log/slog"
)
func main() {}
func getUntrustedData() interface{} { return nil }
func getUntrustedString() string {
return "tainted string"
}
// Package-level convenience functions.
func testSlogDebug() {
slog.Debug(getUntrustedString()) // $ hasValueFlow="call to getUntrustedString"
slog.Debug("msg", "key", getUntrustedData()) // $ hasValueFlow="call to getUntrustedData"
slog.Debug("msg", slog.String("key", getUntrustedString())) // $ hasTaintFlow="call to String"
}
func testSlogInfo() {
slog.Info(getUntrustedString()) // $ hasValueFlow="call to getUntrustedString"
slog.Info("msg", slog.Any("key", getUntrustedData())) // $ hasTaintFlow="call to Any"
slog.Info("msg", slog.String("key", getUntrustedString())) // $ hasTaintFlow="call to String"
}
func testSlogWarn() {
slog.Warn(getUntrustedString()) // $ hasValueFlow="call to getUntrustedString"
slog.Warn("msg", slog.String("key", getUntrustedString())) // $ hasTaintFlow="call to String"
}
func testSlogError() {
slog.Error(getUntrustedString()) // $ hasValueFlow="call to getUntrustedString"
slog.Error("msg", slog.String("key", getUntrustedString())) // $ hasTaintFlow="call to String"
}
func testSlogContextVariants(ctx context.Context) {
slog.DebugContext(ctx, getUntrustedString()) // $ hasValueFlow="call to getUntrustedString"
slog.InfoContext(ctx, getUntrustedString()) // $ hasValueFlow="call to getUntrustedString"
slog.WarnContext(ctx, getUntrustedString()) // $ hasValueFlow="call to getUntrustedString"
slog.ErrorContext(ctx, getUntrustedString()) // $ hasValueFlow="call to getUntrustedString"
slog.InfoContext(ctx, "msg", slog.String("key", getUntrustedString())) // $ hasTaintFlow="call to String"
}
func testSlogLog(ctx context.Context) {
slog.Log(ctx, slog.LevelInfo, getUntrustedString()) // $ hasValueFlow="call to getUntrustedString"
slog.Log(ctx, slog.LevelInfo, "msg", slog.String("key", getUntrustedString())) // $ hasTaintFlow="call to String"
slog.LogAttrs(ctx, slog.LevelInfo, getUntrustedString()) // $ hasValueFlow="call to getUntrustedString"
slog.LogAttrs(ctx, slog.LevelInfo, "msg", slog.String("key", getUntrustedString())) // $ hasTaintFlow="call to String"
}
// Methods on *slog.Logger.
func testLoggerMethods(logger *slog.Logger, ctx context.Context) {
logger.Debug(getUntrustedString()) // $ hasValueFlow="call to getUntrustedString"
logger.Info(getUntrustedString()) // $ hasValueFlow="call to getUntrustedString"
logger.Warn(getUntrustedString()) // $ hasValueFlow="call to getUntrustedString"
logger.Error(getUntrustedString()) // $ hasValueFlow="call to getUntrustedString"
logger.Info("msg", slog.Any("key", getUntrustedData())) // $ hasTaintFlow="call to Any"
logger.InfoContext(ctx, getUntrustedString()) // $ hasValueFlow="call to getUntrustedString"
logger.Log(ctx, slog.LevelInfo, getUntrustedString()) // $ hasValueFlow="call to getUntrustedString"
logger.LogAttrs(ctx, slog.LevelInfo, "msg", slog.String("key", getUntrustedString())) // $ hasTaintFlow="call to String"
}
// With, Logger.With and Logger.WithGroup. Note that for ease of modeling we make these functions
// sinks, although strictly speaking we should consider logging functions called on the returned
// loggers as the sinks.
func testWith(logger *slog.Logger) {
logger1 := logger.With(slog.String("key", getUntrustedString())) // $ hasTaintFlow="call to String"
logger1.Info("hello world")
logger2 := logger.With(slog.Any(getUntrustedString(), nil)) // $ hasTaintFlow="call to Any"
logger2.Info("hello world")
logger.With("key", getUntrustedData()).Info("hello world") // $ hasValueFlow="call to getUntrustedData"
}
func testPackageWith() {
logger := slog.With(slog.String("key", getUntrustedString())) // $ hasTaintFlow="call to String"
logger.Info("hello world")
slog.With("key", getUntrustedData()).Info("hello world") // $ hasValueFlow="call to getUntrustedData"
}
func testWithGroup(logger *slog.Logger) {
grouped := logger.WithGroup(getUntrustedString()) // $ hasValueFlow="call to getUntrustedString"
grouped.Info("hello world")
}
// Summary models: functions relating to Attr/Value that propagate strings.
func testAttrConstructors(logger *slog.Logger) {
logger.Info("msg", slog.Group("group", slog.String("key", getUntrustedString()))) // $ hasTaintFlow="call to Group"
logger.Info("msg", slog.GroupAttrs("group", slog.String("key", getUntrustedString()))) // $ hasTaintFlow="call to GroupAttrs"
}
func testValueConstructors(logger *slog.Logger) {
logger.Info("msg", "key", slog.AnyValue(getUntrustedString())) // $ hasTaintFlow="call to AnyValue"
logger.Info("msg", "key", slog.StringValue(getUntrustedString())) // $ hasTaintFlow="call to StringValue"
attr := slog.String("key", getUntrustedString())
logger.Info("msg", "key", slog.GroupValue(attr)) // $ hasTaintFlow="call to GroupValue"
}
func testAttrAndValueAccessors(logger *slog.Logger) {
attr := slog.String("key", getUntrustedString())
logger.Info("msg", "key", attr.String()) // $ hasTaintFlow="call to String"
v := slog.AnyValue(getUntrustedString())
logger.Info("msg", "key", v.Any()) // $ hasTaintFlow="call to Any"
logger.Info("msg", "key", v.String()) // $ hasTaintFlow="call to String"
group := slog.GroupValue(slog.String("key", getUntrustedString()))
logger.Info("msg", group.Group()[0]) // $ hasTaintFlow="index expression"
}

View File

@@ -29,8 +29,3 @@ nodes
| BadMacUse.java:146:48:146:57 | ciphertext : byte[] | semmle.label | ciphertext : byte[] | | BadMacUse.java:146:48:146:57 | ciphertext : byte[] | semmle.label | ciphertext : byte[] |
| BadMacUse.java:152:42:152:51 | ciphertext | semmle.label | ciphertext | | BadMacUse.java:152:42:152:51 | ciphertext | semmle.label | ciphertext |
subpaths subpaths
testFailures
| BadMacUse.java:50:56:50:66 | // $ Source | Missing result: Source |
| BadMacUse.java:63:118:63:128 | // $ Source | Missing result: Source |
| BadMacUse.java:92:31:92:35 | bytes : byte[] | Unexpected result: Source |
| BadMacUse.java:146:95:146:105 | // $ Source | Missing result: Source |

View File

@@ -30,8 +30,3 @@ nodes
| BadMacUse.java:118:83:118:84 | iv : byte[] | semmle.label | iv : byte[] | | BadMacUse.java:118:83:118:84 | iv : byte[] | semmle.label | iv : byte[] |
| BadMacUse.java:124:42:124:51 | ciphertext | semmle.label | ciphertext | | BadMacUse.java:124:42:124:51 | ciphertext | semmle.label | ciphertext |
subpaths subpaths
testFailures
| BadMacUse.java:63:118:63:128 | // $ Source | Missing result: Source |
| BadMacUse.java:92:16:92:36 | doFinal(...) : byte[] | Unexpected result: Source |
| BadMacUse.java:124:42:124:51 | ciphertext | Unexpected result: Alert |
| BadMacUse.java:146:95:146:105 | // $ Source | Missing result: Source |

View File

@@ -44,8 +44,3 @@ nodes
| BadMacUse.java:146:48:146:57 | ciphertext : byte[] [[]] : Object | semmle.label | ciphertext : byte[] [[]] : Object | | BadMacUse.java:146:48:146:57 | ciphertext : byte[] [[]] : Object | semmle.label | ciphertext : byte[] [[]] : Object |
| BadMacUse.java:152:42:152:51 | ciphertext | semmle.label | ciphertext | | BadMacUse.java:152:42:152:51 | ciphertext | semmle.label | ciphertext |
subpaths subpaths
testFailures
| BadMacUse.java:50:56:50:66 | // $ Source | Missing result: Source |
| BadMacUse.java:139:79:139:90 | input : byte[] | Unexpected result: Source |
| BadMacUse.java:146:95:146:105 | // $ Source | Missing result: Source |
| BadMacUse.java:152:42:152:51 | ciphertext | Unexpected result: Alert |

View File

@@ -47,7 +47,7 @@ class BadMacUse {
SecretKey encryptionKey = new SecretKeySpec(encryptionKeyBytes, "AES"); SecretKey encryptionKey = new SecretKeySpec(encryptionKeyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, encryptionKey, new SecureRandom()); cipher.init(Cipher.DECRYPT_MODE, encryptionKey, new SecureRandom());
byte[] plaintext = cipher.doFinal(ciphertext); // $ Source byte[] plaintext = cipher.doFinal(ciphertext); // $ Source[java/quantum/examples/bad-mac-order-decrypt-to-mac]
// Now verify MAC (too late) // Now verify MAC (too late)
SecretKey macKey = new SecretKeySpec(macKeyBytes, "HmacSHA256"); SecretKey macKey = new SecretKeySpec(macKeyBytes, "HmacSHA256");
@@ -60,7 +60,7 @@ class BadMacUse {
} }
} }
public void BadMacOnPlaintext(byte[] encryptionKeyBytes, byte[] macKeyBytes, byte[] plaintext) throws Exception {// $ Source public void BadMacOnPlaintext(byte[] encryptionKeyBytes, byte[] macKeyBytes, byte[] plaintext) throws Exception {// $ Source[java/quantum/examples/bad-mac-order-encrypt-plaintext-also-in-mac]
// Create keys directly from provided byte arrays // Create keys directly from provided byte arrays
SecretKey encryptionKey = new SecretKeySpec(encryptionKeyBytes, "AES"); SecretKey encryptionKey = new SecretKeySpec(encryptionKeyBytes, "AES");
SecretKey macKey = new SecretKeySpec(macKeyBytes, "HmacSHA256"); SecretKey macKey = new SecretKeySpec(macKeyBytes, "HmacSHA256");
@@ -89,7 +89,7 @@ class BadMacUse {
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
cipher.init(mode, secretKeySpec, ivParameterSpec); cipher.init(mode, secretKeySpec, ivParameterSpec);
return cipher.doFinal(bytes); return cipher.doFinal(bytes); // $ Source[java/quantum/examples/bad-mac-order-decrypt-then-mac] Source[java/quantum/examples/bad-mac-order-decrypt-to-mac]
} }
/** /**
@@ -121,7 +121,7 @@ class BadMacUse {
SecretKey macKey = new SecretKeySpec(macKeyBytes, "HmacSHA256"); SecretKey macKey = new SecretKeySpec(macKeyBytes, "HmacSHA256");
Mac mac = Mac.getInstance("HmacSHA256"); Mac mac = Mac.getInstance("HmacSHA256");
mac.init(macKey); mac.init(macKey);
byte[] computedMac = mac.doFinal(ciphertext); // False Positive byte[] computedMac = mac.doFinal(ciphertext); // $ SPURIOUS: Alert[java/quantum/examples/bad-mac-order-decrypt-to-mac]
// Concatenate ciphertext and MAC // Concatenate ciphertext and MAC
byte[] output = new byte[ciphertext.length + computedMac.length]; byte[] output = new byte[ciphertext.length + computedMac.length];
@@ -136,20 +136,20 @@ class BadMacUse {
* The function decrypts THEN computes the MAC on the plaintext. * The function decrypts THEN computes the MAC on the plaintext.
* It should have the MAC computed on the ciphertext first. * It should have the MAC computed on the ciphertext first.
*/ */
public void decryptThenMac(byte[] encryptionKeyBytes, byte[] macKeyBytes, byte[] input) throws Exception { public void decryptThenMac(byte[] encryptionKeyBytes, byte[] macKeyBytes, byte[] input) throws Exception { // $ SPURIOUS: Source[java/quantum/examples/bad-mac-order-encrypt-plaintext-also-in-mac]
// Split input into ciphertext and MAC // Split input into ciphertext and MAC
int macLength = 32; // HMAC-SHA256 output length int macLength = 32; // HMAC-SHA256 output length
byte[] ciphertext = Arrays.copyOfRange(input, 0, input.length - macLength); byte[] ciphertext = Arrays.copyOfRange(input, 0, input.length - macLength);
byte[] receivedMac = Arrays.copyOfRange(input, input.length - macLength, input.length); byte[] receivedMac = Arrays.copyOfRange(input, input.length - macLength, input.length);
// Decrypt first (unsafe) // Decrypt first (unsafe)
byte[] plaintext = decryptUsingWrapper(ciphertext, encryptionKeyBytes, new byte[16]); // $ Source byte[] plaintext = decryptUsingWrapper(ciphertext, encryptionKeyBytes, new byte[16]);
// Now verify MAC (too late) // Now verify MAC (too late)
SecretKey macKey = new SecretKeySpec(macKeyBytes, "HmacSHA256"); SecretKey macKey = new SecretKeySpec(macKeyBytes, "HmacSHA256");
Mac mac = Mac.getInstance("HmacSHA256"); Mac mac = Mac.getInstance("HmacSHA256");
mac.init(macKey); mac.init(macKey);
byte[] computedMac = mac.doFinal(ciphertext); // $ Alert[java/quantum/examples/bad-mac-order-decrypt-then-mac], False positive for Plaintext reuse byte[] computedMac = mac.doFinal(ciphertext); // $ Alert[java/quantum/examples/bad-mac-order-decrypt-then-mac] SPURIOUS: Alert[java/quantum/examples/bad-mac-order-encrypt-plaintext-also-in-mac]
if (!MessageDigest.isEqual(receivedMac, computedMac)) { if (!MessageDigest.isEqual(receivedMac, computedMac)) {
throw new SecurityException("MAC verification failed"); throw new SecurityException("MAC verification failed");

View File

@@ -126,5 +126,3 @@ nodes
| InsecureIVorNonceSource.java:202:54:202:55 | iv : byte[] | semmle.label | iv : byte[] | | InsecureIVorNonceSource.java:202:54:202:55 | iv : byte[] | semmle.label | iv : byte[] |
| InsecureIVorNonceSource.java:206:51:206:56 | ivSpec | semmle.label | ivSpec | | InsecureIVorNonceSource.java:206:51:206:56 | ivSpec | semmle.label | ivSpec |
subpaths subpaths
testFailures
| InsecureIVorNonceSource.java:42:21:42:21 | 1 : Number | Unexpected result: Source |

View File

@@ -39,7 +39,7 @@ public class InsecureIVorNonceSource {
public byte[] encryptWithStaticIvByteArray(byte[] key, byte[] plaintext) throws Exception { public byte[] encryptWithStaticIvByteArray(byte[] key, byte[] plaintext) throws Exception {
byte[] iv = new byte[16]; byte[] iv = new byte[16];
for (byte i = 0; i < iv.length; i++) { for (byte i = 0; i < iv.length; i++) {
iv[i] = 1; iv[i] = 1; // $ Source[java/quantum/examples/insecure-iv-or-nonce]
} }
IvParameterSpec ivSpec = new IvParameterSpec(iv); IvParameterSpec ivSpec = new IvParameterSpec(iv);

View File

@@ -40,11 +40,11 @@ public class Test {
* SAST/CBOM: - Parent: PBKDF2. - Iteration count is only 10, which is far * SAST/CBOM: - Parent: PBKDF2. - Iteration count is only 10, which is far
* below acceptable security standards. - Flagged as insecure. * below acceptable security standards. - Flagged as insecure.
*/ */
public void pbkdf2LowIteration(String password, int iterationCount) throws Exception { // $ Source public void pbkdf2LowIteration(String password, int iterationCount) throws Exception { // $ Source[java/quantum/examples/unknown-kdf-iteration-count]
byte[] salt = generateSalt(16); byte[] salt = generateSalt(16);
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, iterationCount, 256); // $ Alert[java/quantum/examples/unknown-kdf-iteration-count] PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, iterationCount, 256);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256"); SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
byte[] key = factory.generateSecret(spec).getEncoded(); byte[] key = factory.generateSecret(spec).getEncoded(); // $ Alert[java/quantum/examples/unknown-kdf-iteration-count]
} }
/** /**

View File

@@ -1,5 +1 @@
#select
| Test.java:47:22:47:49 | KeyDerivation | Key derivation operation with unknown iteration: $@ | Test.java:43:53:43:70 | iterationCount | iterationCount | | Test.java:47:22:47:49 | KeyDerivation | Key derivation operation with unknown iteration: $@ | Test.java:43:53:43:70 | iterationCount | iterationCount |
testFailures
| Test.java:45:94:45:154 | // $ Alert[java/quantum/examples/unknown-kdf-iteration-count] | Missing result: Alert[java/quantum/examples/unknown-kdf-iteration-count] |
| Test.java:47:22:47:49 | Key derivation operation with unknown iteration: $@ | Unexpected result: Alert |

View File

@@ -12,5 +12,3 @@ nodes
| Test.java:58:30:58:38 | 1_000_000 : Number | semmle.label | 1_000_000 : Number | | Test.java:58:30:58:38 | 1_000_000 : Number | semmle.label | 1_000_000 : Number |
| Test.java:59:72:59:85 | iterationCount | semmle.label | iterationCount | | Test.java:59:72:59:85 | iterationCount | semmle.label | iterationCount |
subpaths subpaths
testFailures
| Test.java:43:92:43:102 | // $ Source | Missing result: Source |

View File

@@ -1,2 +0,0 @@
import semmle.python.controlflow.internal.AstNodeImpl
import ControlFlow::Consistency

View File

@@ -9,7 +9,6 @@ private import semmle.python.dataflow.new.internal.DataFlowImplSpecific
private import semmle.python.dataflow.new.internal.DataFlowDispatch private import semmle.python.dataflow.new.internal.DataFlowDispatch
private import semmle.python.dataflow.new.internal.TaintTrackingImplSpecific private import semmle.python.dataflow.new.internal.TaintTrackingImplSpecific
private import codeql.dataflow.internal.DataFlowImplConsistency private import codeql.dataflow.internal.DataFlowImplConsistency
private import semmle.python.controlflow.internal.Cfg as Cfg
private module Input implements InputSig<Location, PythonDataFlow> { private module Input implements InputSig<Location, PythonDataFlow> {
private import Private private import Private
@@ -75,7 +74,7 @@ private module Input implements InputSig<Location, PythonDataFlow> {
// resolve to multiple functions), but we only make _one_ ArgumentNode for each // resolve to multiple functions), but we only make _one_ ArgumentNode for each
// argument in the CallNode, we end up violating this consistency check in those // argument in the CallNode, we end up violating this consistency check in those
// cases. (see `getCallArg` in DataFlowDispatch.qll) // cases. (see `getCallArg` in DataFlowDispatch.qll)
exists(DataFlowCall other, Cfg::CallNode cfgCall | other != call | exists(DataFlowCall other, CallNode cfgCall | other != call |
call.getNode() = cfgCall and call.getNode() = cfgCall and
other.getNode() = cfgCall and other.getNode() = cfgCall and
isArgumentNode(arg, call, _) and isArgumentNode(arg, call, _) and
@@ -91,16 +90,16 @@ private module Input implements InputSig<Location, PythonDataFlow> {
// allow it instead. // allow it instead.
( (
call.getScope() = attr.getScope() and call.getScope() = attr.getScope() and
any(CfgNode n | n.asCfgNode() = call.getNode().(Cfg::CallNode).getFunction()) any(CfgNode n | n.asCfgNode() = call.getNode().(CallNode).getFunction()).getALocalSource() =
.getALocalSource() = attr attr
or or
not exists(call.getScope().(Function).getDefinition()) and not exists(call.getScope().(Function).getDefinition()) and
call.getScope().getScope+() = attr.getScope() call.getScope().getScope+() = attr.getScope()
) and ) and
( (
other.getScope() = attr.getScope() and other.getScope() = attr.getScope() and
any(CfgNode n | n.asCfgNode() = other.getNode().(Cfg::CallNode).getFunction()) any(CfgNode n | n.asCfgNode() = other.getNode().(CallNode).getFunction()).getALocalSource() =
.getALocalSource() = attr attr
or or
not exists(other.getScope().(Function).getDefinition()) and not exists(other.getScope().(Function).getDefinition()) and
other.getScope().getScope+() = attr.getScope() other.getScope().getScope+() = attr.getScope()

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* A new Python control flow graph implementation has been added under `semmle.python.controlflow.internal.Cfg` (backed by `AstNodeImpl.qll`), built on the shared `codeql.controlflow.ControlFlowGraph` library. It is not yet used by the dataflow library or any production query; the legacy CFG in `semmle/python/Flow.qll` remains the default. The new library is exposed for tests and for upcoming migrations.

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* A new SSA adapter has been added under `semmle.python.dataflow.new.internal.SsaImpl`, built on the shared `codeql.ssa.Ssa` library and the new shared CFG (`semmle.python.controlflow.internal.Cfg`). It is not yet used by the dataflow library or any production query; the legacy ESSA SSA in `semmle/python/essa/*` remains the default. The new SSA adapter is exposed for tests and for the upcoming dataflow migration.

View File

@@ -1,4 +0,0 @@
---
category: breaking
---
* The deprecated `AstNode.getAFlowNode()` and `Function.getAReturnValueFlowNode()` predicates now return nodes from the new shared CFG (`Cfg::ControlFlowNode`) rather than from the legacy CFG (`ControlFlowNode`). Callers that still rely on these deprecated APIs and feed the result into legacy-CFG-aware predicates will no longer type-check; migrate to `n.getNode() = e` (or, for return values, the explicit `Return` pattern shown in the deprecation message) to get nodes from the dataflow library's current CFG.

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* The new (shared-CFG-based) Python control flow graph now visits parameter and return type annotations as CFG nodes for function definitions, matching the legacy CFG. This restores annotation-based type tracking through framework models such as FastAPI's `Depends()`, Pydantic request models, Starlette `WebSocket` handlers, and any other models that flow a class reference through `Parameter.getAnnotation()` to identify instances of the annotated class.

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Type tracking of values stored in instance attributes and read from outside the class (for example `instance.attr` where the value was assigned to `self.attr` in a method) no longer relies on a dedicated instance type-tracker. This avoids a structural mutual recursion that could cause catastrophic query slowdowns on some OOP-heavy code bases. Such reads are now resolved using local flow from the constructor call, which is slightly less precise for instances that flow across a call or return before being read.

View File

@@ -1,42 +0,0 @@
/**
* @name Print CFG
* @description Produces a representation of a file's Control Flow Graph.
* This query is used by the VS Code extension.
* @id py/print-cfg
* @kind graph
* @tags ide-contextual-queries/print-cfg
*/
import semmle.python.Files as Files
// import semmle.python.Scope
import semmle.python.controlflow.internal.AstNodeImpl
external string selectedSourceFile();
private predicate selectedSourceFileAlias = selectedSourceFile/0;
external int selectedSourceLine();
private predicate selectedSourceLineAlias = selectedSourceLine/0;
external int selectedSourceColumn();
private predicate selectedSourceColumnAlias = selectedSourceColumn/0;
module ViewCfgQueryInput implements ControlFlow::ViewCfgQueryInputSig<Files::File> {
predicate selectedSourceFile = selectedSourceFileAlias/0;
predicate selectedSourceLine = selectedSourceLineAlias/0;
predicate selectedSourceColumn = selectedSourceColumnAlias/0;
predicate cfgScopeSpan(
Ast::Callable scope, Files::File file, int startLine, int startColumn, int endLine,
int endColumn
) {
file = scope.getLocation().getFile() and
scope.getLocation().hasLocationInfo(_, startLine, startColumn, endLine, endColumn)
}
}
import ControlFlow::ViewCfgQuery<Files::File, ViewCfgQueryInput>

View File

@@ -6,9 +6,8 @@
* directed and labeled; they specify how the components represented by nodes relate to each other. * directed and labeled; they specify how the components represented by nodes relate to each other.
*/ */
// Importing python under the `PY` namespace to avoid pulling in `CallNode` from `Flow.qll` (via `import python`) and thereby having a naming conflict with `API::CallNode`. // Importing python under the `py` namespace to avoid importing `CallNode` from `Flow.qll` and thereby having a naming conflict with `API::CallNode`.
private import python as PY private import python as PY
private import semmle.python.controlflow.internal.Cfg as Cfg
import semmle.python.dataflow.new.DataFlow import semmle.python.dataflow.new.DataFlow
private import semmle.python.internal.CachedStages private import semmle.python.internal.CachedStages
@@ -283,7 +282,7 @@ module API {
index = this.getIndex() and index = this.getIndex() and
( (
// subscripting // subscripting
exists(Cfg::SubscriptNode subscript | exists(PY::SubscriptNode subscript |
subscript.getObject() = this.getAValueReachableFromSource().asCfgNode() and subscript.getObject() = this.getAValueReachableFromSource().asCfgNode() and
subscript.getIndex() = index.asSink().asCfgNode() subscript.getIndex() = index.asSink().asCfgNode()
| |
@@ -291,7 +290,7 @@ module API {
subscript = result.asSource().asCfgNode() subscript = result.asSource().asCfgNode()
or or
// writing // writing
subscript.(Cfg::DefinitionNode).getValue() = result.asSink().asCfgNode() subscript.(PY::DefinitionNode).getValue() = result.asSink().asCfgNode()
) )
or or
// dictionary literals // dictionary literals
@@ -685,7 +684,7 @@ module API {
* Ignores relative imports, such as `from ..foo.bar import baz`. * Ignores relative imports, such as `from ..foo.bar import baz`.
*/ */
private predicate imports(DataFlow::CfgNode imp, string name) { private predicate imports(DataFlow::CfgNode imp, string name) {
exists(Cfg::ImportExprNode iexpr | exists(PY::ImportExprNode iexpr |
imp.getNode() = iexpr and imp.getNode() = iexpr and
not iexpr.getNode().isRelative() and not iexpr.getNode().isRelative() and
name = iexpr.getNode().getImportedModuleName() name = iexpr.getNode().getImportedModuleName()
@@ -776,7 +775,7 @@ module API {
// list literals, from `x` to `[x]` // list literals, from `x` to `[x]`
// TODO: once convenient, this should be done at a higher level than the AST, // TODO: once convenient, this should be done at a higher level than the AST,
// at least at the CFG layer, to take splitting into account. // at least at the CFG layer, to take splitting into account.
// Also consider `Cfg::SequenceNode` for generality. // Also consider `SequenceNode for generality.
exists(PY::List list | list = pred.(DataFlow::ExprNode).getNode().getNode() | exists(PY::List list | list = pred.(DataFlow::ExprNode).getNode().getNode() |
rhs.(DataFlow::ExprNode).getNode().getNode() = list.getAnElt() and rhs.(DataFlow::ExprNode).getNode().getNode() = list.getAnElt() and
lbl = Label::subscript() lbl = Label::subscript()
@@ -806,7 +805,7 @@ module API {
subscript = trackUseNode(src).getSubscript(index) subscript = trackUseNode(src).getSubscript(index)
| |
// from `x` to a definition of `x[...]` // from `x` to a definition of `x[...]`
rhs.asCfgNode() = subscript.asCfgNode().(Cfg::DefinitionNode).getValue() and rhs.asCfgNode() = subscript.asCfgNode().(PY::DefinitionNode).getValue() and
lbl = Label::subscript() lbl = Label::subscript()
or or
// from `x` to `"key"` in `x["key"]` // from `x` to `"key"` in `x["key"]`

View File

@@ -3,7 +3,6 @@ module;
import python import python
private import semmle.python.internal.CachedStages private import semmle.python.internal.CachedStages
private import semmle.python.controlflow.internal.Cfg as Cfg
/** A syntactic node (Class, Function, Module, Expr, Stmt or Comprehension) corresponding to a flow node */ /** A syntactic node (Class, Function, Module, Expr, Stmt or Comprehension) corresponding to a flow node */
abstract class AstNode extends AstNode_ { abstract class AstNode extends AstNode_ {
@@ -20,16 +19,17 @@ abstract class AstNode extends AstNode_ {
/** /**
* DEPRECATED: use `ControlFlowNode.getNode()` from the other direction instead; * DEPRECATED: use `ControlFlowNode.getNode()` from the other direction instead;
* that is, replace `e.getAFlowNode() = n` with `n.getNode() = e`. This API is * that is, replace `e.getAFlowNode() = n` with `n.getNode() = e`. This API is
* being removed to untangle the AST and CFG hierarchies. * being removed to untangle the AST and CFG hierarchies in preparation for
* migrating the dataflow library off the legacy CFG.
* *
* Gets a flow node corresponding directly to this node, from the new * Gets a flow node corresponding directly to this node.
* (shared) CFG. NOTE: For some statements and other purely syntactic * NOTE: For some statements and other purely syntactic elements,
* elements, there may not be a `ControlFlowNode`. * there may not be a `ControlFlowNode`.
*/ */
cached cached
deprecated Cfg::ControlFlowNode getAFlowNode() { deprecated ControlFlowNode getAFlowNode() {
Stages::AST::ref() and Stages::AST::ref() and
result.getNode() = this py_flow_bb_node(result, this, _, _)
} }
/** Gets the location for this AST node */ /** Gets the location for this AST node */

View File

@@ -5,7 +5,6 @@
*/ */
private import python private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.dataflow.new.DataFlow private import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.internal.DataFlowImplSpecific private import semmle.python.dataflow.new.internal.DataFlowImplSpecific
private import semmle.python.dataflow.new.RemoteFlowSources private import semmle.python.dataflow.new.RemoteFlowSources
@@ -215,7 +214,7 @@ module Path {
SafeAccessCheck() { this = DataFlow::BarrierGuard<safeAccessCheck/3>::getABarrierNode() } SafeAccessCheck() { this = DataFlow::BarrierGuard<safeAccessCheck/3>::getABarrierNode() }
} }
private predicate safeAccessCheck(DataFlow::GuardNode g, Cfg::ControlFlowNode node, boolean branch) { private predicate safeAccessCheck(DataFlow::GuardNode g, ControlFlowNode node, boolean branch) {
g.(SafeAccessCheck::Range).checks(node, branch) g.(SafeAccessCheck::Range).checks(node, branch)
} }
@@ -224,7 +223,7 @@ module Path {
/** A data-flow node that checks that a path is safe to access in some way, for example by having a controlled prefix. */ /** A data-flow node that checks that a path is safe to access in some way, for example by having a controlled prefix. */
abstract class Range extends DataFlow::GuardNode { abstract class Range extends DataFlow::GuardNode {
/** Holds if this guard validates `node` upon evaluating to `branch`. */ /** Holds if this guard validates `node` upon evaluating to `branch`. */
abstract predicate checks(Cfg::ControlFlowNode node, boolean branch); abstract predicate checks(ControlFlowNode node, boolean branch);
} }
} }
} }

View File

@@ -3,7 +3,6 @@ module;
private import python private import python
private import semmle.python.internal.CachedStages private import semmle.python.internal.CachedStages
private import semmle.python.controlflow.internal.Cfg as Cfg
/** An expression */ /** An expression */
class Expr extends Expr_, AstNode { class Expr extends Expr_, AstNode {
@@ -71,7 +70,7 @@ class Attribute extends Attribute_ {
/* syntax: Expr.name */ /* syntax: Expr.name */
override Expr getASubExpression() { result = this.getObject() } override Expr getASubExpression() { result = this.getObject() }
deprecated override Cfg::AttrNode getAFlowNode() { result = super.getAFlowNode() } deprecated override AttrNode getAFlowNode() { result = super.getAFlowNode() }
/** Gets the name of this attribute. That is the `name` in `obj.name` */ /** Gets the name of this attribute. That is the `name` in `obj.name` */
string getName() { result = Attribute_.super.getAttr() } string getName() { result = Attribute_.super.getAttr() }
@@ -100,7 +99,7 @@ class Subscript extends Subscript_ {
Expr getObject() { result = Subscript_.super.getValue() } Expr getObject() { result = Subscript_.super.getValue() }
deprecated override Cfg::SubscriptNode getAFlowNode() { result = super.getAFlowNode() } deprecated override SubscriptNode getAFlowNode() { result = super.getAFlowNode() }
} }
/** A call expression, such as `func(...)` */ /** A call expression, such as `func(...)` */
@@ -116,7 +115,7 @@ class Call extends Call_ {
override string toString() { result = this.getFunc().toString() + "()" } override string toString() { result = this.getFunc().toString() + "()" }
deprecated override Cfg::CallNode getAFlowNode() { result = super.getAFlowNode() } deprecated override CallNode getAFlowNode() { result = super.getAFlowNode() }
/** Gets a tuple (*) argument of this call. */ /** Gets a tuple (*) argument of this call. */
Expr getStarargs() { result = this.getAPositionalArg().(Starred).getValue() } Expr getStarargs() { result = this.getAPositionalArg().(Starred).getValue() }
@@ -204,7 +203,7 @@ class IfExp extends IfExp_ {
result = this.getTest() or result = this.getBody() or result = this.getOrelse() result = this.getTest() or result = this.getBody() or result = this.getOrelse()
} }
deprecated override Cfg::IfExprNode getAFlowNode() { result = super.getAFlowNode() } deprecated override IfExprNode getAFlowNode() { result = super.getAFlowNode() }
} }
/** A starred expression, such as the `*rest` in the assignment `first, *rest = seq` */ /** A starred expression, such as the `*rest` in the assignment `first, *rest = seq` */
@@ -414,7 +413,7 @@ class PlaceHolder extends PlaceHolder_ {
override string toString() { result = "$" + this.getId() } override string toString() { result = "$" + this.getId() }
deprecated override Cfg::NameNode getAFlowNode() { result = super.getAFlowNode() } deprecated override NameNode getAFlowNode() { result = super.getAFlowNode() }
} }
/** A tuple expression such as `( 1, 3, 5, 7, 9 )` */ /** A tuple expression such as `( 1, 3, 5, 7, 9 )` */
@@ -481,7 +480,7 @@ class Name extends Name_ {
override string toString() { result = this.getId() } override string toString() { result = this.getId() }
deprecated override Cfg::NameNode getAFlowNode() { result = super.getAFlowNode() } deprecated override NameNode getAFlowNode() { result = super.getAFlowNode() }
override predicate isArtificial() { override predicate isArtificial() {
/* Artificial variable names in comprehensions all start with "." */ /* Artificial variable names in comprehensions all start with "." */
@@ -588,7 +587,7 @@ abstract class NameConstant extends Name, ImmutableLiteral {
override predicate isConstant() { any() } override predicate isConstant() { any() }
deprecated override Cfg::NameConstantNode getAFlowNode() { result = Name.super.getAFlowNode() } deprecated override NameConstantNode getAFlowNode() { result = Name.super.getAFlowNode() }
override predicate isArtificial() { none() } override predicate isArtificial() { none() }
} }

View File

@@ -2,7 +2,6 @@ overlay[local]
module; module;
import python import python
private import semmle.python.controlflow.internal.Cfg as Cfg
/** /**
* A function, independent of defaults and binding. * A function, independent of defaults and binding.
@@ -158,12 +157,12 @@ class Function extends Function_, Scope, AstNode {
* DEPRECATED: bind a `Return` node explicitly instead, e.g. * DEPRECATED: bind a `Return` node explicitly instead, e.g.
* `exists(Return ret | ret.getScope() = this and n.getNode() = ret.getValue())`. * `exists(Return ret | ret.getScope() = this and n.getNode() = ret.getValue())`.
* This API is being phased out together with `AstNode.getAFlowNode()` to * This API is being phased out together with `AstNode.getAFlowNode()` to
* untangle the AST and CFG hierarchies. * untangle the AST and CFG hierarchies in preparation for migrating the
* dataflow library off the legacy CFG.
* *
* Gets a control flow node for a return value of this function, from the * Gets a control flow node for a return value of this function.
* new (shared) CFG.
*/ */
deprecated Cfg::ControlFlowNode getAReturnValueFlowNode() { deprecated ControlFlowNode getAReturnValueFlowNode() {
exists(Return ret | exists(Return ret |
ret.getScope() = this and ret.getScope() = this and
ret.getValue() = result.getNode() ret.getValue() = result.getNode()

View File

@@ -4,7 +4,6 @@ module;
import python import python
private import semmle.python.types.Builtins private import semmle.python.types.Builtins
private import semmle.python.internal.CachedStages private import semmle.python.internal.CachedStages
private import semmle.python.controlflow.internal.Cfg as Cfg
/** /**
* An alias in an import statement, the `mod as name` part of `import mod as name`. May be artificial; * An alias in an import statement, the `mod as name` part of `import mod as name`. May be artificial;
@@ -164,7 +163,7 @@ class ImportMember extends ImportMember_ {
result = this.getModule().(ImportExpr).getImportedModuleName() + "." + this.getName() result = this.getModule().(ImportExpr).getImportedModuleName() + "." + this.getName()
} }
deprecated override Cfg::ImportMemberNode getAFlowNode() { result = super.getAFlowNode() } deprecated override ImportMemberNode getAFlowNode() { result = super.getAFlowNode() }
} }
/** An import statement */ /** An import statement */

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,11 @@
/** Provides commonly used BarrierGuards. */ /** Provides commonly used BarrierGuards. */
private import python private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.dataflow.new.DataFlow private import semmle.python.dataflow.new.DataFlow
private predicate constCompare(DataFlow::GuardNode g, Cfg::ControlFlowNode node, boolean branch) { private predicate constCompare(DataFlow::GuardNode g, ControlFlowNode node, boolean branch) {
exists(Cfg::CompareNode cn | cn = g | exists(CompareNode cn | cn = g |
exists(ImmutableLiteral const, Cmpop op, Cfg::ControlFlowNode c | exists(ImmutableLiteral const, Cmpop op, ControlFlowNode c |
c.getNode() = const and c.getNode() = const and
( (
op = any(Eq eq) and branch = true op = any(Eq eq) and branch = true
@@ -19,7 +18,7 @@ private predicate constCompare(DataFlow::GuardNode g, Cfg::ControlFlowNode node,
cn.operands(node, op, c) cn.operands(node, op, c)
) )
or or
exists(NameConstant const, Cmpop op, Cfg::ControlFlowNode c | exists(NameConstant const, Cmpop op, ControlFlowNode c |
c.getNode() = const and c.getNode() = const and
( (
op = any(Is is_) and branch = true op = any(Is is_) and branch = true
@@ -32,12 +31,12 @@ private predicate constCompare(DataFlow::GuardNode g, Cfg::ControlFlowNode node,
cn.operands(node, op, c) cn.operands(node, op, c)
) )
or or
exists(Cfg::IterableNode const_iterable, Cmpop op | exists(IterableNode const_iterable, Cmpop op |
op = any(In in_) and branch = true op = any(In in_) and branch = true
or or
op = any(NotIn ni) and branch = false op = any(NotIn ni) and branch = false
| |
forall(Cfg::ControlFlowNode elem | elem = const_iterable.getAnElement() | forall(ControlFlowNode elem | elem = const_iterable.getAnElement() |
elem.getNode() instanceof ImmutableLiteral elem.getNode() instanceof ImmutableLiteral
) and ) and
cn.operands(node, op, const_iterable) cn.operands(node, op, const_iterable)

View File

@@ -4,7 +4,6 @@
*/ */
private import python private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.dataflow.new.DataFlow private import semmle.python.dataflow.new.DataFlow
// Need to import `semmle.python.Frameworks` since frameworks can extend `SensitiveDataSource::Range` // Need to import `semmle.python.Frameworks` since frameworks can extend `SensitiveDataSource::Range`
private import semmle.python.Frameworks private import semmle.python.Frameworks
@@ -106,7 +105,7 @@ private module SensitiveDataModeling {
or or
// to cover functions that we don't have the definition for, and where the // to cover functions that we don't have the definition for, and where the
// reference to the function has not already been marked as being sensitive // reference to the function has not already been marked as being sensitive
this.getFunction().asCfgNode().(Cfg::NameNode).getId() = sensitiveString(classification) this.getFunction().asCfgNode().(NameNode).getId() = sensitiveString(classification)
} }
override SensitiveDataClassification getClassification() { result = classification } override SensitiveDataClassification getClassification() { result = classification }
@@ -252,12 +251,12 @@ private module SensitiveDataModeling {
SensitiveDataClassification classification; SensitiveDataClassification classification;
SensitiveVariableAssignment() { SensitiveVariableAssignment() {
exists(Cfg::DefinitionNode def | exists(DefinitionNode def |
def.(Cfg::NameNode).getId() = sensitiveString(classification) and def.(NameNode).getId() = sensitiveString(classification) and
( (
this.asCfgNode() = def.getValue() this.asCfgNode() = def.getValue()
or or
this.asCfgNode() = def.getValue().(Cfg::ForNode).getSequence() this.asCfgNode() = def.getValue().(ForNode).getSequence()
) and ) and
not this.asExpr() instanceof FunctionExpr and not this.asExpr() instanceof FunctionExpr and
not this.asExpr() instanceof ClassExpr not this.asExpr() instanceof ClassExpr
@@ -294,7 +293,7 @@ private module SensitiveDataModeling {
SensitiveDataClassification classification; SensitiveDataClassification classification;
SensitiveSubscript() { SensitiveSubscript() {
this.asCfgNode().(Cfg::SubscriptNode).getIndex() = this.asCfgNode().(SubscriptNode).getIndex() =
sensitiveLookupStringConst(classification).asCfgNode() sensitiveLookupStringConst(classification).asCfgNode()
} }

View File

@@ -3,7 +3,6 @@ overlay[local]
module; module;
private import python private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
import DataFlowUtil import DataFlowUtil
import DataFlowPublic import DataFlowPublic
private import DataFlowPrivate private import DataFlowPrivate
@@ -84,9 +83,9 @@ abstract class AttrWrite extends AttrRef {
* ```python * ```python
* object.attr = value * object.attr = value
* ``` * ```
* Also gives access to the `value` being written, by extending `Cfg::DefinitionNode`. * Also gives access to the `value` being written, by extending `DefinitionNode`.
*/ */
private class AttributeAssignmentNode extends Cfg::DefinitionNode, Cfg::AttrNode { } private class AttributeAssignmentNode extends DefinitionNode, AttrNode { }
/** A simple attribute assignment: `object.attr = value`. */ /** A simple attribute assignment: `object.attr = value`. */
private class AttributeAssignmentAsAttrWrite extends AttrWrite, CfgNode { private class AttributeAssignmentAsAttrWrite extends AttrWrite, CfgNode {
@@ -132,13 +131,13 @@ private class GlobalAttributeAssignmentAsAttrWrite extends AttrWrite, CfgNode {
override string getAttributeName() { result = node.getName() } override string getAttributeName() { result = node.getName() }
} }
/** Represents `Cfg::CallNode`s that may refer to calls to built-in functions or classes. */ /** Represents `CallNode`s that may refer to calls to built-in functions or classes. */
private class BuiltInCallNode extends Cfg::CallNode { private class BuiltInCallNode extends CallNode {
string name; string name;
BuiltInCallNode() { BuiltInCallNode() {
// TODO disallow instances where the name of the built-in may refer to an in-scope variable of that name. // TODO disallow instances where the name of the built-in may refer to an in-scope variable of that name.
exists(Cfg::NameNode id | exists(NameNode id |
name = Builtins::getBuiltinName() and name = Builtins::getBuiltinName() and
this.getFunction() = id and this.getFunction() = id and
id.getId() = name and id.getId() = name and
@@ -146,7 +145,7 @@ private class BuiltInCallNode extends Cfg::CallNode {
) )
} }
/** Gets the name of the built-in function that is called at this `Cfg::CallNode` */ /** Gets the name of the built-in function that is called at this `CallNode` */
string getBuiltinName() { result = name } string getBuiltinName() { result = name }
} }
@@ -158,20 +157,20 @@ private class BuiltinAttrCallNode extends BuiltInCallNode {
BuiltinAttrCallNode() { name in ["setattr", "getattr", "hasattr", "delattr"] } BuiltinAttrCallNode() { name in ["setattr", "getattr", "hasattr", "delattr"] }
/** Gets the control flow node for object on which the attribute is accessed. */ /** Gets the control flow node for object on which the attribute is accessed. */
Cfg::ControlFlowNode getObject() { result in [this.getArg(0), this.getArgByName("object")] } ControlFlowNode getObject() { result in [this.getArg(0), this.getArgByName("object")] }
/** /**
* Gets the control flow node for the value that is being written to the attribute. * Gets the control flow node for the value that is being written to the attribute.
* Only relevant for `setattr` calls. * Only relevant for `setattr` calls.
*/ */
Cfg::ControlFlowNode getValue() { ControlFlowNode getValue() {
// only valid for `setattr` // only valid for `setattr`
name = "setattr" and name = "setattr" and
result in [this.getArg(2), this.getArgByName("value")] result in [this.getArg(2), this.getArgByName("value")]
} }
/** Gets the control flow node that defines the name of the attribute being accessed. */ /** Gets the control flow node that defines the name of the attribute being accessed. */
Cfg::ControlFlowNode getName() { result in [this.getArg(1), this.getArgByName("name")] } ControlFlowNode getName() { result in [this.getArg(1), this.getArgByName("name")] }
} }
/** Represents calls to the built-in `setattr`. */ /** Represents calls to the built-in `setattr`. */
@@ -206,10 +205,10 @@ private class SetAttrCallAsAttrWrite extends AttrWrite, CfgNode {
* attr = value * attr = value
* ... * ...
* ``` * ```
* Instances of this class correspond to the `Cfg::NameNode` for `attr`, and also gives access to `value` by * Instances of this class correspond to the `NameNode` for `attr`, and also gives access to `value` by
* virtue of being a `Cfg::DefinitionNode`. * virtue of being a `DefinitionNode`.
*/ */
private class ClassAttributeAssignmentNode extends Cfg::DefinitionNode, Cfg::NameNode { private class ClassAttributeAssignmentNode extends DefinitionNode, NameNode {
ClassAttributeAssignmentNode() { this.getScope() = any(ClassExpr c).getInnerScope() } ClassAttributeAssignmentNode() { this.getScope() = any(ClassExpr c).getInnerScope() }
} }
@@ -249,7 +248,7 @@ abstract class AttrRead extends AttrRef, Node, LocalSourceNode {
/** A simple attribute read, e.g. `object.attr` */ /** A simple attribute read, e.g. `object.attr` */
private class AttributeReadAsAttrRead extends AttrRead, CfgNode { private class AttributeReadAsAttrRead extends AttrRead, CfgNode {
override Cfg::AttrNode node; override AttrNode node;
AttributeReadAsAttrRead() { node.isLoad() } AttributeReadAsAttrRead() { node.isLoad() }
@@ -286,7 +285,7 @@ private class GetAttrCallAsAttrRead extends AttrRead, CfgNode {
* is treated as if it is a read of the attribute `module.attr`, even if `module` is not imported directly. * is treated as if it is a read of the attribute `module.attr`, even if `module` is not imported directly.
*/ */
private class ModuleAttributeImportAsAttrRead extends AttrRead, CfgNode { private class ModuleAttributeImportAsAttrRead extends AttrRead, CfgNode {
override Cfg::ImportMemberNode node; override ImportMemberNode node;
override Node getObject() { result.asCfgNode() = node.getModule(_) } override Node getObject() { result.asCfgNode() = node.getModule(_) }

View File

@@ -3,7 +3,6 @@ overlay[local]
module; module;
private import python private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.dataflow.new.DataFlow private import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.internal.ImportStar private import semmle.python.dataflow.new.internal.ImportStar
@@ -68,7 +67,7 @@ module Builtins {
DataFlow::CfgNode likelyBuiltin(string name) { DataFlow::CfgNode likelyBuiltin(string name) {
exists(Module m | exists(Module m |
result.getNode() = result.getNode() =
any(Cfg::NameNode n | any(NameNode n |
possible_builtin_accessed_in_module(n, name, m) and possible_builtin_accessed_in_module(n, name, m) and
not possible_builtin_defined_in_module(name, m) not possible_builtin_defined_in_module(name, m)
) )
@@ -88,7 +87,7 @@ module Builtins {
* Holds if `n` is an access of a global variable called `name` (which is also the name of a * Holds if `n` is an access of a global variable called `name` (which is also the name of a
* built-in) inside the module `m`. * built-in) inside the module `m`.
*/ */
private predicate possible_builtin_accessed_in_module(Cfg::NameNode n, string name, Module m) { private predicate possible_builtin_accessed_in_module(NameNode n, string name, Module m) {
n.isGlobal() and n.isGlobal() and
n.isLoad() and n.isLoad() and
name = n.getId() and name = n.getId() and

View File

@@ -25,7 +25,7 @@
* what callable this call might end up targeting. * what callable this call might end up targeting.
* *
* Specifically this means that we cannot use type-backtrackers from the function of a * Specifically this means that we cannot use type-backtrackers from the function of a
* `Cfg::CallNode`, since there is no `Cfg::CallNode` to backtrack from for `func` in the example * `CallNode`, since there is no `CallNode` to backtrack from for `func` in the example
* above. * above.
* *
* Note: This hasn't been 100% realized yet, so we don't currently expose a predicate to * Note: This hasn't been 100% realized yet, so we don't currently expose a predicate to
@@ -35,7 +35,6 @@ overlay[local?]
module; module;
private import python private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import DataFlowPublic private import DataFlowPublic
private import DataFlowPrivate private import DataFlowPrivate
private import FlowSummaryImpl as FlowSummaryImpl private import FlowSummaryImpl as FlowSummaryImpl
@@ -163,7 +162,7 @@ newtype TArgumentPosition =
*/ */
TLambdaSelfArgumentPosition() or TLambdaSelfArgumentPosition() or
TPositionalArgumentPosition(int index) { TPositionalArgumentPosition(int index) {
exists(any(Cfg::CallNode c).getArg(index)) exists(any(CallNode c).getArg(index))
or or
// since synthetic calls within a summarized callable could use a unique argument // since synthetic calls within a summarized callable could use a unique argument
// position, we need to ensure we make these available (these are specified as // position, we need to ensure we make these available (these are specified as
@@ -175,7 +174,7 @@ newtype TArgumentPosition =
index = 0 index = 0
} or } or
TKeywordArgumentPosition(string name) { TKeywordArgumentPosition(string name) {
exists(any(Cfg::CallNode c).getArgByName(name)) exists(any(CallNode c).getArgByName(name))
or or
// see comment for TPositionalArgumentPosition // see comment for TPositionalArgumentPosition
FlowSummaryImpl::ParsePositions::isParsedKeywordParameterPosition(_, name) FlowSummaryImpl::ParsePositions::isParsedKeywordParameterPosition(_, name)
@@ -298,12 +297,10 @@ predicate hasPropertyDecorator(Function func) {
*/ */
overlay[local] overlay[local]
predicate hasContextmanagerDecorator(Function func) { predicate hasContextmanagerDecorator(Function func) {
exists(Cfg::ControlFlowNode contextmanager | exists(ControlFlowNode contextmanager |
contextmanager.(Cfg::NameNode).getId() = "contextmanager" and contextmanager.(NameNode).getId() = "contextmanager" and contextmanager.(NameNode).isGlobal()
contextmanager.(Cfg::NameNode).isGlobal()
or or
contextmanager.(Cfg::AttrNode).getObject("contextmanager").(Cfg::NameNode).getId() = contextmanager.(AttrNode).getObject("contextmanager").(NameNode).getId() = "contextlib"
"contextlib"
| |
func.getADecorator() = contextmanager.getNode() func.getADecorator() = contextmanager.getNode()
) )
@@ -319,10 +316,10 @@ predicate hasContextmanagerDecorator(Function func) {
*/ */
overlay[local] overlay[local]
private predicate hasOverloadDecorator(Function func) { private predicate hasOverloadDecorator(Function func) {
exists(Cfg::ControlFlowNode overload | exists(ControlFlowNode overload |
overload.(Cfg::NameNode).getId() = "overload" and overload.(Cfg::NameNode).isGlobal() overload.(NameNode).getId() = "overload" and overload.(NameNode).isGlobal()
or or
overload.(Cfg::AttrNode).getObject("overload").(Cfg::NameNode).isGlobal() overload.(AttrNode).getObject("overload").(NameNode).isGlobal()
| |
func.getADecorator() = overload.getNode() func.getADecorator() = overload.getNode()
) )
@@ -541,7 +538,7 @@ class LibraryCallableValue extends DataFlowCallable, TLibraryCallable {
// ============================================================================= // =============================================================================
/** Gets a call to `type`. */ /** Gets a call to `type`. */
private CallCfgNode getTypeCall() { private CallCfgNode getTypeCall() {
exists(Cfg::NameNode id | id.getId() = "type" and id.isGlobal() | exists(NameNode id | id.getId() = "type" and id.isGlobal() |
result.getFunction().asCfgNode() = id result.getFunction().asCfgNode() = id
) )
} }
@@ -553,7 +550,7 @@ private CallCfgNode getSuperCall() {
// link below), but otherwise only 2 edgecases. Overall it seems ok to ignore this complexity. // link below), but otherwise only 2 edgecases. Overall it seems ok to ignore this complexity.
// //
// https://github.com/python/cpython/blob/18b1782192f85bd26db89f5bc850f8bee4247c1a/Lib/unittest/mock.py#L48-L50 // https://github.com/python/cpython/blob/18b1782192f85bd26db89f5bc850f8bee4247c1a/Lib/unittest/mock.py#L48-L50
exists(Cfg::NameNode id | id.getId() = "super" and id.isGlobal() | exists(NameNode id | id.getId() = "super" and id.isGlobal() |
result.getFunction().asCfgNode() = id result.getFunction().asCfgNode() = id
) )
} }
@@ -1039,7 +1036,7 @@ private module MethodCalls {
*/ */
pragma[nomagic] pragma[nomagic]
private predicate directCall( private predicate directCall(
Cfg::CallNode call, Function target, string functionName, Class cls, AttrRead attr, Node self CallNode call, Function target, string functionName, Class cls, AttrRead attr, Node self
) { ) {
target = findFunctionAccordingToMroKnownStartingClass(cls, functionName) and target = findFunctionAccordingToMroKnownStartingClass(cls, functionName) and
directCall_join(call, functionName, cls, attr, self) directCall_join(call, functionName, cls, attr, self)
@@ -1048,7 +1045,7 @@ private module MethodCalls {
/** Extracted to give good join order */ /** Extracted to give good join order */
pragma[nomagic] pragma[nomagic]
private predicate directCall_join( private predicate directCall_join(
Cfg::CallNode call, string functionName, Class cls, AttrRead attr, Node self CallNode call, string functionName, Class cls, AttrRead attr, Node self
) { ) {
call.getFunction() = attrReadTracker(attr).asCfgNode() and call.getFunction() = attrReadTracker(attr).asCfgNode() and
attr.accesses(self, functionName) and attr.accesses(self, functionName) and
@@ -1065,7 +1062,7 @@ private module MethodCalls {
*/ */
pragma[nomagic] pragma[nomagic]
private predicate callWithinMethodImplicitSelfOrCls( private predicate callWithinMethodImplicitSelfOrCls(
Cfg::CallNode call, Function target, string functionName, Class classWithMethod, AttrRead attr, CallNode call, Function target, string functionName, Class classWithMethod, AttrRead attr,
Node self Node self
) { ) {
target = findFunctionAccordingToMro(getADirectSubclass*(classWithMethod), functionName) and target = findFunctionAccordingToMro(getADirectSubclass*(classWithMethod), functionName) and
@@ -1075,7 +1072,7 @@ private module MethodCalls {
/** Extracted to give good join order */ /** Extracted to give good join order */
pragma[nomagic] pragma[nomagic]
private predicate callWithinMethodImplicitSelfOrCls_join( private predicate callWithinMethodImplicitSelfOrCls_join(
Cfg::CallNode call, string functionName, Class classWithMethod, AttrRead attr, Node self CallNode call, string functionName, Class classWithMethod, AttrRead attr, Node self
) { ) {
call.getFunction() = attrReadTracker(attr).asCfgNode() and call.getFunction() = attrReadTracker(attr).asCfgNode() and
attr.accesses(self, functionName) and attr.accesses(self, functionName) and
@@ -1087,7 +1084,7 @@ private module MethodCalls {
* resolve the call to a known target (since the only super class might be the * resolve the call to a known target (since the only super class might be the
* builtin `object`, so we never have the implementation of `__new__` in the DB). * builtin `object`, so we never have the implementation of `__new__` in the DB).
*/ */
predicate fromSuperNewCall(Cfg::CallNode call, Class classUsedInSuper, AttrRead attr, Node self) { predicate fromSuperNewCall(CallNode call, Class classUsedInSuper, AttrRead attr, Node self) {
fromSuper_join(call, "__new__", classUsedInSuper, attr, self) and fromSuper_join(call, "__new__", classUsedInSuper, attr, self) and
self in [classTracker(_), clsArgumentTracker(_)] self in [classTracker(_), clsArgumentTracker(_)]
} }
@@ -1109,7 +1106,7 @@ private module MethodCalls {
*/ */
pragma[nomagic] pragma[nomagic]
predicate fromSuper( predicate fromSuper(
Cfg::CallNode call, Function target, string functionName, Class classUsedInSuper, AttrRead attr, CallNode call, Function target, string functionName, Class classUsedInSuper, AttrRead attr,
Node self Node self
) { ) {
target = findFunctionAccordingToMro(getNextClassInMro(classUsedInSuper), functionName) and target = findFunctionAccordingToMro(getNextClassInMro(classUsedInSuper), functionName) and
@@ -1119,7 +1116,7 @@ private module MethodCalls {
/** Extracted to give good join order */ /** Extracted to give good join order */
pragma[nomagic] pragma[nomagic]
private predicate fromSuper_join( private predicate fromSuper_join(
Cfg::CallNode call, string functionName, Class classUsedInSuper, AttrRead attr, Node self CallNode call, string functionName, Class classUsedInSuper, AttrRead attr, Node self
) { ) {
call.getFunction() = attrReadTracker(attr).asCfgNode() and call.getFunction() = attrReadTracker(attr).asCfgNode() and
( (
@@ -1138,7 +1135,7 @@ private module MethodCalls {
) )
} }
predicate resolveMethodCall(Cfg::CallNode call, Function target, CallType type, Node self) { predicate resolveMethodCall(CallNode call, Function target, CallType type, Node self) {
( (
directCall(call, target, _, _, _, self) directCall(call, target, _, _, _, self)
or or
@@ -1185,7 +1182,7 @@ import MethodCalls
* NOTE: We have this predicate mostly to be able to compare with old point-to * NOTE: We have this predicate mostly to be able to compare with old point-to
* call-graph resolution. So it could be removed in the future. * call-graph resolution. So it could be removed in the future.
*/ */
predicate resolveClassCall(Cfg::CallNode call, Class cls) { predicate resolveClassCall(CallNode call, Class cls) {
call.getFunction() = classTracker(cls).asCfgNode() call.getFunction() = classTracker(cls).asCfgNode()
or or
// `cls()` inside a classmethod (which also contains `type(self)()` inside a method) // `cls()` inside a classmethod (which also contains `type(self)()` inside a method)
@@ -1215,7 +1212,7 @@ Function invokedFunctionFromClassConstruction(Class cls, string funcName) {
* *
* See https://docs.python.org/3/reference/datamodel.html#object.__call__ * See https://docs.python.org/3/reference/datamodel.html#object.__call__
*/ */
predicate resolveClassInstanceCall(Cfg::CallNode call, Function target, Node self) { predicate resolveClassInstanceCall(CallNode call, Function target, Node self) {
exists(Class cls | exists(Class cls |
call.getFunction() = classInstanceTracker(cls).asCfgNode() and call.getFunction() = classInstanceTracker(cls).asCfgNode() and
target = findFunctionAccordingToMroKnownStartingClass(cls, "__call__") target = findFunctionAccordingToMroKnownStartingClass(cls, "__call__")
@@ -1234,7 +1231,7 @@ predicate resolveClassInstanceCall(Cfg::CallNode call, Function target, Node sel
* Holds if `call` is a call to the `target`, with call-type `type`. * Holds if `call` is a call to the `target`, with call-type `type`.
*/ */
cached cached
predicate resolveCall(Cfg::CallNode call, Function target, CallType type) { predicate resolveCall(CallNode call, Function target, CallType type) {
Stages::DataFlow::ref() and Stages::DataFlow::ref() and
( (
type instanceof CallTypePlainFunction and type instanceof CallTypePlainFunction and
@@ -1259,11 +1256,11 @@ predicate resolveCall(Cfg::CallNode call, Function target, CallType type) {
// ============================================================================= // =============================================================================
/** /**
* Holds if the argument of `call` at position `apos` is `arg`. This is just a helper * Holds if the argument of `call` at position `apos` is `arg`. This is just a helper
* predicate that maps ArgumentPositions to the arguments of the underlying `Cfg::CallNode`. * predicate that maps ArgumentPositions to the arguments of the underlying `CallNode`.
*/ */
overlay[local] overlay[local]
cached cached
predicate normalCallArg(Cfg::CallNode call, Node arg, ArgumentPosition apos) { predicate normalCallArg(CallNode call, Node arg, ArgumentPosition apos) {
exists(int index | exists(int index |
apos.isPositional(index) and apos.isPositional(index) and
arg.asCfgNode() = call.getArg(index) arg.asCfgNode() = call.getArg(index)
@@ -1278,7 +1275,7 @@ predicate normalCallArg(Cfg::CallNode call, Node arg, ArgumentPosition apos) {
exists(int index | exists(int index |
apos.isStarArgs(index) and apos.isStarArgs(index) and
arg.asCfgNode() = call.getStarArg() and arg.asCfgNode() = call.getStarArg() and
// since `Cfg::CallNode.getArg` doesn't include `*args`, we need to drop to the AST level // since `CallNode.getArg` doesn't include `*args`, we need to drop to the AST level
// to get the index. Notice that we only use the AST for getting the index, so we // to get the index. Notice that we only use the AST for getting the index, so we
// don't need to check for dominance in regards to splitting. // don't need to check for dominance in regards to splitting.
call.getStarArg().getNode() = call.getNode().getPositionalArg(index).(Starred).getValue() call.getStarArg().getNode() = call.getNode().getPositionalArg(index).(Starred).getValue()
@@ -1352,9 +1349,7 @@ predicate normalCallArg(Cfg::CallNode call, Node arg, ArgumentPosition apos) {
* translated into `l.clear()`, and we can still have use-use flow. * translated into `l.clear()`, and we can still have use-use flow.
*/ */
cached cached
predicate getCallArg( predicate getCallArg(CallNode call, Function target, CallType type, Node arg, ArgumentPosition apos) {
Cfg::CallNode call, Function target, CallType type, Node arg, ArgumentPosition apos
) {
Stages::DataFlow::ref() and Stages::DataFlow::ref() and
resolveCall(call, target, type) and resolveCall(call, target, type) and
( (
@@ -1447,12 +1442,10 @@ private predicate sameEnclosingCallable(Node node1, Node node2) {
// DataFlowCall // DataFlowCall
// ============================================================================= // =============================================================================
newtype TDataFlowCall = newtype TDataFlowCall =
TNormalCall(Cfg::CallNode call, Function target, CallType type) { TNormalCall(CallNode call, Function target, CallType type) { resolveCall(call, target, type) } or
call.injects(_) and resolveCall(call, target, type)
} or
/** A call to the generated function inside a comprehension */ /** A call to the generated function inside a comprehension */
TComprehensionCall(Comp c) or TComprehensionCall(Comp c) or
TPotentialLibraryCall(Cfg::CallNode call) { call.injects(_) } or TPotentialLibraryCall(CallNode call) or
/** A synthesized call inside a summarized callable */ /** A synthesized call inside a summarized callable */
TSummaryCall( TSummaryCall(
FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver FlowSummaryImpl::Public::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNode receiver
@@ -1472,7 +1465,7 @@ abstract class DataFlowCall extends TDataFlowCall {
abstract ArgumentNode getArgument(ArgumentPosition apos); abstract ArgumentNode getArgument(ArgumentPosition apos);
/** Get the control flow node representing this call, if any. */ /** Get the control flow node representing this call, if any. */
abstract Cfg::ControlFlowNode getNode(); abstract ControlFlowNode getNode();
/** Gets the enclosing callable of this call. */ /** Gets the enclosing callable of this call. */
DataFlowCallable getEnclosingCallable() { result = getCallableScope(this.getScope()) } DataFlowCallable getEnclosingCallable() { result = getCallableScope(this.getScope()) }
@@ -1503,28 +1496,28 @@ abstract class ExtractedDataFlowCall extends DataFlowCall {
} }
/** /**
* A resolved call in source code with an underlying `Cfg::CallNode`. * A resolved call in source code with an underlying `CallNode`.
* *
* This is considered normal, compared with special calls such as `obj[0]` calling the * This is considered normal, compared with special calls such as `obj[0]` calling the
* `__getitem__` method on the object. However, this also includes calls that go to the * `__getitem__` method on the object. However, this also includes calls that go to the
* `__call__` special method. * `__call__` special method.
*/ */
class NormalCall extends ExtractedDataFlowCall, TNormalCall { class NormalCall extends ExtractedDataFlowCall, TNormalCall {
Cfg::CallNode call; CallNode call;
Function target; Function target;
CallType type; CallType type;
NormalCall() { this = TNormalCall(call, target, type) } NormalCall() { this = TNormalCall(call, target, type) }
override string toString() { override string toString() {
// note: if we used toString directly on the Cfg::CallNode we would get // note: if we used toString directly on the CallNode we would get
// `Cfg::ControlFlowNode for func()` // `ControlFlowNode for func()`
// but the `Cfg::ControlFlowNode` part is just clutter, so we go directly to the AST node // but the `ControlFlowNode` part is just clutter, so we go directly to the AST node
// instead. // instead.
result = call.getNode().toString() result = call.getNode().toString()
} }
override Cfg::ControlFlowNode getNode() { result = call } override ControlFlowNode getNode() { result = call }
override Scope getScope() { result = call.getScope() } override Scope getScope() { result = call.getScope() }
@@ -1552,7 +1545,7 @@ class ComprehensionCall extends ExtractedDataFlowCall, TComprehensionCall {
override string toString() { result = "comprehension call" } override string toString() { result = "comprehension call" }
override Cfg::ControlFlowNode getNode() { result.getNode() = c } override ControlFlowNode getNode() { result.getNode() = c }
override Scope getScope() { result = c.getScope() } override Scope getScope() { result = c.getScope() }
@@ -1575,14 +1568,14 @@ class ComprehensionCall extends ExtractedDataFlowCall, TComprehensionCall {
* in this class. * in this class.
*/ */
class PotentialLibraryCall extends ExtractedDataFlowCall, TPotentialLibraryCall { class PotentialLibraryCall extends ExtractedDataFlowCall, TPotentialLibraryCall {
Cfg::CallNode call; CallNode call;
PotentialLibraryCall() { this = TPotentialLibraryCall(call) } PotentialLibraryCall() { this = TPotentialLibraryCall(call) }
override string toString() { override string toString() {
// note: if we used toString directly on the Cfg::CallNode we would get // note: if we used toString directly on the CallNode we would get
// `Cfg::ControlFlowNode for func()` // `ControlFlowNode for func()`
// but the `Cfg::ControlFlowNode` part is just clutter, so we go directly to the AST node // but the `ControlFlowNode` part is just clutter, so we go directly to the AST node
// instead. // instead.
result = call.getNode().toString() result = call.getNode().toString()
} }
@@ -1599,10 +1592,10 @@ class PotentialLibraryCall extends ExtractedDataFlowCall, TPotentialLibraryCall
// potential self argument, from `foo.bar()` -- note that this could also just be a // potential self argument, from `foo.bar()` -- note that this could also just be a
// module reference, but we really don't have a good way of knowing :| // module reference, but we really don't have a good way of knowing :|
apos.isSelf() and apos.isSelf() and
result.asCfgNode() = call.getFunction().(Cfg::AttrNode).getObject() result.asCfgNode() = call.getFunction().(AttrNode).getObject()
} }
override Cfg::ControlFlowNode getNode() { result = call } override ControlFlowNode getNode() { result = call }
override Scope getScope() { result = call.getScope() } override Scope getScope() { result = call.getScope() }
} }
@@ -1634,7 +1627,7 @@ class SummaryCall extends DataFlowCall, TSummaryCall {
override ArgumentNode getArgument(ArgumentPosition apos) { none() } override ArgumentNode getArgument(ArgumentPosition apos) { none() }
override Cfg::ControlFlowNode getNode() { none() } override ControlFlowNode getNode() { none() }
override string toString() { result = "[summary] call to " + receiver + " in " + c } override string toString() { result = "[summary] call to " + receiver + " in " + c }
@@ -1776,12 +1769,12 @@ private class SummaryPostUpdateNode extends FlowSummaryNode, PostUpdateNodeImpl
* This is used for tracking flow through captured variables. * This is used for tracking flow through captured variables.
*/ */
class SynthCapturedVariablesArgumentNode extends Node, TSynthCapturedVariablesArgumentNode { class SynthCapturedVariablesArgumentNode extends Node, TSynthCapturedVariablesArgumentNode {
Cfg::ControlFlowNode callable; ControlFlowNode callable;
SynthCapturedVariablesArgumentNode() { this = TSynthCapturedVariablesArgumentNode(callable) } SynthCapturedVariablesArgumentNode() { this = TSynthCapturedVariablesArgumentNode(callable) }
/** Gets the `Cfg::CallNode` corresponding to this captured variables argument node. */ /** Gets the `CallNode` corresponding to this captured variables argument node. */
Cfg::CallNode getCallNode() { result.getFunction() = callable } CallNode getCallNode() { result.getFunction() = callable }
/** Gets the `CfgNode` that corresponds to this synthetic node. */ /** Gets the `CfgNode` that corresponds to this synthetic node. */
CfgNode getUnderlyingNode() { result.asCfgNode() = callable } CfgNode getUnderlyingNode() { result.asCfgNode() = callable }
@@ -1799,7 +1792,7 @@ class CapturedVariablesArgumentNodeAsArgumentNode extends ArgumentNode,
{ {
overlay[global] overlay[global]
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) { override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
exists(Cfg::CallNode callNode | callNode = this.getCallNode() | exists(CallNode callNode | callNode = this.getCallNode() |
callNode = call.getNode() and callNode = call.getNode() and
exists(Function target | resolveCall(callNode, target, _) | exists(Function target | resolveCall(callNode, target, _) |
target = any(VariableCapture::CapturedVariable v).getACapturingScope() target = any(VariableCapture::CapturedVariable v).getACapturingScope()
@@ -1813,7 +1806,7 @@ class CapturedVariablesArgumentNodeAsArgumentNode extends ArgumentNode,
class SynthCapturedVariablesArgumentPostUpdateNode extends PostUpdateNodeImpl, class SynthCapturedVariablesArgumentPostUpdateNode extends PostUpdateNodeImpl,
TSynthCapturedVariablesArgumentPostUpdateNode TSynthCapturedVariablesArgumentPostUpdateNode
{ {
Cfg::ControlFlowNode callable; ControlFlowNode callable;
SynthCapturedVariablesArgumentPostUpdateNode() { SynthCapturedVariablesArgumentPostUpdateNode() {
this = TSynthCapturedVariablesArgumentPostUpdateNode(callable) this = TSynthCapturedVariablesArgumentPostUpdateNode(callable)

View File

@@ -2,9 +2,8 @@ overlay[local?]
module; module;
private import python private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import DataFlowPublic private import DataFlowPublic
private import semmle.python.dataflow.new.internal.SsaImpl as SsaImpl private import semmle.python.essa.SsaCompute
private import semmle.python.dataflow.new.internal.ImportResolution private import semmle.python.dataflow.new.internal.ImportResolution
private import FlowSummaryImpl as FlowSummaryImpl private import FlowSummaryImpl as FlowSummaryImpl
private import semmle.python.frameworks.data.ModelsAsData private import semmle.python.frameworks.data.ModelsAsData
@@ -44,28 +43,13 @@ predicate isArgumentNode(ArgumentNode arg, DataFlowCall c, ArgumentPosition pos)
// Nodes // Nodes
//-------- //--------
overlay[local] overlay[local]
predicate isExpressionNode(Cfg::ControlFlowNode node) { predicate isExpressionNode(ControlFlowNode node) { node.getNode() instanceof Expr }
// Restrict to the `injects` representative so the dataflow layer creates
// exactly one `TCfgNode` per AST expression.
node.injects(_) and
(
node.getNode() instanceof Expr
or
// `Cfg::ForNode` wraps a `For` statement's iter position, but
// overrides `.getNode()` to return the `Py::For` statement (for
// legacy parity). The underlying AST is still an `Expr` (the iter
// expression); we want a dataflow node here so that for-loop
// content reads (`for y in l`) have a source expression node to
// read content from.
node instanceof Cfg::ForNode
)
}
// ============================================================================= // =============================================================================
// SyntheticPreUpdateNode // SyntheticPreUpdateNode
// ============================================================================= // =============================================================================
class SyntheticPreUpdateNode extends Node, TSyntheticPreUpdateNode { class SyntheticPreUpdateNode extends Node, TSyntheticPreUpdateNode {
Cfg::CallNode node; CallNode node;
SyntheticPreUpdateNode() { this = TSyntheticPreUpdateNode(node) } SyntheticPreUpdateNode() { this = TSyntheticPreUpdateNode(node) }
@@ -167,7 +151,7 @@ predicate synthStarArgsElementParameterNodeStoreStep(
* been passed in a `**kwargs` argument. * been passed in a `**kwargs` argument.
*/ */
class SynthDictSplatArgumentNode extends Node, TSynthDictSplatArgumentNode { class SynthDictSplatArgumentNode extends Node, TSynthDictSplatArgumentNode {
Cfg::CallNode node; CallNode node;
SynthDictSplatArgumentNode() { this = TSynthDictSplatArgumentNode(node) } SynthDictSplatArgumentNode() { this = TSynthDictSplatArgumentNode(node) }
@@ -181,7 +165,7 @@ class SynthDictSplatArgumentNode extends Node, TSynthDictSplatArgumentNode {
private predicate synthDictSplatArgumentNodeStoreStep( private predicate synthDictSplatArgumentNodeStoreStep(
ArgumentNode nodeFrom, DictionaryElementContent c, SynthDictSplatArgumentNode nodeTo ArgumentNode nodeFrom, DictionaryElementContent c, SynthDictSplatArgumentNode nodeTo
) { ) {
exists(string name, Cfg::CallNode call, ArgumentPosition keywordPos | exists(string name, CallNode call, ArgumentPosition keywordPos |
nodeTo = TSynthDictSplatArgumentNode(call) and nodeTo = TSynthDictSplatArgumentNode(call) and
getCallArg(call, _, _, nodeFrom, keywordPos) and getCallArg(call, _, _, nodeFrom, keywordPos) and
keywordPos.isKeyword(name) and keywordPos.isKeyword(name) and
@@ -305,7 +289,7 @@ abstract class PostUpdateNodeImpl extends Node {
* Synthetic post-update nodes for synthetic nodes need to be listed one by one. * Synthetic post-update nodes for synthetic nodes need to be listed one by one.
*/ */
class SyntheticPostUpdateNode extends PostUpdateNodeImpl, TSyntheticPostUpdateNode { class SyntheticPostUpdateNode extends PostUpdateNodeImpl, TSyntheticPostUpdateNode {
Cfg::ControlFlowNode node; ControlFlowNode node;
SyntheticPostUpdateNode() { this = TSyntheticPostUpdateNode(node) } SyntheticPostUpdateNode() { this = TSyntheticPostUpdateNode(node) }
@@ -349,42 +333,16 @@ module LocalFlow {
// `x = f(42)` // `x = f(42)`
// nodeFrom is `f(42)` // nodeFrom is `f(42)`
// nodeTo is `x` // nodeTo is `x`
// exists(AssignmentDefinition def |
// We use the CFG-level `DefinitionNode.getValue()` directly rather
// than going through SSA, because the new SSA library prunes write
// definitions that have no subsequent read in the same scope (e.g.
// a module-level `def f():` whose `f` is only read inside other
// functions). The CFG-level link is unconditional.
//
// The Name-target restriction mirrors legacy ESSA's
// `SsaDefinitions::assignment_definition`, which required
// `defn.(NameNode).defines(v)`. Subscript and attribute writes
// (`x[i] = 42`, `obj.attr = 42`) are intentionally excluded — their
// value flow is handled by the content-flow / `AttrWrite` machinery,
// not by a local-flow step *into* the Subscript/Attribute expression.
// Excluding them is essential for keeping augmented-assignment
// targets (`x[i] += 42`) classifiable as `LocalSourceNode` on the
// read side: the single canonical CFG node is both a load and a
// store, and any incoming local-flow step would disqualify it from
// being a local source.
exists(Cfg::DefinitionNode def |
nodeFrom.(CfgNode).getNode() = def.getValue() and nodeFrom.(CfgNode).getNode() = def.getValue() and
nodeTo.(CfgNode).getNode() = def and nodeTo.(CfgNode).getNode() = def.getDefiningNode()
def instanceof Cfg::NameNode and
// Parameter defaults are evaluated in the enclosing scope, while the
// parameter itself lives in the function's scope. The cross-scope
// edge is provided by `runtimeJumpStep` instead.
not exists(Py::Parameter param | def.getNode() = param.asName())
) )
or or
// With definition // With definition
// `with f(42) as x:` // `with f(42) as x:`
// nodeFrom is `f(42)` // nodeFrom is `f(42)`
// nodeTo is `x` // nodeTo is `x`
exists( exists(With with, ControlFlowNode contextManager, WithDefinition withDef, ControlFlowNode var |
With with, Cfg::ControlFlowNode contextManager, SsaImpl::WithDefinition withDef,
Cfg::ControlFlowNode var
|
var = withDef.getDefiningNode() var = withDef.getDefiningNode()
| |
nodeFrom.(CfgNode).getNode() = contextManager and nodeFrom.(CfgNode).getNode() = contextManager and
@@ -403,13 +361,13 @@ module LocalFlow {
predicate expressionFlowStep(Node nodeFrom, Node nodeTo) { predicate expressionFlowStep(Node nodeFrom, Node nodeTo) {
// If expressions // If expressions
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(Cfg::IfExprNode).getAnOperand() nodeFrom.asCfgNode() = nodeTo.asCfgNode().(IfExprNode).getAnOperand()
or or
// Assignment expressions // Assignment expressions
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(Cfg::AssignmentExprNode).getValue() nodeFrom.asCfgNode() = nodeTo.asCfgNode().(AssignmentExprNode).getValue()
or or
// boolean inline expressions such as `x or y` or `x and y` // boolean inline expressions such as `x or y` or `x and y`
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(Cfg::BoolExprNode).getAnOperand() nodeFrom.asCfgNode() = nodeTo.asCfgNode().(BoolExprNode).getAnOperand()
or or
// Flow inside an unpacking assignment // Flow inside an unpacking assignment
iterableUnpackingFlowStep(nodeFrom, nodeTo) iterableUnpackingFlowStep(nodeFrom, nodeTo)
@@ -418,25 +376,12 @@ module LocalFlow {
matchFlowStep(nodeFrom, nodeTo) matchFlowStep(nodeFrom, nodeTo)
} }
predicate useToNextUse(Cfg::NameNode nodeFrom, Cfg::NameNode nodeTo) { predicate useToNextUse(NameNode nodeFrom, NameNode nodeTo) {
// The SSA-level adjacent-use predicate works on specific CFG variants AdjacentUses::adjacentUseUse(nodeFrom, nodeTo)
// (e.g. boolean-outcome `[true]`/`[false]` or emptiness `[empty]`/`[non-empty]`
// splits of the same AST node), but dataflow values are insensitive to
// those splits — there is at most one `CfgNode` per AST. Project both
// ends through `.getNode()` so all variants contribute their use-use
// edges to the canonical pair.
exists(Cfg::NameNode fromVariant, Cfg::NameNode toVariant |
SsaImpl::AdjacentUses::adjacentUseUse(fromVariant, toVariant) and
fromVariant.getNode() = nodeFrom.getNode() and
toVariant.getNode() = nodeTo.getNode()
)
} }
predicate defToFirstUse(SsaImpl::EssaVariable var, Cfg::NameNode nodeTo) { predicate defToFirstUse(EssaVariable var, NameNode nodeTo) {
exists(Cfg::NameNode toVariant | AdjacentUses::firstUse(var.getDefinition(), nodeTo)
SsaImpl::AdjacentUses::firstUse(var.getDefinition(), toVariant) and
toVariant.getNode() = nodeTo.getNode()
)
} }
predicate useUseFlowStep(Node nodeFrom, Node nodeTo) { predicate useUseFlowStep(Node nodeFrom, Node nodeTo) {
@@ -445,13 +390,12 @@ module LocalFlow {
// `x = f(y)` // `x = f(y)`
// nodeFrom is `y` on first line // nodeFrom is `y` on first line
// nodeTo is `y` on second line // nodeTo is `y` on second line
exists(SsaImpl::EssaDefinition def, Cfg::NameNode toVariant | exists(EssaDefinition def |
nodeFrom.(CfgNode).getNode() = def.(SsaImpl::EssaNodeDefinition).getDefiningNode() nodeFrom.(CfgNode).getNode() = def.(EssaNodeDefinition).getDefiningNode()
or or
nodeFrom.(ScopeEntryDefinitionNode).getDefinition() = def nodeFrom.(ScopeEntryDefinitionNode).getDefinition() = def
| |
SsaImpl::AdjacentUses::firstUse(def, toVariant) and AdjacentUses::firstUse(def, nodeTo.(CfgNode).getNode())
toVariant.getNode() = nodeTo.(CfgNode).getNode().getNode()
) )
or or
// Next use after use // Next use after use
@@ -613,9 +557,9 @@ predicate runtimeJumpStep(Node nodeFrom, Node nodeTo) {
// a parameter with a default value, since the parameter will be in the scope of the // a parameter with a default value, since the parameter will be in the scope of the
// function, while the default value itself will be in the scope that _defines_ the // function, while the default value itself will be in the scope that _defines_ the
// function. // function.
exists(SsaImpl::ParameterDefinition param | exists(ParameterDefinition param |
// note: we go to the _control-flow node_ of the parameter, and not the ESSA node of the parameter, since for type-tracking, the ESSA node is not a LocalSourceNode, so we would get in trouble. // note: we go to the _control-flow node_ of the parameter, and not the ESSA node of the parameter, since for type-tracking, the ESSA node is not a LocalSourceNode, so we would get in trouble.
nodeFrom.asCfgNode().getNode() = param.getParameter().(Parameter).getDefault() and nodeFrom.asCfgNode() = param.getDefault() and
nodeTo.asCfgNode() = param.getDefiningNode() nodeTo.asCfgNode() = param.getDefiningNode()
) )
or or
@@ -719,7 +663,7 @@ predicate neverSkipInPathGraph(Node n) {
// ``` // ```
// we would end up saying that the path MUST not skip the x in `y = x`, which is just // we would end up saying that the path MUST not skip the x in `y = x`, which is just
// annoying and doesn't help the path explanation become clearer. // annoying and doesn't help the path explanation become clearer.
n.asCfgNode() = any(SsaImpl::EssaNodeDefinition def).getDefiningNode() n.asCfgNode() = any(EssaNodeDefinition def).getDefiningNode()
} }
/** /**
@@ -930,7 +874,7 @@ predicate listStoreStep(CfgNode nodeFrom, ListElementContent c, CfgNode nodeTo)
// nodeFrom is `42`, cfg node // nodeFrom is `42`, cfg node
// nodeTo is the list, `[..., 42, ...]`, cfg node // nodeTo is the list, `[..., 42, ...]`, cfg node
// c denotes element of list // c denotes element of list
nodeTo.getNode().(Cfg::ListNode).getAnElement() = nodeFrom.getNode() and nodeTo.getNode().(ListNode).getAnElement() = nodeFrom.getNode() and
not nodeTo.getNode() instanceof UnpackingAssignmentSequenceTarget and not nodeTo.getNode() instanceof UnpackingAssignmentSequenceTarget and
// Suppress unused variable warning // Suppress unused variable warning
c = c c = c
@@ -943,7 +887,7 @@ predicate setStoreStep(CfgNode nodeFrom, SetElementContent c, CfgNode nodeTo) {
// nodeFrom is `42`, cfg node // nodeFrom is `42`, cfg node
// nodeTo is the set, `{..., 42, ...}`, cfg node // nodeTo is the set, `{..., 42, ...}`, cfg node
// c denotes element of list // c denotes element of list
nodeTo.getNode().(Cfg::SetNode).getAnElement() = nodeFrom.getNode() and nodeTo.getNode().(SetNode).getAnElement() = nodeFrom.getNode() and
// Suppress unused variable warning // Suppress unused variable warning
c = c c = c
} }
@@ -956,7 +900,7 @@ predicate tupleStoreStep(CfgNode nodeFrom, TupleElementContent c, CfgNode nodeTo
// nodeTo is the tuple, `(..., 42, ...)`, cfg node // nodeTo is the tuple, `(..., 42, ...)`, cfg node
// c denotes element of tuple and index of nodeFrom // c denotes element of tuple and index of nodeFrom
exists(int n | exists(int n |
nodeTo.getNode().(Cfg::TupleNode).getElement(n) = nodeFrom.getNode() and nodeTo.getNode().(TupleNode).getElement(n) = nodeFrom.getNode() and
not nodeTo.getNode() instanceof UnpackingAssignmentSequenceTarget and not nodeTo.getNode() instanceof UnpackingAssignmentSequenceTarget and
c.getIndex() = n c.getIndex() = n
) )
@@ -970,7 +914,7 @@ predicate dictStoreStep(CfgNode nodeFrom, DictionaryElementContent c, Node nodeT
// nodeTo is the dict, `{..., "key" = 42, ...}`, cfg node // nodeTo is the dict, `{..., "key" = 42, ...}`, cfg node
// c denotes element of dictionary and the key `"key"` // c denotes element of dictionary and the key `"key"`
exists(KeyValuePair item | exists(KeyValuePair item |
item = nodeTo.asCfgNode().(Cfg::DictNode).getNode().(Dict).getAnItem() and item = nodeTo.asCfgNode().(DictNode).getNode().(Dict).getAnItem() and
nodeFrom.getNode().getNode() = item.getValue() and nodeFrom.getNode().getNode() = item.getValue() and
c.getKey() = item.getKey().(StringLiteral).getS() c.getKey() = item.getKey().(StringLiteral).getS()
) )
@@ -985,9 +929,9 @@ predicate dictStoreStep(CfgNode nodeFrom, DictionaryElementContent c, Node nodeT
private predicate moreDictStoreSteps(CfgNode nodeFrom, DictionaryElementContent c, Node nodeTo) { private predicate moreDictStoreSteps(CfgNode nodeFrom, DictionaryElementContent c, Node nodeTo) {
// NOTE: It's important to add logic to the newtype definition of // NOTE: It's important to add logic to the newtype definition of
// DictionaryElementContent if you add new cases here. // DictionaryElementContent if you add new cases here.
exists(Cfg::SubscriptNode subscript | exists(SubscriptNode subscript |
nodeTo.(PostUpdateNode).getPreUpdateNode().asCfgNode() = subscript.getObject() and nodeTo.(PostUpdateNode).getPreUpdateNode().asCfgNode() = subscript.getObject() and
nodeFrom.asCfgNode() = subscript.(Cfg::DefinitionNode).getValue() and nodeFrom.asCfgNode() = subscript.(DefinitionNode).getValue() and
c.getKey() = subscript.getIndex().getNode().(StringLiteral).getText() c.getKey() = subscript.getIndex().getNode().(StringLiteral).getText()
) )
or or
@@ -1000,8 +944,8 @@ private predicate moreDictStoreSteps(CfgNode nodeFrom, DictionaryElementContent
} }
predicate dictClearStep(Node node, DictionaryElementContent c) { predicate dictClearStep(Node node, DictionaryElementContent c) {
exists(Cfg::SubscriptNode subscript | exists(SubscriptNode subscript |
subscript instanceof Cfg::DefinitionNode and subscript instanceof DefinitionNode and
node.asCfgNode() = subscript.getObject() and node.asCfgNode() = subscript.getObject() and
c.getKey() = subscript.getIndex().getNode().(StringLiteral).getText() c.getKey() = subscript.getIndex().getNode().(StringLiteral).getText()
) )
@@ -1080,7 +1024,7 @@ predicate subscriptReadStep(CfgNode nodeFrom, Content c, CfgNode nodeTo) {
// nodeFrom is `l`, cfg node // nodeFrom is `l`, cfg node
// nodeTo is `l[3]`, cfg node // nodeTo is `l[3]`, cfg node
// c is compatible with 3 // c is compatible with 3
nodeFrom.getNode() = nodeTo.getNode().(Cfg::SubscriptNode).getObject() and nodeFrom.getNode() = nodeTo.getNode().(SubscriptNode).getObject() and
( (
c instanceof ListElementContent c instanceof ListElementContent
or or
@@ -1089,10 +1033,10 @@ predicate subscriptReadStep(CfgNode nodeFrom, Content c, CfgNode nodeTo) {
c instanceof DictionaryElementAnyContent c instanceof DictionaryElementAnyContent
or or
c.(TupleElementContent).getIndex() = c.(TupleElementContent).getIndex() =
nodeTo.getNode().(Cfg::SubscriptNode).getIndex().getNode().(IntegerLiteral).getValue() nodeTo.getNode().(SubscriptNode).getIndex().getNode().(IntegerLiteral).getValue()
or or
c.(DictionaryElementContent).getKey() = c.(DictionaryElementContent).getKey() =
nodeTo.getNode().(Cfg::SubscriptNode).getIndex().getNode().(StringLiteral).getS() nodeTo.getNode().(SubscriptNode).getIndex().getNode().(StringLiteral).getS()
) )
} }
@@ -1147,7 +1091,7 @@ module Conversions {
predicate formatReadStep(Node nodeFrom, ContentSet c, Node nodeTo) { predicate formatReadStep(Node nodeFrom, ContentSet c, Node nodeTo) {
// % formatting // % formatting
exists(Cfg::BinaryExprNode fmt | fmt = nodeTo.asCfgNode() | exists(BinaryExprNode fmt | fmt = nodeTo.asCfgNode() |
fmt.getOp() instanceof Mod and fmt.getOp() instanceof Mod and
fmt.getRight() = nodeFrom.asCfgNode() fmt.getRight() = nodeFrom.asCfgNode()
) and ) and

View File

@@ -5,14 +5,11 @@ overlay[local]
module; module;
private import python private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.controlflow.internal.AstNodeImpl as CfgImpl
private import codeql.controlflow.SuccessorType
private import DataFlowPrivate private import DataFlowPrivate
import semmle.python.dataflow.new.TypeTracking import semmle.python.dataflow.new.TypeTracking
import Attributes import Attributes
import LocalSources import LocalSources
private import semmle.python.dataflow.new.internal.SsaImpl as SsaImpl private import semmle.python.essa.SsaCompute
private import semmle.python.dataflow.new.internal.ImportStar private import semmle.python.dataflow.new.internal.ImportStar
private import semmle.python.frameworks.data.ModelsAsData private import semmle.python.frameworks.data.ModelsAsData
private import FlowSummaryImpl as FlowSummaryImpl private import FlowSummaryImpl as FlowSummaryImpl
@@ -30,18 +27,16 @@ private import semmle.python.frameworks.data.ModelsAsData
overlay[local] overlay[local]
newtype TNode = newtype TNode =
/** A node corresponding to a control flow node. */ /** A node corresponding to a control flow node. */
TCfgNode(Cfg::ControlFlowNode node) { TCfgNode(ControlFlowNode node) {
isExpressionNode(node) isExpressionNode(node)
or or
node.injects(_) and node.getNode() instanceof Pattern node.getNode() instanceof Pattern
} or } or
/** /**
* A node corresponding to a scope entry definition. That is, the value of a variable * A node corresponding to a scope entry definition. That is, the value of a variable
* as it enters a scope. * as it enters a scope.
*/ */
TScopeEntryDefinitionNode(SsaImpl::ScopeEntryDefinition def) { TScopeEntryDefinitionNode(ScopeEntryDefinition def) { not def.getScope() instanceof Module } or
not def.getScope() instanceof Module
} or
/** /**
* A synthetic node representing the value of an object before a state change. * A synthetic node representing the value of an object before a state change.
* *
@@ -52,39 +47,36 @@ newtype TNode =
// NOTE: since we can't rely on the call graph, but we want to have synthetic // NOTE: since we can't rely on the call graph, but we want to have synthetic
// pre-update nodes for class calls, we end up getting synthetic pre-update nodes for // pre-update nodes for class calls, we end up getting synthetic pre-update nodes for
// ALL calls :| // ALL calls :|
TSyntheticPreUpdateNode(Cfg::CallNode call) { call.injects(_) } or TSyntheticPreUpdateNode(CallNode call) or
/** /**
* A synthetic node representing the value of an object after a state change. * A synthetic node representing the value of an object after a state change.
* See QLDoc for `PostUpdateNode`. * See QLDoc for `PostUpdateNode`.
*/ */
TSyntheticPostUpdateNode(Cfg::ControlFlowNode node) { TSyntheticPostUpdateNode(ControlFlowNode node) {
node.injects(_) and exists(CallNode call |
( node = call.getArg(_)
exists(Cfg::CallNode call |
node = call.getArg(_)
or
node = call.getArgByName(_)
or
// `self` argument when handling class instance calls (`__call__` special method))
node = call.getFunction()
)
or or
node = any(Cfg::AttrNode a).getObject() node = call.getArgByName(_)
or or
node = any(Cfg::SubscriptNode s).getObject() // `self` argument when handling class instance calls (`__call__` special method))
or node = call.getFunction()
// self parameter when used implicitly in `super()`
exists(Class cls, Function func, SsaImpl::ParameterDefinition def |
func = cls.getAMethod() and
not isStaticmethod(func) and
// this matches what we do in ExtractedParameterNode
def.getDefiningNode() = node and
def.getParameter() = func.getArg(0)
)
or
// the iterable argument to the implicit comprehension function
node.getNode() = any(Comp c).getIterable()
) )
or
node = any(AttrNode a).getObject()
or
node = any(SubscriptNode s).getObject()
or
// self parameter when used implicitly in `super()`
exists(Class cls, Function func, ParameterDefinition def |
func = cls.getAMethod() and
not isStaticmethod(func) and
// this matches what we do in ExtractedParameterNode
def.getDefiningNode() = node and
def.getParameter() = func.getArg(0)
)
or
// the iterable argument to the implicit comprehension function
node.getNode() = any(Comp c).getIterable()
} or } or
/** A node representing a global (module-level) variable in a specific module. */ /** A node representing a global (module-level) variable in a specific module. */
TModuleVariableNode(Module m, GlobalVariable v) { v.getScope() = m } or TModuleVariableNode(Module m, GlobalVariable v) { v.getScope() = m } or
@@ -120,9 +112,7 @@ newtype TNode =
exists(ParameterPosition ppos | ppos.isStarArgs(_) | exists(callable.getParameter(ppos))) exists(ParameterPosition ppos | ppos.isStarArgs(_) | exists(callable.getParameter(ppos)))
} or } or
/** A synthetic node to capture keyword arguments that are passed to a `**kwargs` parameter. */ /** A synthetic node to capture keyword arguments that are passed to a `**kwargs` parameter. */
TSynthDictSplatArgumentNode(Cfg::CallNode call) { TSynthDictSplatArgumentNode(CallNode call) { exists(call.getArgByName(_)) } or
call.injects(_) and exists(call.getArgByName(_))
} or
/** A synthetic node to allow flow to keyword parameters from a `**kwargs` argument. */ /** A synthetic node to allow flow to keyword parameters from a `**kwargs` argument. */
TSynthDictSplatParameterNode(DataFlowCallable callable) { TSynthDictSplatParameterNode(DataFlowCallable callable) {
exists(ParameterPosition ppos | ppos.isKeyword(_) | exists(callable.getParameter(ppos))) exists(ParameterPosition ppos | ppos.isKeyword(_) | exists(callable.getParameter(ppos)))
@@ -138,15 +128,15 @@ newtype TNode =
* A synthetic node representing the values of the variables captured * A synthetic node representing the values of the variables captured
* by the callable being called. * by the callable being called.
*/ */
TSynthCapturedVariablesArgumentNode(Cfg::ControlFlowNode callable) { TSynthCapturedVariablesArgumentNode(ControlFlowNode callable) {
callable.injects(_) and callable = any(Cfg::CallNode c).getFunction() callable = any(CallNode c).getFunction()
} or } or
/** /**
* A synthetic node representing the values of the variables captured * A synthetic node representing the values of the variables captured
* by the callable being called, after the output has been computed. * by the callable being called, after the output has been computed.
*/ */
TSynthCapturedVariablesArgumentPostUpdateNode(Cfg::ControlFlowNode callable) { TSynthCapturedVariablesArgumentPostUpdateNode(ControlFlowNode callable) {
callable.injects(_) and callable = any(Cfg::CallNode c).getFunction() callable = any(CallNode c).getFunction()
} or } or
/** A synthetic node representing the values of variables captured by a comprehension. */ /** A synthetic node representing the values of variables captured by a comprehension. */
TSynthCompCapturedVariablesArgumentNode(Comp comp) { TSynthCompCapturedVariablesArgumentNode(Comp comp) {
@@ -204,7 +194,7 @@ class Node extends TNode {
} }
/** Gets the control-flow node corresponding to this node, if any. */ /** Gets the control-flow node corresponding to this node, if any. */
Cfg::ControlFlowNode asCfgNode() { none() } ControlFlowNode asCfgNode() { none() }
/** Gets the expression corresponding to this node, if any. */ /** Gets the expression corresponding to this node, if any. */
Expr asExpr() { none() } Expr asExpr() { none() }
@@ -217,14 +207,14 @@ class Node extends TNode {
/** A data-flow node corresponding to a control-flow node. */ /** A data-flow node corresponding to a control-flow node. */
class CfgNode extends Node, TCfgNode { class CfgNode extends Node, TCfgNode {
Cfg::ControlFlowNode node; ControlFlowNode node;
CfgNode() { this = TCfgNode(node) } CfgNode() { this = TCfgNode(node) }
/** Gets the `Cfg::ControlFlowNode` represented by this data-flow node. */ /** Gets the `ControlFlowNode` represented by this data-flow node. */
Cfg::ControlFlowNode getNode() { result = node } ControlFlowNode getNode() { result = node }
override Cfg::ControlFlowNode asCfgNode() { result = node } override ControlFlowNode asCfgNode() { result = node }
/** Gets a textual representation of this element. */ /** Gets a textual representation of this element. */
override string toString() { result = node.toString() } override string toString() { result = node.toString() }
@@ -234,9 +224,9 @@ class CfgNode extends Node, TCfgNode {
override Location getLocation() { result = node.getLocation() } override Location getLocation() { result = node.getLocation() }
} }
/** A data-flow node corresponding to a `Cfg::CallNode` in the control-flow graph. */ /** A data-flow node corresponding to a `CallNode` in the control-flow graph. */
class CallCfgNode extends CfgNode, LocalSourceNode { class CallCfgNode extends CfgNode, LocalSourceNode {
override Cfg::CallNode node; override CallNode node;
/** /**
* Gets the data-flow node for the function component of the call corresponding to this data-flow * Gets the data-flow node for the function component of the call corresponding to this data-flow
@@ -317,15 +307,15 @@ ExprNode exprNode(DataFlowExpr e) { result.getNode().getNode() = e }
* as it enters a scope. * as it enters a scope.
*/ */
class ScopeEntryDefinitionNode extends Node, TScopeEntryDefinitionNode { class ScopeEntryDefinitionNode extends Node, TScopeEntryDefinitionNode {
SsaImpl::ScopeEntryDefinition def; ScopeEntryDefinition def;
ScopeEntryDefinitionNode() { this = TScopeEntryDefinitionNode(def) } ScopeEntryDefinitionNode() { this = TScopeEntryDefinitionNode(def) }
/** Gets the `SsaImpl::ScopeEntryDefinition` associated with this node. */ /** Gets the `ScopeEntryDefinition` associated with this node. */
SsaImpl::ScopeEntryDefinition getDefinition() { result = def } ScopeEntryDefinition getDefinition() { result = def }
/** Gets the source variable represented by this node. */ /** Gets the source variable represented by this node. */
SsaImpl::SsaSourceVariable getVariable() { result = def.getSourceVariable() } SsaSourceVariable getVariable() { result = def.getSourceVariable() }
override Location getLocation() { result = def.getLocation() } override Location getLocation() { result = def.getLocation() }
@@ -347,7 +337,7 @@ class ParameterNode extends Node instanceof ParameterNodeImpl {
/** A parameter node found in the source code (not in a summary). */ /** A parameter node found in the source code (not in a summary). */
class ExtractedParameterNode extends ParameterNodeImpl, CfgNode { class ExtractedParameterNode extends ParameterNodeImpl, CfgNode {
//, LocalSourceNode { //, LocalSourceNode {
SsaImpl::ParameterDefinition def; ParameterDefinition def;
ExtractedParameterNode() { node = def.getDefiningNode() } ExtractedParameterNode() { node = def.getDefiningNode() }
@@ -378,10 +368,10 @@ Node getCallArgApproximation() {
exists(Class c | result.asExpr() = c.getAMethod().getArg(0)) exists(Class c | result.asExpr() = c.getAMethod().getArg(0))
or or
// the object part of an attribute expression (which might be a bound method) // the object part of an attribute expression (which might be a bound method)
result.asCfgNode() = any(Cfg::AttrNode a).getObject() result.asCfgNode() = any(AttrNode a).getObject()
or or
// the function part of any call // the function part of any call
result.asCfgNode() = any(Cfg::CallNode c).getFunction() result.asCfgNode() = any(CallNode c).getFunction()
} }
/** Gets the extracted argument nodes that do not rely on `getCallArg`. */ /** Gets the extracted argument nodes that do not rely on `getCallArg`. */
@@ -390,7 +380,7 @@ private Node implicitArgumentNode() {
normalCallArg(_, result, _) normalCallArg(_, result, _)
or or
// and self arguments // and self arguments
result.asCfgNode() = any(Cfg::CallNode c).getFunction().(Cfg::AttrNode).getObject() result.asCfgNode() = any(CallNode c).getFunction().(AttrNode).getObject()
or or
// for comprehensions, we allow the synthetic `iterable` argument // for comprehensions, we allow the synthetic `iterable` argument
result.asExpr() = any(Comp c).getIterable() result.asExpr() = any(Comp c).getIterable()
@@ -499,20 +489,17 @@ class ModuleVariableNode extends Node, TModuleVariableNode {
not result.getScope() = mod not result.getScope() = mod
} }
/** Gets a CFG node that corresponds to an assignment of this global variable. */ /** Gets an `EssaNode` that corresponds to an assignment of this global variable. */
Node getAWrite() { Node getAWrite() {
exists(Cfg::NameNode n | any(EssaNodeDefinition def).definedBy(var, result.asCfgNode().(DefinitionNode))
n.defines(var) and
result.asCfgNode() = n
)
} }
/** Gets the possible values of the variable at the end of import time */ /** Gets the possible values of the variable at the end of import time */
CfgNode getADefiningWrite() { CfgNode getADefiningWrite() {
exists(SsaImpl::EssaVariable def | exists(SsaVariable def |
def = any(SsaImpl::EssaVariable ssa_var).getAnUltimateDefinition() and def = any(SsaVariable ssa_var).getAnUltimateDefinition() and
def.getDefinition().(SsaImpl::EssaNodeDefinition).getDefiningNode() = result.asCfgNode() and def.getDefinition() = result.asCfgNode() and
def.getSourceVariable().getVariable() = var def.getVariable() = var
) )
} }
@@ -529,7 +516,7 @@ private ModuleVariableNode import_star_read(Node n) {
overlay[global] overlay[global]
pragma[nomagic] pragma[nomagic]
private predicate resolved_import_star_module(Module m, string name, Node n) { private predicate resolved_import_star_module(Module m, string name, Node n) {
exists(Cfg::NameNode nn | nn = n.asCfgNode() | exists(NameNode nn | nn = n.asCfgNode() |
ImportStar::importStarResolvesTo(pragma[only_bind_into](nn), m) and ImportStar::importStarResolvesTo(pragma[only_bind_into](nn), m) and
nn.getId() = name nn.getId() = name
) )
@@ -543,9 +530,9 @@ private predicate resolved_import_star_module(Module m, string name, Node n) {
* step to TIterableElement followed by a store step to the target. * step to TIterableElement followed by a store step to the target.
*/ */
class IterableSequenceNode extends Node, TIterableSequenceNode { class IterableSequenceNode extends Node, TIterableSequenceNode {
UnpackingAssignmentSequenceTarget consumer; CfgNode consumer;
IterableSequenceNode() { this = TIterableSequenceNode(consumer) } IterableSequenceNode() { this = TIterableSequenceNode(consumer.getNode()) }
override string toString() { result = "IterableSequence" } override string toString() { result = "IterableSequence" }
@@ -560,9 +547,9 @@ class IterableSequenceNode extends Node, TIterableSequenceNode {
* read step from the list to IterableElement followed by a store step to the tuple. * read step from the list to IterableElement followed by a store step to the tuple.
*/ */
class IterableElementNode extends Node, TIterableElementNode { class IterableElementNode extends Node, TIterableElementNode {
UnpackingAssignmentTarget consumer; CfgNode consumer;
IterableElementNode() { this = TIterableElementNode(consumer) } IterableElementNode() { this = TIterableElementNode(consumer.getNode()) }
override string toString() { result = "IterableElement" } override string toString() { result = "IterableElement" }
@@ -587,88 +574,88 @@ class StarPatternElementNode extends Node, TStarPatternElementNode {
} }
/** /**
* A node that participates in a conditional split: a CFG node whose * Gets a node that controls whether other nodes are evaluated.
* evaluation outcome (true/false) is used to choose between two
* successor basic blocks. In the shared CFG, branching is detected
* via typed successor edges (boolean successor types) on the unique
* `injects` node for each AST expression.
* *
* Users typically obtain a `GuardNode` by casting from a more specific * In the base case, this is the last node of `conditionBlock`, and `flipped` is `false`.
* Cfg type: `g.(Cfg::CallNode)` for a call-based check, etc. * This definition accounts for (short circuting) `and`- and `or`-expressions, as the structure
* of basic blocks will reflect their semantics.
*
* However, in the program
* ```python
* if not is_safe(path):
* return
* ```
* the last node in the `ConditionBlock` is `not is_safe(path)`.
*
* We would like to consider also `is_safe(path)` a guard node, albeit with `flipped` being `true`.
* Thus we recurse through `not`-expressions.
*/ */
class GuardNode extends Cfg::ControlFlowNode { ControlFlowNode guardNode(ConditionBlock conditionBlock, boolean flipped) {
GuardNode() { // Base case: the last node truly does determine which successor is chosen
// This node has boolean successor edges (directly or via wrapping). result = conditionBlock.getLastNode() and
outcomeOfGuard(this, _, _) flipped = false
} or
// Recursive cases:
/** Holds if this guard controls block `b` upon evaluating to `branch`. */ // if a guard node is a `not`-expression,
predicate controlsBlock(Cfg::BasicBlock b, boolean branch) { // the operand is also a guard node, but with inverted polarity.
exists(CfgImpl::BasicBlock outcomeBB | exists(UnaryExprNode notNode |
outcomeOfGuard(this, outcomeBB, branch) and result = notNode.getOperand() and
outcomeBB.dominates(b) notNode.getNode().getOp() instanceof Not
|
notNode = guardNode(conditionBlock, flipped.booleanNot())
)
or
// if a guard node is compared to a boolean literal,
// the other operand is also a guard node,
// but with polarity depending on the literal (and on the comparison).
exists(CompareNode cmpNode, Cmpop op, ControlFlowNode b, boolean should_flip |
(
cmpNode.operands(result, op, b) or
cmpNode.operands(b, op, result)
) and
not result.getNode() instanceof BooleanLiteral and
(
// comparing to the boolean
(op instanceof Eq or op instanceof Is) and
// we should flip if the value compared against, here the value of `b`, is false
should_flip = b.getNode().(BooleanLiteral).booleanValue().booleanNot()
or
// comparing to the negation of the boolean
(op instanceof NotEq or op instanceof IsNot) and
// again, we should flip if the value compared against, here the value of `not b`, is false.
// That is, if the value of `b` is true.
should_flip = b.getNode().(BooleanLiteral).booleanValue()
) )
} |
// we flip `flipped` according to `should_flip` via the formula `flipped xor should_flip`.
flipped in [true, false] and
cmpNode = guardNode(conditionBlock, flipped.booleanXor(should_flip))
)
} }
/** /**
* Holds if `outcomeBB` is the basic block entered when `guard` evaluates * A node that controls whether other nodes are evaluated.
* to `branch`.
* *
* For a direct guard `if g:`, the outcome BB starts at the after-value * The field `flipped` allows us to match `GuardNode`s underneath
* node for the matching branch. For wrapped guards like `not g` or * `not`-expressions and still choose the appropriate branch.
* `g == True`, we follow those wrappers up the AST to find the
* outermost expression that actually branches, with an appropriate
* polarity transform.
*/ */
private predicate outcomeOfGuard( class GuardNode extends ControlFlowNode {
Cfg::ControlFlowNode guard, CfgImpl::BasicBlock outcomeBB, boolean branch ConditionBlock conditionBlock;
) { boolean flipped;
// Base case: the guard has boolean successor edges.
// Only the canonical representative (injects) can act as a guard base. GuardNode() { this = guardNode(conditionBlock, flipped) }
guard.injects(_) and
exists(BooleanSuccessor t | /** Holds if this guard controls block `b` upon evaluating to `branch`. */
t.getValue() = branch and predicate controlsBlock(BasicBlock b, boolean branch) {
outcomeBB = guard.(CfgImpl::ControlFlowNode).getASuccessor(t).getBasicBlock() branch in [true, false] and
) conditionBlock.controls(b, branch.booleanXor(flipped))
or }
// Recursive: `not guard` — same outcome split as `guard`, flipped.
exists(Cfg::UnaryExprNode notNode, boolean notBranch |
notNode.injects(_) and
notNode.getOperand().getNode() = guard.getNode() and
notNode.getNode().getOp() instanceof Not and
outcomeOfGuard(notNode, outcomeBB, notBranch) and
branch = notBranch.booleanNot()
)
or
// Recursive: comparisons against a boolean literal.
exists(
Cfg::CompareNode cmpNode, Cmpop op, Cfg::ControlFlowNode otherOperand,
Cfg::ControlFlowNode guardOperand, boolean polarity, boolean cmpBranch
|
cmpNode.injects(_) and
guardOperand.getNode() = guard.getNode() and
(
cmpNode.operands(guardOperand, op, otherOperand) or
cmpNode.operands(otherOperand, op, guardOperand)
) and
not guard.getNode() instanceof BooleanLiteral and
(
(op instanceof Eq or op instanceof Is) and
polarity = otherOperand.getNode().(BooleanLiteral).booleanValue()
or
(op instanceof NotEq or op instanceof IsNot) and
polarity = otherOperand.getNode().(BooleanLiteral).booleanValue().booleanNot()
) and
outcomeOfGuard(cmpNode, outcomeBB, cmpBranch) and
branch = cmpBranch.booleanXor(polarity.booleanNot())
)
} }
/** /**
* Holds if the guard `g` validates `node` upon evaluating to `branch`. * Holds if the guard `g` validates `node` upon evaluating to `branch`.
*/ */
signature predicate guardChecksSig(GuardNode g, Cfg::ControlFlowNode node, boolean branch); signature predicate guardChecksSig(GuardNode g, ControlFlowNode node, boolean branch);
/** /**
* Provides a set of barrier nodes for a guard that validates a node. * Provides a set of barrier nodes for a guard that validates a node.
@@ -683,9 +670,7 @@ module BarrierGuard<guardChecksSig/3 guardChecks> {
result = ParameterizedBarrierGuard<Unit, extendedGuardChecks/4>::getABarrierNode(_) result = ParameterizedBarrierGuard<Unit, extendedGuardChecks/4>::getABarrierNode(_)
} }
private predicate extendedGuardChecks( private predicate extendedGuardChecks(GuardNode g, ControlFlowNode node, boolean branch, Unit u) {
GuardNode g, Cfg::ControlFlowNode node, boolean branch, Unit u
) {
guardChecks(g, node, branch) and guardChecks(g, node, branch) and
u = u u = u
} }
@@ -695,7 +680,7 @@ bindingset[this]
private signature class ParamSig; private signature class ParamSig;
private module WithParam<ParamSig P> { private module WithParam<ParamSig P> {
signature predicate guardChecksSig(GuardNode g, Cfg::ControlFlowNode node, boolean branch, P param); signature predicate guardChecksSig(GuardNode g, ControlFlowNode node, boolean branch, P param);
} }
/** /**
@@ -708,16 +693,10 @@ module ParameterizedBarrierGuard<ParamSig P, WithParam<P>::guardChecksSig/4 guar
/** Gets a node that is safely guarded by the given guard check with parameter `param`. */ /** Gets a node that is safely guarded by the given guard check with parameter `param`. */
overlay[global] overlay[global]
ExprNode getABarrierNode(P param) { ExprNode getABarrierNode(P param) {
exists(GuardNode g, SsaImpl::EssaDefinition def, Cfg::ControlFlowNode node, boolean branch | exists(GuardNode g, EssaDefinition def, ControlFlowNode node, boolean branch |
SsaImpl::AdjacentUses::useOfDef(def, node) and AdjacentUses::useOfDef(def, node) and
guardChecks(g, node, branch, param) and guardChecks(g, node, branch, param) and
SsaImpl::AdjacentUses::useOfDef(def, result.asCfgNode()) and AdjacentUses::useOfDef(def, result.asCfgNode()) and
// The protected use must be a different SSA position than the test
// position itself: `controlsBlock` is reflexive on dominance, and
// the test expression is an SSA-use position on the def-use chain.
// Without this guard, the test position would be returned as a
// barrier and block flow before it can reach genuine branch uses.
node != result.asCfgNode() and
g.controlsBlock(result.asCfgNode().getBasicBlock(), branch) g.controlsBlock(result.asCfgNode().getBasicBlock(), branch)
) )
} }
@@ -733,7 +712,7 @@ module ExternalBarrierGuard {
private import semmle.python.ApiGraphs private import semmle.python.ApiGraphs
overlay[global] overlay[global]
private predicate guardCheck(GuardNode g, Cfg::ControlFlowNode node, boolean branch, string kind) { private predicate guardCheck(GuardNode g, ControlFlowNode node, boolean branch, string kind) {
exists(API::CallNode call, API::Node parameter | exists(API::CallNode call, API::Node parameter |
parameter = call.getAParameter() and parameter = call.getAParameter() and
parameter = ModelOutput::getABarrierGuardNode(kind, branch) parameter = ModelOutput::getABarrierGuardNode(kind, branch)
@@ -769,10 +748,10 @@ newtype TContent =
TSetElementContent() or TSetElementContent() or
/** An element of a tuple at a specific index. */ /** An element of a tuple at a specific index. */
TTupleElementContent(int index) { TTupleElementContent(int index) {
exists(any(Cfg::TupleNode tn).getElement(index)) exists(any(TupleNode tn).getElement(index))
or or
// Arguments can overflow and end up in the starred parameter tuple. // Arguments can overflow and end up in the starred parameter tuple.
exists(any(Cfg::CallNode cn).getArg(index)) exists(any(CallNode cn).getArg(index))
or or
// since flow summaries might use tuples, we ensure that we at least have valid // since flow summaries might use tuples, we ensure that we at least have valid
// TTupleElementContent for the 0..7 (7 was picked to match `small_tuple` in // TTupleElementContent for the 0..7 (7 was picked to match `small_tuple` in
@@ -789,14 +768,10 @@ newtype TContent =
or or
// d["key"] = ... // d["key"] = ...
key = key =
any(Cfg::SubscriptNode sub | any(SubscriptNode sub | sub.isStore() | sub.getIndex().getNode().(StringLiteral).getText())
sub.isStore()
|
sub.getIndex().getNode().(StringLiteral).getText()
)
or or
// d.setdefault("key", ...) // d.setdefault("key", ...)
exists(Cfg::CallNode call | call.getFunction().(Cfg::AttrNode).getName() = "setdefault" | exists(CallNode call | call.getFunction().(AttrNode).getName() = "setdefault" |
key = call.getArg(0).getNode().(StringLiteral).getText() key = call.getArg(0).getNode().(StringLiteral).getText()
) )
} or } or

View File

@@ -5,18 +5,17 @@
*/ */
private import python private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.dataflow.new.internal.SsaImpl as SsaImpl
private import semmle.python.dataflow.new.DataFlow private import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.internal.ImportStar private import semmle.python.dataflow.new.internal.ImportStar
private import semmle.python.dataflow.new.TypeTracking private import semmle.python.dataflow.new.TypeTracking
private import semmle.python.dataflow.new.internal.DataFlowPrivate private import semmle.python.dataflow.new.internal.DataFlowPrivate
/** /**
* Holds if the name of `var` refers to a submodule of a package and `init` is * Holds if `init` is a package's `__init__.py` and `var` is a global variable in
* the `__init__` module of that package. Locally inlined replacement for the * `init` whose name matches a submodule of the package.
* legacy `SsaSource::init_module_submodule_defn` so that this module has no *
* direct dependency on `semmle.python.essa.SsaDefinitions`. * Inlined from `SsaSource::init_module_submodule_defn` to avoid pulling
* `semmle.python.essa.SsaDefinitions` into the new dataflow stack.
*/ */
private predicate initModuleSubmoduleDefn(GlobalVariable var, Module init) { private predicate initModuleSubmoduleDefn(GlobalVariable var, Module init) {
init.isPackageInit() and init.isPackageInit() and
@@ -82,19 +81,13 @@ module ImportResolution {
* Holds if there is an ESSA step from `defFrom` to `defTo`, which should be allowed * Holds if there is an ESSA step from `defFrom` to `defTo`, which should be allowed
* for import resolution. * for import resolution.
*/ */
private predicate allowedEssaImportStep( private predicate allowedEssaImportStep(EssaDefinition defFrom, EssaDefinition defTo) {
SsaImpl::EssaDefinition defFrom, SsaImpl::EssaDefinition defTo
) {
// to handle definitions guarded by if-then-else // to handle definitions guarded by if-then-else
defFrom = defTo.(SsaImpl::PhiFunction).getAnInput() defFrom = defTo.(PhiFunction).getAnInput()
or or
// to handle uncertain writes such as `from X import *`, which create an // refined variable
// uncertain SSA definition for every name in the importing scope. The // example: https://github.com/nvbn/thefuck/blob/ceeaeab94b5df5a4fe9d94d61e4f6b0bbea96378/thefuck/utils.py#L25-L45
// immediately preceding definition is still potentially the value of the defFrom = defTo.(EssaNodeRefinement).getInput().getDefinition()
// module export.
SsaImpl::Ssa::uncertainWriteDefinitionInput(defTo, defFrom)
// Note: legacy ESSA refinement-step (e.g. for `foo.bar = X`) is
// not modelled in the new SSA beyond the cases handled above.
} }
/** /**
@@ -111,32 +104,30 @@ module ImportResolution {
// Definitions made inside `m` itself // Definitions made inside `m` itself
// //
// for code such as `foo = ...; foo.bar = ...` there will be TWO // for code such as `foo = ...; foo.bar = ...` there will be TWO
// SsaImpl::EssaDefinition/SsaImpl::EssaVariable. One for `foo = ...` (SsaImpl::AssignmentDefinition) and one // EssaDefinition/EssaVariable. One for `foo = ...` (AssignmentDefinition) and one
// for `foo.bar = ...`. The one for `foo.bar = ...` (EssaNodeRefinement). The // for `foo.bar = ...`. The one for `foo.bar = ...` (EssaNodeRefinement). The
// EssaNodeRefinement is the one that will reach the end of the module (normal // EssaNodeRefinement is the one that will reach the end of the module (normal
// exit). // exit).
// //
// However, we cannot just use the EssaNodeRefinement as the `val`, because the // However, we cannot just use the EssaNodeRefinement as the `val`, because the
// normal data-flow depends on use-use flow, and use-use flow targets CFG nodes not // normal data-flow depends on use-use flow, and use-use flow targets CFG nodes not
// EssaNodes. So we need to go back from the SsaImpl::EssaDefinition/SsaImpl::EssaVariable that // EssaNodes. So we need to go back from the EssaDefinition/EssaVariable that
// reaches the end of the module, to the first definition of the variable, and then // reaches the end of the module, to the first definition of the variable, and then
// track forwards using use-use flow to find a suitable CFG node that has flow into // track forwards using use-use flow to find a suitable CFG node that has flow into
// it from use-use flow. // it from use-use flow.
exists(SsaImpl::EssaVariable lastUseVar, SsaImpl::EssaVariable firstDef | exists(EssaVariable lastUseVar, EssaVariable firstDef |
lastUseVar.getName() = name and lastUseVar.getName() = name and
// we ignore special variable $ introduced by our analysis (not used for anything) // we ignore special variable $ introduced by our analysis (not used for anything)
// we ignore special variable * introduced by `from <pkg> import *` -- TODO: understand why we even have this? // we ignore special variable * introduced by `from <pkg> import *` -- TODO: understand why we even have this?
not name in ["$", "*"] and not name in ["$", "*"] and
exists(Cfg::ControlFlowNode exit | lastUseVar.getAUse() = m.getANormalExit() and
exit.isNormalExit() and exit.getScope() = m and lastUseVar.getAUse() = exit
) and
allowedEssaImportStep*(firstDef, lastUseVar) and allowedEssaImportStep*(firstDef, lastUseVar) and
not allowedEssaImportStep(_, firstDef) not allowedEssaImportStep(_, firstDef)
| |
not LocalFlow::defToFirstUse(firstDef, _) and not LocalFlow::defToFirstUse(firstDef, _) and
val.asCfgNode() = firstDef.getDefinition().(SsaImpl::EssaNodeDefinition).getDefiningNode() val.asCfgNode() = firstDef.getDefinition().(EssaNodeDefinition).getDefiningNode()
or or
exists(Cfg::ControlFlowNode mid, Cfg::ControlFlowNode end | exists(ControlFlowNode mid, ControlFlowNode end |
LocalFlow::defToFirstUse(firstDef, mid) and LocalFlow::defToFirstUse(firstDef, mid) and
LocalFlow::useToNextUse*(mid, end) and LocalFlow::useToNextUse*(mid, end) and
not LocalFlow::useToNextUse(end, _) and not LocalFlow::useToNextUse(end, _) and
@@ -164,9 +155,9 @@ module ImportResolution {
* handles simple cases where we can statically tell that this is the case. * handles simple cases where we can statically tell that this is the case.
*/ */
private predicate all_mentions_name(Module m, string name) { private predicate all_mentions_name(Module m, string name) {
exists(Cfg::DefinitionNode def, Cfg::SequenceNode n | exists(DefinitionNode def, SequenceNode n |
def.getValue() = n and def.getValue() = n and
def.(Cfg::NameNode).getId() = "__all__" and def.(NameNode).getId() = "__all__" and
def.getScope() = m and def.getScope() = m and
any(StringLiteral s | s.getText() = name) = n.getAnElement().getNode() any(StringLiteral s | s.getText() = name) = n.getAnElement().getNode()
) )
@@ -179,20 +170,18 @@ module ImportResolution {
*/ */
private predicate no_or_complicated_all(Module m) { private predicate no_or_complicated_all(Module m) {
// No mention of `__all__` in the module // No mention of `__all__` in the module
not exists(Cfg::DefinitionNode def | not exists(DefinitionNode def | def.getScope() = m and def.(NameNode).getId() = "__all__")
def.getScope() = m and def.(Cfg::NameNode).getId() = "__all__"
)
or or
// `__all__` is set to a non-sequence value // `__all__` is set to a non-sequence value
exists(Cfg::DefinitionNode def | exists(DefinitionNode def |
def.(Cfg::NameNode).getId() = "__all__" and def.(NameNode).getId() = "__all__" and
def.getScope() = m and def.getScope() = m and
not def.getValue() instanceof Cfg::SequenceNode not def.getValue() instanceof SequenceNode
) )
or or
// `__all__` is used in some way that doesn't involve storing a value in it. This usually means // `__all__` is used in some way that doesn't involve storing a value in it. This usually means
// it is being mutated through `append` or `extend`, which we don't handle. // it is being mutated through `append` or `extend`, which we don't handle.
exists(Cfg::NameNode n | n.getId() = "__all__" and n.getScope() = m and n.isLoad()) exists(NameNode n | n.getId() = "__all__" and n.getScope() = m and n.isLoad())
} }
private predicate potential_module_export(Module m, string name) { private predicate potential_module_export(Module m, string name) {
@@ -200,7 +189,7 @@ module ImportResolution {
or or
no_or_complicated_all(m) and no_or_complicated_all(m) and
( (
exists(Cfg::NameNode n | n.getId() = name and n.getScope() = m and name.charAt(0) != "_") exists(NameNode n | n.getId() = name and n.getScope() = m and name.charAt(0) != "_")
or or
exists(Alias a | a.getAsname().(Name).getId() = name and a.getValue().getScope() = m) exists(Alias a | a.getAsname().(Name).getId() = name and a.getValue().getScope() = m)
) )
@@ -230,12 +219,12 @@ module ImportResolution {
/** Gets a module that may have been added to `sys.modules`. */ /** Gets a module that may have been added to `sys.modules`. */
private Module sys_modules_module_with_name(string name) { private Module sys_modules_module_with_name(string name) {
exists(Cfg::ControlFlowNode n, DataFlow::Node mod | exists(ControlFlowNode n, DataFlow::Node mod |
exists(Cfg::SubscriptNode sub | exists(SubscriptNode sub |
sub.getObject() = sys_modules_reference().asCfgNode() and sub.getObject() = sys_modules_reference().asCfgNode() and
sub.getIndex() = n and sub.getIndex() = n and
n.getNode().(StringLiteral).getText() = name and n.getNode().(StringLiteral).getText() = name and
sub.(Cfg::DefinitionNode).getValue() = mod.asCfgNode() and sub.(DefinitionNode).getValue() = mod.asCfgNode() and
mod = getModuleReference(result) mod = getModuleReference(result)
) )
) )
@@ -347,11 +336,11 @@ module ImportResolution {
// name as a submodule, we always consider that this attribute _could_ be a // name as a submodule, we always consider that this attribute _could_ be a
// reference to the submodule, even if we don't know that the submodule has been // reference to the submodule, even if we don't know that the submodule has been
// imported yet. // imported yet.
exists(string submodule, Module package, SsaImpl::EssaVariable var | exists(string submodule, Module package, EssaVariable var |
submodule = var.getName() and submodule = var.getName() and
initModuleSubmoduleDefn(var.getSourceVariable().getVariable(), package) and initModuleSubmoduleDefn(var.getSourceVariable(), package) and
m = getModuleFromName(package.getPackageName() + "." + submodule) and m = getModuleFromName(package.getPackageName() + "." + submodule) and
result.asCfgNode() = var.getDefinition().(SsaImpl::EssaNodeDefinition).getDefiningNode() result.asCfgNode() = var.getDefinition().(EssaNodeDefinition).getDefiningNode()
) )
} }

View File

@@ -3,7 +3,6 @@ overlay[local]
module; module;
private import python private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.dataflow.new.internal.Builtins private import semmle.python.dataflow.new.internal.Builtins
private import semmle.python.dataflow.new.internal.ImportResolution private import semmle.python.dataflow.new.internal.ImportResolution
private import semmle.python.dataflow.new.DataFlow private import semmle.python.dataflow.new.DataFlow
@@ -16,7 +15,7 @@ module ImportStar {
*/ */
overlay[local] overlay[local]
cached cached
predicate namePossiblyDefinedInImportStar(Cfg::NameNode n, string name, Scope s) { predicate namePossiblyDefinedInImportStar(NameNode n, string name, Scope s) {
n.isLoad() and n.isLoad() and
name = n.getId() and name = n.getId() and
s = n.getScope().getEnclosingScope*() and s = n.getScope().getEnclosingScope*() and
@@ -53,7 +52,7 @@ module ImportStar {
/** Holds if a global variable called `name` is assigned a value in the module `m`. */ /** Holds if a global variable called `name` is assigned a value in the module `m`. */
cached cached
predicate globalNameDefinedInModule(string name, Module m) { predicate globalNameDefinedInModule(string name, Module m) {
exists(Cfg::NameNode n | exists(NameNode n |
not exists(LocalVariable v | n.defines(v)) and not exists(LocalVariable v | n.defines(v)) and
n.isStore() and n.isStore() and
name = n.getId() and name = n.getId() and
@@ -67,7 +66,7 @@ module ImportStar {
*/ */
overlay[global] overlay[global]
cached cached
predicate importStarResolvesTo(Cfg::NameNode n, Module m) { predicate importStarResolvesTo(NameNode n, Module m) {
m = getStarImported+(n.getEnclosingModule()) and m = getStarImported+(n.getEnclosingModule()) and
globalNameDefinedInModule(n.getId(), m) and globalNameDefinedInModule(n.getId(), m) and
not isDefinedLocally(n.getNode()) not isDefinedLocally(n.getNode())
@@ -100,7 +99,7 @@ module ImportStar {
*/ */
overlay[local] overlay[local]
cached cached
Cfg::ControlFlowNode potentialImportStarBase(Scope s) { ControlFlowNode potentialImportStarBase(Scope s) {
result = any(Cfg::ImportStarNode n | n.getScope() = s).getModule() result = any(ImportStarNode n | n.getScope() = s).getModule()
} }
} }

View File

@@ -170,8 +170,6 @@ overlay[local]
module; module;
private import python private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.dataflow.new.internal.SsaImpl as SsaImpl
private import DataFlowPublic private import DataFlowPublic
/** /**
@@ -180,7 +178,7 @@ private import DataFlowPublic
* This class abstracts away the differing representations of comprehensions and * This class abstracts away the differing representations of comprehensions and
* for statements. * for statements.
*/ */
class ForTarget extends Cfg::ControlFlowNode { class ForTarget extends ControlFlowNode {
Expr source; Expr source;
ForTarget() { ForTarget() {
@@ -200,7 +198,7 @@ class ForTarget extends Cfg::ControlFlowNode {
} }
/** The LHS of an assignment, it also records the assigned value. */ /** The LHS of an assignment, it also records the assigned value. */
class AssignmentTarget extends Cfg::ControlFlowNode { class AssignmentTarget extends ControlFlowNode {
Expr value; Expr value;
AssignmentTarget() { AssignmentTarget() {
@@ -211,7 +209,7 @@ class AssignmentTarget extends Cfg::ControlFlowNode {
} }
/** A direct (or top-level) target of an unpacking assignment. */ /** A direct (or top-level) target of an unpacking assignment. */
class UnpackingAssignmentDirectTarget extends Cfg::ControlFlowNode instanceof Cfg::SequenceNode { class UnpackingAssignmentDirectTarget extends ControlFlowNode instanceof SequenceNode {
Expr value; Expr value;
UnpackingAssignmentDirectTarget() { UnpackingAssignmentDirectTarget() {
@@ -224,7 +222,7 @@ class UnpackingAssignmentDirectTarget extends Cfg::ControlFlowNode instanceof Cf
} }
/** A (possibly recursive) target of an unpacking assignment. */ /** A (possibly recursive) target of an unpacking assignment. */
class UnpackingAssignmentTarget extends Cfg::ControlFlowNode { class UnpackingAssignmentTarget extends ControlFlowNode {
UnpackingAssignmentTarget() { UnpackingAssignmentTarget() {
this instanceof UnpackingAssignmentDirectTarget this instanceof UnpackingAssignmentDirectTarget
or or
@@ -233,11 +231,10 @@ class UnpackingAssignmentTarget extends Cfg::ControlFlowNode {
} }
/** A (possibly recursive) target of an unpacking assignment which is also a sequence. */ /** A (possibly recursive) target of an unpacking assignment which is also a sequence. */
class UnpackingAssignmentSequenceTarget extends UnpackingAssignmentTarget instanceof Cfg::SequenceNode class UnpackingAssignmentSequenceTarget extends UnpackingAssignmentTarget instanceof SequenceNode {
{ ControlFlowNode getElement(int i) { result = super.getElement(i) }
Cfg::ControlFlowNode getElement(int i) { result = super.getElement(i) }
Cfg::ControlFlowNode getAnElement() { result = this.getElement(_) } ControlFlowNode getAnElement() { result = this.getElement(_) }
} }
/** /**
@@ -258,7 +255,7 @@ predicate iterableUnpackingAssignmentFlowStep(Node nodeFrom, Node nodeTo) {
predicate iterableUnpackingForReadStep(CfgNode nodeFrom, Content c, Node nodeTo) { predicate iterableUnpackingForReadStep(CfgNode nodeFrom, Content c, Node nodeTo) {
exists(ForTarget target | exists(ForTarget target |
nodeFrom.getNode().getNode() = target.getSource() and nodeFrom.getNode().getNode() = target.getSource() and
target instanceof Cfg::SequenceNode and target instanceof SequenceNode and
nodeTo = TIterableSequenceNode(target) nodeTo = TIterableSequenceNode(target)
) and ) and
( (
@@ -326,11 +323,11 @@ predicate iterableUnpackingConvertingStoreStep(Node nodeFrom, Content c, Node no
*/ */
predicate iterableUnpackingElementReadStep(Node nodeFrom, Content c, Node nodeTo) { predicate iterableUnpackingElementReadStep(Node nodeFrom, Content c, Node nodeTo) {
exists( exists(
UnpackingAssignmentSequenceTarget target, int index, Cfg::ControlFlowNode element, int starIndex UnpackingAssignmentSequenceTarget target, int index, ControlFlowNode element, int starIndex
| |
target.getElement(starIndex) instanceof Cfg::StarredNode target.getElement(starIndex) instanceof StarredNode
or or
not exists(target.getAnElement().(Cfg::StarredNode)) and not exists(target.getAnElement().(StarredNode)) and
starIndex = -1 starIndex = -1
| |
nodeFrom.(CfgNode).getNode() = target and nodeFrom.(CfgNode).getNode() = target and
@@ -345,18 +342,18 @@ predicate iterableUnpackingElementReadStep(Node nodeFrom, Content c, Node nodeTo
else c.(TupleElementContent).getIndex() >= index - 1 else c.(TupleElementContent).getIndex() >= index - 1
) and ) and
( (
if element instanceof Cfg::SequenceNode if element instanceof SequenceNode
then then
// Step 5b // Step 5b
nodeTo = TIterableSequenceNode(element) nodeTo = TIterableSequenceNode(element)
else else
if element instanceof Cfg::StarredNode if element instanceof StarredNode
then then
// Step 5c // Step 5c
nodeTo = TIterableElementNode(element) nodeTo = TIterableElementNode(element)
else else
// Step 5a // Step 5a
exists(SsaImpl::MultiAssignmentDefinition mad | element = mad.getDefiningNode() | exists(MultiAssignmentDefinition mad | element = mad.getDefiningNode() |
nodeTo.(CfgNode).getNode() = element nodeTo.(CfgNode).getNode() = element
) )
) )
@@ -369,7 +366,7 @@ predicate iterableUnpackingElementReadStep(Node nodeFrom, Content c, Node nodeTo
* content type `ListElementContent`. * content type `ListElementContent`.
*/ */
predicate iterableUnpackingStarredElementStoreStep(Node nodeFrom, Content c, Node nodeTo) { predicate iterableUnpackingStarredElementStoreStep(Node nodeFrom, Content c, Node nodeTo) {
exists(Cfg::ControlFlowNode starred, SsaImpl::MultiAssignmentDefinition mad | exists(ControlFlowNode starred, MultiAssignmentDefinition mad |
starred.getNode() instanceof Starred and starred.getNode() instanceof Starred and
starred = mad.getDefiningNode() starred = mad.getDefiningNode()
| |

View File

@@ -9,7 +9,6 @@ overlay[local]
module; module;
private import python private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
import DataFlowPublic import DataFlowPublic
private import DataFlowPrivate private import DataFlowPrivate
private import semmle.python.internal.CachedStages private import semmle.python.internal.CachedStages
@@ -315,7 +314,7 @@ private module Cached {
*/ */
cached cached
predicate subscript(LocalSourceNode node, CfgNode subscript, CfgNode index) { predicate subscript(LocalSourceNode node, CfgNode subscript, CfgNode index) {
exists(CfgNode seq, Cfg::SubscriptNode subscriptNode | subscriptNode = subscript.getNode() | exists(CfgNode seq, SubscriptNode subscriptNode | subscriptNode = subscript.getNode() |
node.flowsTo(seq) and node.flowsTo(seq) and
seq.getNode() = subscriptNode.getObject() and seq.getNode() = subscriptNode.getObject() and
index.getNode() = subscriptNode.getIndex() index.getNode() = subscriptNode.getIndex()

View File

@@ -91,7 +91,9 @@ predicate matchAsFlowStep(Node nodeFrom, Node nodeTo) {
or or
// the interior pattern flows to the alias // the interior pattern flows to the alias
nodeFrom.(CfgNode).getNode().getNode() = subject.getPattern() and nodeFrom.(CfgNode).getNode().getNode() = subject.getPattern() and
nodeTo.(CfgNode).getNode().getNode() = alias exists(PatternAliasDefinition pad | pad.getDefiningNode().getNode() = alias |
nodeTo.(CfgNode).getNode() = pad.getDefiningNode()
)
) )
} }
@@ -122,9 +124,11 @@ predicate matchLiteralFlowStep(Node nodeFrom, Node nodeTo) {
* syntax (toplevel): `case var:` * syntax (toplevel): `case var:`
*/ */
predicate matchCaptureFlowStep(Node nodeFrom, Node nodeTo) { predicate matchCaptureFlowStep(Node nodeFrom, Node nodeTo) {
exists(MatchCapturePattern capture | exists(MatchCapturePattern capture, Name var | capture.getVariable() = var |
nodeFrom.(CfgNode).getNode().getNode() = capture and nodeFrom.(CfgNode).getNode().getNode() = capture and
nodeTo.(CfgNode).getNode().getNode() = capture.getVariable() exists(PatternCaptureDefinition pcd | pcd.getDefiningNode().getNode() = var |
nodeTo.(CfgNode).getNode() = pcd.getDefiningNode()
)
) )
} }

View File

@@ -1,548 +0,0 @@
/**
* Provides the Python SSA implementation built on the new (shared) CFG.
*
* Mirrors the Java SSA adapter at
* `java/ql/lib/semmle/code/java/dataflow/internal/SsaImpl.qll`:
* an `InputSig` is defined in terms of positional `(BasicBlock, int)`
* variable references, and the shared
* `codeql.ssa.Ssa::Make<Location, Cfg, Input>` module is then
* instantiated.
*
* `SourceVariable` is the AST-level `Py::Variable`. Variable references
* are looked up via the CFG facade's `NameNode.defines`/`uses`/`deletes`
* predicates, which themselves are one-line bridges to AST-level
* `Name.defines`/`uses`/`deletes`.
*
* Implicit-entry definitions are inserted for:
* - non-local / global / builtin variables that are read in the scope
* but never assigned (no enclosing CFG node defines them),
* - captured variables (variables defined in an enclosing scope that
* are read inside the scope), and
* - parameters, but only if the corresponding parameter name is *not*
* itself a CFG node. With the C#-style parameter wiring already
* installed in `AstNodeImpl.qll`, parameter names *are* CFG nodes,
* so the regular `variableWrite` path handles them — no `i = -1`
* entry is needed for ordinary parameters.
*/
overlay[local?]
module;
private import python as Py
private import semmle.python.controlflow.internal.AstNodeImpl as CfgImpl
private import semmle.python.controlflow.internal.Cfg as Cfg
private import codeql.ssa.Ssa as SsaImplCommon
private import codeql.controlflow.BasicBlock as BB
/**
* Adapts the Python `Cfg` facade to the shared SSA library's `CfgSig`.
* All members are inherited from `Cfg::ControlFlowNode` and
* `Cfg::BasicBlock`.
*/
private module CfgForSsa implements BB::CfgSig<Py::Location> {
class ControlFlowNode = CfgImpl::ControlFlowNode;
class BasicBlock = CfgImpl::BasicBlock;
class EntryBasicBlock = CfgImpl::Cfg::EntryBasicBlock;
predicate dominatingEdge = CfgImpl::Cfg::dominatingEdge/2;
}
/**
* A source variable for SSA, wrapping a Python AST `Variable`.
*
* We only track variables that are read at least once in their scope —
* tracking write-only variables would be unnecessary work — *except*
* for module-scope globals, where the "read" can be external (e.g.
* `import mymodule; mymodule.x`). Such globals are tracked
* unconditionally so that import-resolution can find their defining
* write.
*/
private newtype TSsaSourceVariable =
TPyVar(Py::Variable v) {
// Has a use somewhere — read-relevant for SSA.
exists(Cfg::NameNode n | n.uses(v))
or
// Or has a deletion (treated as a write that destroys the value).
exists(Cfg::NameNode n | n.deletes(v))
or
// Or is a module-scope global written in this module — must be
// tracked even if never read locally, because importers may read
// it as an attribute on the module object.
v.getScope() instanceof Py::Module and
exists(Cfg::NameNode n | n.defines(v))
or
// Or is a parameter — parameters must always have a
// `ParameterDefinition` for dataflow argument-routing to work,
// even if the parameter is never read in its scope. Mirrors
// legacy ESSA's `ParameterDefinition` (which fired for every
// parameter binding regardless of liveness).
exists(Py::Parameter p | p.asName() = v.getAStore())
}
/**
* A source variable for SSA, wrapping a Python AST `Variable`.
*/
class SsaSourceVariable extends TSsaSourceVariable {
/** Gets the underlying Python AST variable. */
Py::Variable getVariable() { this = TPyVar(result) }
/** Gets the (textual) name of this variable. */
string getName() { result = this.getVariable().getId() }
/** Gets a textual representation of this source variable. */
string toString() { result = this.getVariable().toString() }
/** Gets the location of this source variable. */
Py::Location getLocation() { result = this.getVariable().getScope().getLocation() }
/** Gets the scope in which this variable lives. */
Py::Scope getScope() { result = this.getVariable().getScope() }
/**
* Gets a use of this variable as it appears in the source — a `NameNode`
* that loads or deletes the variable. Mirrors legacy
* `SsaSourceVariable.getASourceUse()`.
*/
Cfg::ControlFlowNode getASourceUse() {
exists(Cfg::NameNode n | result = n |
n.uses(this.getVariable()) or n.deletes(this.getVariable())
)
}
/**
* Gets an implicit use of this variable. The new SSA does not have
* implicit-use refinements, but we keep this for API parity — every
* normal-exit of the variable's scope counts as a sink, ensuring
* variables stay live to scope exit for taint-tracking.
*/
Cfg::ControlFlowNode getAnImplicitUse() {
result.isNormalExit() and result.getScope() = this.getScope()
}
/**
* Gets a use of this variable — either an explicit source use or an
* implicit use at scope exit. Mirrors legacy `SsaSourceVariable.getAUse()`.
*/
Cfg::ControlFlowNode getAUse() {
result = this.getASourceUse() or result = this.getAnImplicitUse()
}
}
/**
* Holds if `v` is a non-local read in scope `s`, in the sense that `s`
* uses `v` but does not write it within `s`. This includes globals,
* builtins, and variables captured from an enclosing function scope.
*
* The `Py::Variable` `v` lives in some defining scope (the module for
* globals, an outer function for closures, etc.); the reading scope
* `s` is the scope where the use of `v` occurs.
*/
private predicate nonLocalReadIn(Py::Variable v, Py::Scope s) {
exists(Cfg::NameNode n |
n.uses(v) and
n.getScope() = s and
not exists(Cfg::NameNode def | def.defines(v) and def.getScope() = s)
) and
// Match legacy ESSA: only create entry defs for variables that have
// at least one defining store somewhere — otherwise the entry def
// represents "nothing reaches here", which is the default anyway and
// introduces no useful flow. (Legacy's `ModuleVariable` required a
// store; this is the closure-aware generalisation.)
exists(Cfg::NameNode store | store.defines(v))
}
/**
* Holds if `bb` is the entry basic block of a scope where `v` should
* have an implicit entry definition. This covers:
* - non-local / global / builtin variables read in `s`, and
* - captured variables (defined in an enclosing scope but read in `s`).
*
* Each reading scope gets its own entry def, so a closure variable can
* have multiple entry defs across all functions/methods that read it.
*
* Parameters are *not* included: their bound `Name` is itself a CFG
* node (per the C#-style parameter wiring), so `variableWrite` fires at
* the parameter's natural CFG index.
*/
private predicate hasEntryDefIn(SsaSourceVariable v, CfgImpl::BasicBlock bb) {
exists(Py::Scope s |
nonLocalReadIn(v.getVariable(), s) and
bb = entryBlock(s)
)
}
/**
* Gets the entry basic block of scope `s`, where implicit entry
* definitions are placed (at synthetic index `-1`).
*/
private CfgImpl::BasicBlock entryBlock(Py::Scope s) {
exists(CfgImpl::ControlFlowNode entry |
entry instanceof CfgImpl::ControlFlow::EntryNode and
entry.getEnclosingCallable().asScope() = s and
result = entry.getBasicBlock()
)
}
/**
* The SSA `InputSig` for Python. References are positional
* `(BasicBlock, int)` pairs into the new CFG.
*/
private module SsaImplInput implements SsaImplCommon::InputSig<Py::Location, CfgImpl::BasicBlock> {
class SourceVariable = SsaSourceVariable;
predicate variableWrite(CfgImpl::BasicBlock bb, int i, SourceVariable v, boolean certain) {
// Explicit binding at a CFG node — includes assignments,
// parameter Names (wired in via the C# pattern), exception-handler
// `as`-bindings, import aliases, and match-pattern captures.
exists(Cfg::NameNode n |
bb.getNode(i) = n and
n.defines(v.getVariable()) and
certain = true
)
or
// `del x` — removes the binding. Modelled as a certain write that
// makes any subsequent read invalid.
exists(Cfg::NameNode n |
bb.getNode(i) = n and
n.deletes(v.getVariable()) and
certain = true
)
or
// Implicit entry definition for non-local / captured / global /
// builtin variables read in some scope. Each reading scope's entry
// block gets one such write, allowing closures: e.g. when `x` is a
// parameter of an outer function and read inside a nested
// function, both scopes get entry defs for `x`.
hasEntryDefIn(v, bb) and
i = -1 and
certain = true
or
// `from X import *` — possibly rebinds every name in the importing
// scope. Modelled as an uncertain write at the import-star's CFG
// position for every variable that lives in (or is referenced
// from) the same scope as the import-star. Mirrors legacy ESSA's
// `ImportStarRefinement` (see `essa/SsaDefinitions.qll`'s
// `import_star_refinement` predicate). The write is uncertain so
// that prior definitions of the variable remain available — the
// shared-SSA `SsaUncertainWrite` merges the new value with the
// immediately preceding definition.
exists(Cfg::ImportStarNode imp |
imp.injects(_) and
bb.getNode(i) = imp and
certain = false and
(
v.getVariable().getScope() = imp.getScope()
or
// Variable is defined in some other scope but referenced in
// the same scope as the import-star (matches legacy clause 2:
// `other.uses(v) and def.getScope() = other.getScope()`).
exists(Cfg::NameNode other |
other.uses(v.getVariable()) and
imp.getScope() = other.getScope()
)
)
)
}
predicate variableRead(CfgImpl::BasicBlock bb, int i, SourceVariable v, boolean certain) {
// Explicit source use — a `Name` load or a `del x` of the variable.
exists(Cfg::NameNode n |
bb.getNode(i) = n and
n.uses(v.getVariable()) and
certain = true
)
or
// Synthetic use at the normal exit of the variable's defining scope.
// This keeps every variable live to scope exit so that callers (e.g.
// `module_export` in ImportResolution.qll, or taint-tracking pass-through
// through unread locals) can ask "which definition reaches end of
// scope?". Mirrors legacy ESSA's `SsaSourceVariable.getAUse()` which
// included `getScope().getANormalExit()`.
exists(Cfg::ControlFlowNode exit |
exit.isNormalExit() and
exit.getScope() = v.getVariable().getScope() and
bb.getNode(i) = exit and
certain = true
)
}
}
/**
* The shared SSA instantiation for Python.
*
* Members:
* - `Definition` — the union of explicit, uncertain, and phi definitions
* - `WriteDefinition`, `UncertainWriteDefinition`, `PhiNode`
* - the standard SSA predicates (`getAUse`, `getAnUltimateDefinition`, ...).
*/
module Ssa = SsaImplCommon::Make<Py::Location, CfgForSsa, SsaImplInput>;
final class Definition = Ssa::Definition;
final class WriteDefinition = Ssa::WriteDefinition;
final class UncertainWriteDefinition = Ssa::UncertainWriteDefinition;
final class PhiNode = Ssa::PhiNode;
// ===========================================================================
// ESSA-shaped adapter layer
//
// The dataflow library (`python/ql/lib/semmle/python/dataflow/new/`) and
// related modules (`ApiGraphs.qll`, etc.) consume the legacy ESSA API
// (`EssaVariable`, `EssaDefinition`, `AssignmentDefinition`,
// `ScopeEntryDefinition`, `ParameterDefinition`, `WithDefinition`,
// `PhiFunction`, plus the `AdjacentUses` module). To migrate them off
// the legacy CFG, we expose the same API surface on top of the
// shared SSA built above.
//
// This adapter is intentionally narrow: it covers only the predicates
// that new dataflow consumes. The richer legacy ESSA — refinement
// nodes, attribute refinements, edge refinements — stays available
// via `semmle.python.essa.Essa` for points-to / legacy code.
// ===========================================================================
/**
* Gets the CFG node at which a write definition's binding takes place.
*
* For ordinary writes (assignment, deletion, parameter) this is the
* canonical CFG node of the bound Name. For implicit entry definitions
* (synthesised at position `-1` of a scope's entry BB) this is the
* scope's entry node.
*/
private Cfg::ControlFlowNode writeDefNode(Ssa::WriteDefinition def) {
exists(CfgImpl::BasicBlock bb, int i | def.definesAt(_, bb, i) |
i >= 0 and result = bb.getNode(i)
or
i = -1 and result = bb.getNode(0)
)
}
/**
* A write definition whose binding has a corresponding CFG node — i.e.
* everything that's not a phi node. Mirrors legacy ESSA's
* `EssaNodeDefinition`.
*/
class EssaNodeDefinition extends Ssa::WriteDefinition {
/** Gets the CFG node where this definition's binding takes place. */
Cfg::ControlFlowNode getDefiningNode() { result = writeDefNode(this) }
/** Gets the variable defined here (legacy name). */
SsaSourceVariable getVariable() { result = this.getSourceVariable() }
/** Gets the enclosing scope. */
Py::Scope getScope() {
exists(Cfg::ControlFlowNode n | n = this.getDefiningNode() | result = n.getScope())
}
/**
* Holds if this definition defines source variable `v` at CFG node
* `defNode`. Flatter form of `getSourceVariable()` +
* `getDefiningNode()`, matching legacy ESSA's `definedBy`.
*/
predicate definedBy(SsaSourceVariable v, Cfg::ControlFlowNode defNode) {
v = this.getSourceVariable() and defNode = this.getDefiningNode()
}
}
/**
* An assignment definition: any binding where the value being assigned
* is statically known via `Cfg::DefinitionNode.getValue()`. Includes
* plain assignments, walrus, annotated assignments, augmented
* assignments, import aliases (`import x` / `from m import x [as y]`),
* `with ... as x`, and for-target bindings (where `getValue()` returns
* the iter expression's CFG node). Excludes parameter bindings —
* those are modelled by `ParameterDefinition`.
*/
class AssignmentDefinition extends EssaNodeDefinition {
AssignmentDefinition() {
exists(Cfg::NameNode n | n = this.getDefiningNode() |
exists(n.(Cfg::DefinitionNode).getValue()) and
not n.(Cfg::ControlFlowNode).isParameter()
)
}
/** Gets the CFG node for the value being assigned, if statically known. */
Cfg::ControlFlowNode getValue() {
result = this.getDefiningNode().(Cfg::DefinitionNode).getValue()
}
}
/**
* A parameter definition — the binding of a parameter name in a
* function's scope.
*/
class ParameterDefinition extends EssaNodeDefinition {
ParameterDefinition() { this.getDefiningNode().isParameter() }
/** Gets the AST `Parameter` (a `Py::Name` in param context). */
Py::Name getParameter() { result = this.getDefiningNode().getNode() }
}
/**
* A definition introduced by a `with ... as x:` clause.
*/
class WithDefinition extends EssaNodeDefinition {
WithDefinition() {
exists(Cfg::NameNode n, Py::With w |
n = this.getDefiningNode() and
w.getOptionalVars() = n.getNode()
)
}
}
/**
* An assignment where the LHS is a tuple/list and the RHS is unpacked:
* `a, b = (1, 2)` or `a, *rest = xs`. The SSA def lives at the inner
* `Name` CFG node, but for IterableUnpacking integration we expose
* the enclosing `StarredNode` as the `getDefiningNode()` for `*rest`
* patterns — mirroring legacy ESSA's `multi_assignment_definition`,
* which placed the def at the StarredNode CFG node.
*/
class MultiAssignmentDefinition extends EssaNodeDefinition {
MultiAssignmentDefinition() {
exists(Cfg::NameNode n | n = super.getDefiningNode() |
exists(Py::Assign a, Py::Expr lhs |
a.getATarget() = lhs and
(lhs instanceof Py::Tuple or lhs instanceof Py::List) and
lhs.getASubExpression+() = n.getNode()
)
or
// For-loop with tuple/list target: `for a, b in xs:` —
// tuple-unpacking semantics applies to the for-target.
exists(Py::For f, Py::Expr lhs |
f.getTarget() = lhs and
(lhs instanceof Py::Tuple or lhs instanceof Py::List) and
lhs.getASubExpression+() = n.getNode()
)
)
}
override Cfg::ControlFlowNode getDefiningNode() {
// Default: the underlying `Name` CFG node (where the SSA def lives).
not exists(Cfg::StarredNode s |
s.getNode().(Py::Starred).getValue() = super.getDefiningNode().getNode()
) and
result = super.getDefiningNode()
or
// Exception: for `*rest`, expose the enclosing `Starred` CFG node
// so that `IterableUnpacking::iterableUnpackingStarredElementStoreStep`
// can attach the rest-list to it.
exists(Cfg::StarredNode s |
s.getNode().(Py::Starred).getValue() = super.getDefiningNode().getNode()
|
result = s
)
}
}
/**
* An implicit entry definition for a non-local / captured / global /
* builtin variable read in a scope but not defined there.
*
* Inherits from `EssaNodeDefinition` and exposes the scope's entry node
* as its defining node (matching legacy ESSA semantics).
*/
class ScopeEntryDefinition extends EssaNodeDefinition {
ScopeEntryDefinition() {
exists(CfgImpl::BasicBlock bb |
this.definesAt(_, bb, -1) and
bb instanceof CfgImpl::Cfg::EntryBasicBlock
)
}
/** Gets the enclosing scope (the scope whose entry block this def is in). */
override Py::Scope getScope() {
exists(CfgImpl::BasicBlock bb |
this.definesAt(_, bb, -1) and
result = bb.getNode(0).(Cfg::ControlFlowNode).getScope()
)
}
}
/** A phi node (alias matching legacy naming). */
class PhiFunction extends PhiNode {
/**
* Gets an input to this phi function (a definition that flows into
* the phi from one of its predecessor blocks). Mirrors legacy
* ESSA's `PhiFunction.getAnInput()`.
*/
Ssa::Definition getAnInput() { Ssa::phiHasInputFromBlock(this, result, _) }
}
/** Base class for all ESSA definitions (legacy-shaped). */
class EssaDefinition = Ssa::Definition;
/**
* An adapter representing a single SSA-defined "variable" — wrapping
* one `Ssa::Definition`. Mirrors legacy `EssaVariable` API.
*/
class EssaVariable extends Ssa::Definition {
/** Gets the underlying SSA definition (legacy name). */
Ssa::Definition getDefinition() { result = this }
/**
* Gets a CFG node where this definition is used. Includes regular
* `Name` reads as well as the synthetic scope-exit "use" registered
* via `SsaImplInput::variableRead` — mirrors legacy ESSA's
* `EssaVariable.getAUse()` which inherited the synthetic exit-use
* from `SsaSourceVariable`.
*/
Cfg::ControlFlowNode getAUse() {
exists(CfgImpl::BasicBlock bb, int i |
Ssa::ssaDefReachesRead(this.getSourceVariable(), this, bb, i) and
bb.getNode(i) = result
)
}
/** Gets the (textual) name of the underlying variable. */
string getName() { result = this.getSourceVariable().getVariable().getId() }
/** Gets the scope in which this variable lives. */
Py::Scope getScope() { result = this.getSourceVariable().getVariable().getScope() }
/** Gets an ultimate non-phi ancestor of this definition. */
EssaVariable getAnUltimateDefinition() {
if this instanceof PhiNode
then
exists(Ssa::Definition input |
Ssa::phiHasInputFromBlock(this, input, _) and
result = input.(EssaVariable).getAnUltimateDefinition()
)
else result = this
}
}
/**
* Adjacent use-use and def-use relations exposed by the shared SSA
* library. Provides the same interface as legacy
* `semmle.python.essa.SsaCompute::AdjacentUses`.
*/
module AdjacentUses {
/** Holds if `nodeFrom` and `nodeTo` are adjacent uses of the same SSA variable. */
predicate adjacentUseUse(Cfg::NameNode nodeFrom, Cfg::NameNode nodeTo) {
exists(SsaSourceVariable v, CfgImpl::BasicBlock bb1, int i1, CfgImpl::BasicBlock bb2, int i2 |
Ssa::adjacentUseUse(bb1, i1, bb2, i2, v, _) and
nodeFrom = bb1.getNode(i1) and
nodeTo = bb2.getNode(i2)
)
}
/** Holds if `use` is a first use of definition `def`. */
predicate firstUse(Ssa::Definition def, Cfg::NameNode use) {
exists(CfgImpl::BasicBlock bb, int i |
Ssa::firstUse(def, bb, i, _) and
use = bb.getNode(i)
)
}
/**
* Holds if `use` is any reachable use of definition `def`. Combines
* `firstUse` with transitive use-use adjacency.
*/
predicate useOfDef(Ssa::Definition def, Cfg::NameNode use) {
firstUse(def, use)
or
exists(Cfg::NameNode mid | useOfDef(def, mid) and adjacentUseUse(mid, use))
}
}

View File

@@ -1,6 +1,4 @@
private import python private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.dataflow.new.internal.SsaImpl as SsaImpl
private import semmle.python.dataflow.new.DataFlow private import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.internal.DataFlowPrivate as DataFlowPrivate private import semmle.python.dataflow.new.internal.DataFlowPrivate as DataFlowPrivate
private import FlowSummaryImpl as FlowSummaryImpl private import FlowSummaryImpl as FlowSummaryImpl
@@ -99,7 +97,7 @@ import Cached
* and isn't a big problem in practice. * and isn't a big problem in practice.
*/ */
predicate concatStep(DataFlow::CfgNode nodeFrom, DataFlow::CfgNode nodeTo) { predicate concatStep(DataFlow::CfgNode nodeFrom, DataFlow::CfgNode nodeTo) {
exists(Cfg::BinaryExprNode add | add = nodeTo.getNode() | exists(BinaryExprNode add | add = nodeTo.getNode() |
add.getOp() instanceof Add and add.getAnOperand() = nodeFrom.getNode() add.getOp() instanceof Add and add.getAnOperand() = nodeFrom.getNode()
) )
} }
@@ -108,7 +106,7 @@ predicate concatStep(DataFlow::CfgNode nodeFrom, DataFlow::CfgNode nodeTo) {
* Holds if taint can flow from `nodeFrom` to `nodeTo` with a step related to subscripting. * Holds if taint can flow from `nodeFrom` to `nodeTo` with a step related to subscripting.
*/ */
predicate subscriptStep(DataFlow::CfgNode nodeFrom, DataFlow::CfgNode nodeTo) { predicate subscriptStep(DataFlow::CfgNode nodeFrom, DataFlow::CfgNode nodeTo) {
nodeTo.getNode().(Cfg::SubscriptNode).getObject() = nodeFrom.getNode() nodeTo.getNode().(SubscriptNode).getObject() = nodeFrom.getNode()
} }
/** /**
@@ -124,15 +122,15 @@ predicate stringManipulation(DataFlow::CfgNode nodeFrom, DataFlow::CfgNode nodeT
( (
call = API::builtin(["str", "bytes", "unicode"]).getACall() call = API::builtin(["str", "bytes", "unicode"]).getACall()
or or
call.getFunction().asCfgNode().(Cfg::NameNode).getId() in ["str", "bytes", "unicode"] call.getFunction().asCfgNode().(NameNode).getId() in ["str", "bytes", "unicode"]
) and ) and
nodeFrom in [call.getArg(0), call.getArgByName("object")] nodeFrom in [call.getArg(0), call.getArgByName("object")]
) )
or or
// String methods. Note that this doesn't recognize `meth = "foo".upper; meth()` // String methods. Note that this doesn't recognize `meth = "foo".upper; meth()`
exists(Cfg::CallNode call, string method_name, Cfg::ControlFlowNode object | exists(CallNode call, string method_name, ControlFlowNode object |
call = nodeTo.getNode() and call = nodeTo.getNode() and
object = call.getFunction().(Cfg::AttrNode).getObject(method_name) object = call.getFunction().(AttrNode).getObject(method_name)
| |
nodeFrom.getNode() = object and nodeFrom.getNode() = object and
method_name in [ method_name in [
@@ -158,7 +156,7 @@ predicate stringManipulation(DataFlow::CfgNode nodeFrom, DataFlow::CfgNode nodeT
) )
or or
// % formatting // % formatting
exists(Cfg::BinaryExprNode fmt | fmt = nodeTo.getNode() | exists(BinaryExprNode fmt | fmt = nodeTo.getNode() |
fmt.getOp() instanceof Mod and fmt.getOp() instanceof Mod and
( (
fmt.getLeft() = nodeFrom.getNode() fmt.getLeft() = nodeFrom.getNode()
@@ -168,7 +166,7 @@ predicate stringManipulation(DataFlow::CfgNode nodeFrom, DataFlow::CfgNode nodeT
) )
or or
// string multiplication -- `"foo" * 10` // string multiplication -- `"foo" * 10`
exists(Cfg::BinaryExprNode mult | mult = nodeTo.getNode() | exists(BinaryExprNode mult | mult = nodeTo.getNode() |
mult.getOp() instanceof Mult and mult.getOp() instanceof Mult and
mult.getLeft() = nodeFrom.getNode() mult.getLeft() = nodeFrom.getNode()
) )
@@ -215,8 +213,8 @@ predicate awaitStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
* the variable `f` is tainted if the result of `open("foo")` is tainted. * the variable `f` is tainted if the result of `open("foo")` is tainted.
*/ */
predicate asyncWithStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { predicate asyncWithStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
exists(With with, Cfg::ControlFlowNode contextManager, Cfg::ControlFlowNode var | exists(With with, ControlFlowNode contextManager, ControlFlowNode var |
var = any(SsaImpl::WithDefinition wd).getDefiningNode() var = any(WithDefinition wd).getDefiningNode()
| |
nodeFrom.(DataFlow::CfgNode).getNode() = contextManager and nodeFrom.(DataFlow::CfgNode).getNode() = contextManager and
nodeTo.(DataFlow::CfgNode).getNode() = var and nodeTo.(DataFlow::CfgNode).getNode() = var and

View File

@@ -2,8 +2,6 @@ import codeql.util.Unit
import codeql.typetracking.TypeTracking as Shared import codeql.typetracking.TypeTracking as Shared
import codeql.typetracking.internal.TypeTrackingImpl as SharedImpl import codeql.typetracking.internal.TypeTrackingImpl as SharedImpl
private import python private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.dataflow.new.internal.SsaImpl as SsaImpl
private import semmle.python.internal.CachedStages private import semmle.python.internal.CachedStages
private import semmle.python.dataflow.new.internal.DataFlowPublic as DataFlowPublic private import semmle.python.dataflow.new.internal.DataFlowPublic as DataFlowPublic
private import semmle.python.dataflow.new.internal.DataFlowPrivate as DataFlowPrivate private import semmle.python.dataflow.new.internal.DataFlowPrivate as DataFlowPrivate
@@ -164,7 +162,7 @@ module TypeTrackingInput implements Shared::TypeTrackingInput<Location> {
// ignore the flow steps from the synthetic sequence node to the real sequence node, // ignore the flow steps from the synthetic sequence node to the real sequence node,
// since we only support one level of content in type-trackers, and the nested // since we only support one level of content in type-trackers, and the nested
// structure requires two levels at least to be useful. // structure requires two levels at least to be useful.
not exists(Cfg::SequenceNode outer | not exists(SequenceNode outer |
outer.getAnElement() = nodeTo.asCfgNode() and outer.getAnElement() = nodeTo.asCfgNode() and
IterableUnpacking::iterableUnpackingTupleFlowStep(nodeFrom, nodeTo) IterableUnpacking::iterableUnpackingTupleFlowStep(nodeFrom, nodeTo)
) )
@@ -269,7 +267,7 @@ module TypeTrackingInput implements Shared::TypeTrackingInput<Location> {
// Since we only support one level of content in type-trackers we don't actually // Since we only support one level of content in type-trackers we don't actually
// support `(aa, ab), (ba, bb) = ...`. Therefore we exclude the read-step from `(aa, // support `(aa, ab), (ba, bb) = ...`. Therefore we exclude the read-step from `(aa,
// ab)` to `aa` (since it is not needed). // ab)` to `aa` (since it is not needed).
not exists(Cfg::SequenceNode outer | not exists(SequenceNode outer |
outer.getAnElement() = nodeFrom.asCfgNode() and outer.getAnElement() = nodeFrom.asCfgNode() and
IterableUnpacking::iterableUnpackingTupleFlowStep(_, nodeFrom) IterableUnpacking::iterableUnpackingTupleFlowStep(_, nodeFrom)
) and ) and
@@ -279,7 +277,7 @@ module TypeTrackingInput implements Shared::TypeTrackingInput<Location> {
IterableUnpacking::iterableUnpackingForReadStep(_, _, seq) and IterableUnpacking::iterableUnpackingForReadStep(_, _, seq) and
IterableUnpacking::iterableUnpackingConvertingReadStep(seq, _, elem) and IterableUnpacking::iterableUnpackingConvertingReadStep(seq, _, elem) and
IterableUnpacking::iterableUnpackingConvertingStoreStep(elem, _, nodeFrom) and IterableUnpacking::iterableUnpackingConvertingStoreStep(elem, _, nodeFrom) and
nodeFrom.asCfgNode() instanceof Cfg::SequenceNode nodeFrom.asCfgNode() instanceof SequenceNode
) )
or or
TypeTrackerSummaryFlow::basicLoadStep(nodeFrom, nodeTo, DataFlowPublic::singleton(content)) TypeTrackerSummaryFlow::basicLoadStep(nodeFrom, nodeTo, DataFlowPublic::singleton(content))
@@ -317,15 +315,13 @@ module TypeTrackingInput implements Shared::TypeTrackingInput<Location> {
// //
// nodeFrom is `expr` // nodeFrom is `expr`
// nodeTo is entry node for `f` // nodeTo is entry node for `f`
exists( exists(ScopeEntryDefinition e, SsaSourceVariable var, DefinitionNode def |
SsaImpl::ScopeEntryDefinition e, SsaImpl::SsaSourceVariable var, Cfg::DefinitionNode def
|
e.getSourceVariable() = var and e.getSourceVariable() = var and
def.getNode() = var.getVariable().getAStore() var.hasDefiningNode(def)
| |
nodeTo.(DataFlowPublic::ScopeEntryDefinitionNode).getDefinition() = e and nodeTo.(DataFlowPublic::ScopeEntryDefinitionNode).getDefinition() = e and
nodeFrom.asCfgNode() = def and nodeFrom.asCfgNode() = def and
var.getVariable().getScope().getScope*() = nodeFrom.getScope() var.getScope().getScope*() = nodeFrom.getScope()
) )
} }
@@ -353,11 +349,23 @@ module TypeTrackingInput implements Shared::TypeTrackingInput<Location> {
* `instance.attr`, where `instance` is a reference to an instance of `cls`). * `instance.attr`, where `instance` is a reference to an instance of `cls`).
* *
* This complements `selfAttrRef`, which only handles `self.attr` accesses inside the * This complements `selfAttrRef`, which only handles `self.attr` accesses inside the
* methods of `cls`. Unlike `selfAttrRef`, this depends on the call graph (via * methods of `cls`. The instance is identified using *local* flow from a constructor
* `classInstanceTracker`), so steps using it must be reported as `levelStepCall`. * call `cls(...)` (resolved via the call graph by `resolveClassCall`), rather than a
* dedicated instance type-tracker (`classInstanceTracker`).
*
* Using `classInstanceTracker` here would make `levelStepCall` mutually recursive with
* `classInstanceTracker` -- itself a full type-tracker run -- which caused catastrophic
* query slowdowns on some OOP-heavy Python code bases (e.g. `mypy` and `dask`). Relying
* on local flow from a resolved constructor call instead depends only on `classTracker`
* (the same call-graph machinery already used by `inheritedFieldStep`), avoiding that
* blow-up. The trade-off is reduced precision: instances that flow across a call or
* return before being read are no longer covered by this step.
*/ */
private predicate instanceAttrRead(Class cls, string attr, DataFlowPublic::AttrRead read) { private predicate instanceAttrRead(Class cls, string attr, DataFlowPublic::AttrRead read) {
read.getObject() = DataFlowDispatch::classInstanceTracker(cls) and exists(DataFlowPublic::CallCfgNode construction |
DataFlowDispatch::resolveClassCall(construction.asCfgNode(), cls) and
read.getObject().getALocalSource() = construction
) and
read.mayHaveAttributeName(attr) read.mayHaveAttributeName(attr)
} }
@@ -436,9 +444,9 @@ module TypeTrackingInput implements Shared::TypeTrackingInput<Location> {
* This is the cross-instance counterpart of `localFieldStep`: it relates a write of * This is the cross-instance counterpart of `localFieldStep`: it relates a write of
* `self.attr` inside a class to a read of `attr` on a reference to an instance of that * `self.attr` inside a class to a read of `attr` on a reference to an instance of that
* class or one of its subclasses. Identifying instances relies on the call graph (via * class or one of its subclasses. Identifying instances relies on the call graph (via
* `classInstanceTracker`), so this step is reported as `levelStepCall` rather than * `resolveClassCall`, see `instanceAttrRead`), so this step is reported as
* `levelStepNoCall`. The write may occur in the instance's own class or in any of its * `levelStepCall` rather than `levelStepNoCall`. The write may occur in the instance's
* superclasses, since those methods are inherited. * own class or in any of its superclasses, since those methods are inherited.
* *
* Like `localFieldStep`, this is an over-approximation: it is both instance-insensitive * Like `localFieldStep`, this is an over-approximation: it is both instance-insensitive
* and order-insensitive. * and order-insensitive.

View File

@@ -3,9 +3,6 @@ overlay[local]
module; module;
private import python private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.controlflow.internal.AstNodeImpl as CfgImpl
private import semmle.python.dataflow.new.internal.SsaImpl as SsaImpl
private import DataFlowPublic private import DataFlowPublic
private import semmle.python.dataflow.new.internal.DataFlowPrivate private import semmle.python.dataflow.new.internal.DataFlowPrivate
private import codeql.dataflow.VariableCapture as Shared private import codeql.dataflow.VariableCapture as Shared
@@ -17,10 +14,10 @@ private import codeql.dataflow.VariableCapture as Shared
// The first is the main implementation, the second is a performance motivated restriction. // The first is the main implementation, the second is a performance motivated restriction.
// The restriction is to clear any `CapturedVariableContent` before writing a new one // The restriction is to clear any `CapturedVariableContent` before writing a new one
// to avoid long access paths (see the link for a nice explanation). // to avoid long access paths (see the link for a nice explanation).
private module CaptureInput implements Shared::InputSig<Location, CfgImpl::BasicBlock> { private module CaptureInput implements Shared::InputSig<Location, Cfg::BasicBlock> {
private import python as PY private import python as PY
additional class ExprCfgNode extends Cfg::ControlFlowNode { additional class ExprCfgNode extends ControlFlowNode {
ExprCfgNode() { isExpressionNode(this) } ExprCfgNode() { isExpressionNode(this) }
} }
@@ -28,9 +25,7 @@ private module CaptureInput implements Shared::InputSig<Location, CfgImpl::Basic
predicate isConstructor() { none() } predicate isConstructor() { none() }
} }
Callable basicBlockGetEnclosingCallable(CfgImpl::BasicBlock bb) { Callable basicBlockGetEnclosingCallable(Cfg::BasicBlock bb) { result = bb.getScope() }
result = bb.getEnclosingCallable().asScope()
}
class CapturedVariable extends LocalVariable { class CapturedVariable extends LocalVariable {
Function f; Function f;
@@ -56,23 +51,21 @@ private module CaptureInput implements Shared::InputSig<Location, CfgImpl::Basic
class CapturedParameter extends CapturedVariable { class CapturedParameter extends CapturedVariable {
CapturedParameter() { this.isParameter() } CapturedParameter() { this.isParameter() }
Cfg::ControlFlowNode getCfgNode() { result.getNode().(Parameter) = this.getAnAccess() } ControlFlowNode getCfgNode() { result.getNode().(Parameter) = this.getAnAccess() }
} }
class Expr extends ExprCfgNode { class Expr extends ExprCfgNode {
predicate hasCfgNode(CfgImpl::BasicBlock bb, int i) { this = bb.getNode(i) } predicate hasCfgNode(Cfg::BasicBlock bb, int i) { this = bb.getNode(i) }
} }
class VariableWrite extends Cfg::ControlFlowNode { class VariableWrite extends ControlFlowNode {
CapturedVariable v; CapturedVariable v;
VariableWrite() { VariableWrite() { exists(DefinitionNode d | d.getNode() = v.getAStore() | this = d.getValue()) }
exists(Cfg::DefinitionNode d | d.getNode() = v.getAStore() | this = d.getValue())
}
CapturedVariable getVariable() { result = v } CapturedVariable getVariable() { result = v }
predicate hasCfgNode(CfgImpl::BasicBlock bb, int i) { this = bb.getNode(i) } predicate hasCfgNode(Cfg::BasicBlock bb, int i) { this = bb.getNode(i) }
} }
class VariableRead extends Expr { class VariableRead extends Expr {
@@ -87,14 +80,9 @@ private module CaptureInput implements Shared::InputSig<Location, CfgImpl::Basic
// TODO: Other languages have an extra case here looking like // TODO: Other languages have an extra case here looking like
// simpleAstFlowStep(nodeFrom, nodeTo) // simpleAstFlowStep(nodeFrom, nodeTo)
// we should investigate the potential benefit of adding that. // we should investigate the potential benefit of adding that.
exists(SsaImpl::EssaVariable def | exists(SsaVariable def |
def.getAUse() = nodeTo and def.getAUse() = nodeTo and
def.getAnUltimateDefinition() def.getAnUltimateDefinition().getDefinition().(DefinitionNode).getValue() = nodeFrom
.getDefinition()
.(SsaImpl::EssaNodeDefinition)
.getDefiningNode()
.(Cfg::DefinitionNode)
.getValue() = nodeFrom
) )
} }
@@ -119,7 +107,7 @@ class CapturedVariable = CaptureInput::CapturedVariable;
class ClosureExpr = CaptureInput::ClosureExpr; class ClosureExpr = CaptureInput::ClosureExpr;
module Flow = Shared::Flow<Location, Cfg::CfgForBb, CaptureInput>; module Flow = Shared::Flow<Location, Cfg, CaptureInput>;
private Flow::ClosureNode asClosureNode(Node n) { private Flow::ClosureNode asClosureNode(Node n) {
result = n.(SynthCaptureNode).getSynthesizedCaptureNode() result = n.(SynthCaptureNode).getSynthesizedCaptureNode()

View File

@@ -4,7 +4,6 @@
*/ */
private import python private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.Concepts private import semmle.python.Concepts
private import semmle.python.ApiGraphs private import semmle.python.ApiGraphs
private import semmle.python.dataflow.new.RemoteFlowSources private import semmle.python.dataflow.new.RemoteFlowSources
@@ -158,9 +157,9 @@ module Bottle {
DataFlow::Node value; DataFlow::Node value;
HeaderWriteSubscript() { HeaderWriteSubscript() {
exists(Cfg::SubscriptNode subscript | exists(SubscriptNode subscript |
this.asCfgNode() = subscript and this.asCfgNode() = subscript and
value.asCfgNode() = subscript.(Cfg::DefinitionNode).getValue() and value.asCfgNode() = subscript.(DefinitionNode).getValue() and
name.asCfgNode() = subscript.getIndex() and name.asCfgNode() = subscript.getIndex() and
subscript.getObject() = headers().asSource().asCfgNode() subscript.getObject() = headers().asSource().asCfgNode()
) )

View File

@@ -4,7 +4,6 @@
*/ */
private import python private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.dataflow.new.DataFlow private import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.RemoteFlowSources private import semmle.python.dataflow.new.RemoteFlowSources
private import semmle.python.dataflow.new.TaintTracking private import semmle.python.dataflow.new.TaintTracking
@@ -1306,7 +1305,7 @@ module PrivateDjango {
dict.(DataFlow::MethodCallNode).calls(files, "dict") dict.(DataFlow::MethodCallNode).calls(files, "dict")
) )
| |
this.asCfgNode().(Cfg::SubscriptNode).getObject() = dict.asCfgNode() this.asCfgNode().(SubscriptNode).getObject() = dict.asCfgNode()
or or
this.(DataFlow::MethodCallNode).calls(dict, "get") this.(DataFlow::MethodCallNode).calls(dict, "get")
) )
@@ -1315,7 +1314,7 @@ module PrivateDjango {
exists(DataFlow::AttrRead files, DataFlow::MethodCallNode getlistCall | exists(DataFlow::AttrRead files, DataFlow::MethodCallNode getlistCall |
files.accesses(instance(), "FILES") and files.accesses(instance(), "FILES") and
getlistCall.calls(files, "getlist") and getlistCall.calls(files, "getlist") and
this.asCfgNode().(Cfg::SubscriptNode).getObject() = getlistCall.asCfgNode() this.asCfgNode().(SubscriptNode).getObject() = getlistCall.asCfgNode()
) )
} }
} }
@@ -2217,7 +2216,7 @@ module PrivateDjango {
DataFlow::Node value; DataFlow::Node value;
DjangoResponseCookieSubscriptWrite() { DjangoResponseCookieSubscriptWrite() {
exists(Cfg::SubscriptNode subscript, DataFlow::AttrRead cookieLookup | exists(SubscriptNode subscript, DataFlow::AttrRead cookieLookup |
// To give `this` a value, we need to choose between either LHS or RHS, // To give `this` a value, we need to choose between either LHS or RHS,
// and just go with the LHS // and just go with the LHS
this.asCfgNode() = subscript this.asCfgNode() = subscript
@@ -2229,7 +2228,7 @@ module PrivateDjango {
| |
cookieLookup.flowsTo(subscriptObj) cookieLookup.flowsTo(subscriptObj)
) and ) and
value.asCfgNode() = subscript.(Cfg::DefinitionNode).getValue() and value.asCfgNode() = subscript.(DefinitionNode).getValue() and
index.asCfgNode() = subscript.getIndex() index.asCfgNode() = subscript.getIndex()
) )
} }
@@ -2250,7 +2249,7 @@ module PrivateDjango {
DataFlow::Node value; DataFlow::Node value;
DjangoResponseHeaderSubscriptWrite() { DjangoResponseHeaderSubscriptWrite() {
exists(Cfg::SubscriptNode subscript, DataFlow::AttrRead headerLookup | exists(SubscriptNode subscript, DataFlow::AttrRead headerLookup |
// To give `this` a value, we need to choose between either LHS or RHS, // To give `this` a value, we need to choose between either LHS or RHS,
// and just go with the LHS // and just go with the LHS
this.asCfgNode() = subscript this.asCfgNode() = subscript
@@ -2262,7 +2261,7 @@ module PrivateDjango {
| |
headerLookup.flowsTo(subscriptObj) headerLookup.flowsTo(subscriptObj)
) and ) and
value.asCfgNode() = subscript.(Cfg::DefinitionNode).getValue() and value.asCfgNode() = subscript.(DefinitionNode).getValue() and
index.asCfgNode() = subscript.getIndex() index.asCfgNode() = subscript.getIndex()
) )
} }
@@ -2285,14 +2284,14 @@ module PrivateDjango {
DataFlow::Node value; DataFlow::Node value;
DjangoResponseSubscriptWrite() { DjangoResponseSubscriptWrite() {
exists(Cfg::SubscriptNode subscript | exists(SubscriptNode subscript |
// To give `this` a value, we need to choose between either LHS or RHS, // To give `this` a value, we need to choose between either LHS or RHS,
// and just go with the LHS // and just go with the LHS
this.asCfgNode() = subscript this.asCfgNode() = subscript
| |
subscript.getObject() = subscript.getObject() =
DjangoImpl::DjangoHttp::Response::HttpResponse::instance().asCfgNode() and DjangoImpl::DjangoHttp::Response::HttpResponse::instance().asCfgNode() and
value.asCfgNode() = subscript.(Cfg::DefinitionNode).getValue() and value.asCfgNode() = subscript.(DefinitionNode).getValue() and
index.asCfgNode() = subscript.getIndex() index.asCfgNode() = subscript.getIndex()
) )
} }
@@ -2427,7 +2426,7 @@ module PrivateDjango {
/** Gets a reference to the result of calling the `as_view` classmethod of this class. */ /** Gets a reference to the result of calling the `as_view` classmethod of this class. */
private DataFlow::TypeTrackingNode asViewResult(DataFlow::TypeTracker t) { private DataFlow::TypeTrackingNode asViewResult(DataFlow::TypeTracker t) {
t.start() and t.start() and
result.asCfgNode().(Cfg::CallNode).getFunction() = this.asViewRef().asCfgNode() result.asCfgNode().(CallNode).getFunction() = this.asViewRef().asCfgNode()
or or
exists(DataFlow::TypeTracker t2 | result = this.asViewResult(t2).track(t2, t)) exists(DataFlow::TypeTracker t2 | result = this.asViewResult(t2).track(t2, t))
} }

View File

@@ -4,7 +4,6 @@
*/ */
private import python private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.dataflow.new.DataFlow private import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.RemoteFlowSources private import semmle.python.dataflow.new.RemoteFlowSources
private import semmle.python.dataflow.new.TaintTracking private import semmle.python.dataflow.new.TaintTracking
@@ -442,7 +441,7 @@ module FastApi {
DataFlow::Node value; DataFlow::Node value;
HeaderSubscriptWrite() { HeaderSubscriptWrite() {
exists(Cfg::SubscriptNode subscript, DataFlow::AttrRead headerLookup | exists(SubscriptNode subscript, DataFlow::AttrRead headerLookup |
// To give `this` a value, we need to choose between either LHS or RHS, // To give `this` a value, we need to choose between either LHS or RHS,
// and just go with the LHS // and just go with the LHS
this.asCfgNode() = subscript this.asCfgNode() = subscript
@@ -451,7 +450,7 @@ module FastApi {
exists(DataFlow::Node subscriptObj | subscriptObj.asCfgNode() = subscript.getObject() | exists(DataFlow::Node subscriptObj | subscriptObj.asCfgNode() = subscript.getObject() |
headerLookup.flowsTo(subscriptObj) headerLookup.flowsTo(subscriptObj)
) and ) and
value.asCfgNode() = subscript.(Cfg::DefinitionNode).getValue() and value.asCfgNode() = subscript.(DefinitionNode).getValue() and
index.asCfgNode() = subscript.getIndex() index.asCfgNode() = subscript.getIndex()
) )
} }

View File

@@ -4,7 +4,6 @@
*/ */
import python import python
private import semmle.python.controlflow.internal.Cfg as Cfg
import semmle.python.dataflow.new.RemoteFlowSources import semmle.python.dataflow.new.RemoteFlowSources
import semmle.python.dataflow.new.TaintTracking import semmle.python.dataflow.new.TaintTracking
import semmle.python.ApiGraphs import semmle.python.ApiGraphs
@@ -52,9 +51,9 @@ module Gradio {
// limit only to lists of parameters given to `inputs`. // limit only to lists of parameters given to `inputs`.
( (
( (
call.getKeywordParameter("inputs").asSink().asCfgNode() instanceof Cfg::ListNode call.getKeywordParameter("inputs").asSink().asCfgNode() instanceof ListNode
or or
call.getParameter(1).asSink().asCfgNode() instanceof Cfg::ListNode call.getParameter(1).asSink().asCfgNode() instanceof ListNode
) and ) and
( (
this = call.getKeywordParameter("inputs").getASubscript().getAValueReachingSink() this = call.getKeywordParameter("inputs").getASubscript().getAValueReachingSink()
@@ -76,8 +75,8 @@ module Gradio {
exists(GradioInput call | exists(GradioInput call |
this = call.getParameter(0, "fn").getParameter(_).asSource() and this = call.getParameter(0, "fn").getParameter(_).asSource() and
// exclude lists of parameters given to `inputs` // exclude lists of parameters given to `inputs`
not call.getKeywordParameter("inputs").asSink().asCfgNode() instanceof Cfg::ListNode and not call.getKeywordParameter("inputs").asSink().asCfgNode() instanceof ListNode and
not call.getParameter(1).asSink().asCfgNode() instanceof Cfg::ListNode not call.getParameter(1).asSink().asCfgNode() instanceof ListNode
) )
} }
@@ -106,16 +105,16 @@ module Gradio {
// handle cases where there are multiple arguments passed as a list to `inputs` // handle cases where there are multiple arguments passed as a list to `inputs`
( (
( (
node.getKeywordParameter("inputs").asSink().asCfgNode() instanceof Cfg::ListNode node.getKeywordParameter("inputs").asSink().asCfgNode() instanceof ListNode
or or
node.getParameter(1).asSink().asCfgNode() instanceof Cfg::ListNode node.getParameter(1).asSink().asCfgNode() instanceof ListNode
) and ) and
exists(int i | nodeTo = node.getParameter(0, "fn").getParameter(i).asSource() | exists(int i | nodeTo = node.getParameter(0, "fn").getParameter(i).asSource() |
nodeFrom.asCfgNode() = nodeFrom.asCfgNode() =
node.getKeywordParameter("inputs").asSink().asCfgNode().(Cfg::ListNode).getElement(i) node.getKeywordParameter("inputs").asSink().asCfgNode().(ListNode).getElement(i)
or or
nodeFrom.asCfgNode() = nodeFrom.asCfgNode() =
node.getParameter(1).asSink().asCfgNode().(Cfg::ListNode).getElement(i) node.getParameter(1).asSink().asCfgNode().(ListNode).getElement(i)
) )
) )
) )

View File

@@ -4,7 +4,6 @@
*/ */
private import python private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.dataflow.new.DataFlow private import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.TaintTracking private import semmle.python.dataflow.new.TaintTracking
private import semmle.python.Concepts private import semmle.python.Concepts
@@ -47,7 +46,7 @@ module MarkupSafeModel {
/** A direct instantiation of `markupsafe.Markup`. */ /** A direct instantiation of `markupsafe.Markup`. */
private class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode { private class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode {
override Cfg::CallNode node; override CallNode node;
ClassInstantiation() { this = classRef().getACall() } ClassInstantiation() { this = classRef().getACall() }
} }
@@ -65,7 +64,7 @@ module MarkupSafeModel {
/** A string concatenation with a `markupsafe.Markup` involved. */ /** A string concatenation with a `markupsafe.Markup` involved. */
class StringConcat extends Markup::InstanceSource, DataFlow::CfgNode { class StringConcat extends Markup::InstanceSource, DataFlow::CfgNode {
override Cfg::BinaryExprNode node; override BinaryExprNode node;
StringConcat() { StringConcat() {
node.getOp() instanceof Add and node.getOp() instanceof Add and
@@ -80,7 +79,7 @@ module MarkupSafeModel {
/** A %-style string format with `markupsafe.Markup` as the format string. */ /** A %-style string format with `markupsafe.Markup` as the format string. */
class PercentStringFormat extends Markup::InstanceSource, DataFlow::CfgNode { class PercentStringFormat extends Markup::InstanceSource, DataFlow::CfgNode {
override Cfg::BinaryExprNode node; override BinaryExprNode node;
PercentStringFormat() { PercentStringFormat() {
node.getOp() instanceof Mod and node.getOp() instanceof Mod and

View File

@@ -7,7 +7,6 @@
*/ */
private import python private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.Concepts private import semmle.python.Concepts
private import semmle.python.ApiGraphs private import semmle.python.ApiGraphs
private import semmle.python.frameworks.data.ModelsAsData private import semmle.python.frameworks.data.ModelsAsData
@@ -57,7 +56,7 @@ module Pycurl {
{ {
OutgoingRequestCall() { OutgoingRequestCall() {
this = setopt().getACall() and this = setopt().getACall() and
this.getArg(0).asCfgNode().(Cfg::AttrNode).getName() = "URL" this.getArg(0).asCfgNode().(AttrNode).getName() = "URL"
} }
override DataFlow::Node getAUrlPart() { override DataFlow::Node getAUrlPart() {
@@ -82,7 +81,7 @@ module Pycurl {
private class CurlSslCall extends Http::Client::Request::Range instanceof DataFlow::CallCfgNode { private class CurlSslCall extends Http::Client::Request::Range instanceof DataFlow::CallCfgNode {
CurlSslCall() { CurlSslCall() {
this = setopt().getACall() and this = setopt().getACall() and
this.getArg(0).asCfgNode().(Cfg::AttrNode).getName() = ["SSL_VERIFYPEER", "SSL_VERIFYHOST"] this.getArg(0).asCfgNode().(AttrNode).getName() = ["SSL_VERIFYPEER", "SSL_VERIFYHOST"]
} }
override DataFlow::Node getAUrlPart() { none() } override DataFlow::Node getAUrlPart() { none() }

View File

@@ -7,7 +7,6 @@
*/ */
private import python private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.dataflow.new.DataFlow private import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.TaintTracking private import semmle.python.dataflow.new.TaintTracking
private import semmle.python.Concepts private import semmle.python.Concepts
@@ -94,7 +93,7 @@ module Pydantic {
// be a Pydantic model. So `model[0]` will be an overapproximation, but should not // be a Pydantic model. So `model[0]` will be an overapproximation, but should not
// really cause problems (since we don't expect real code to contain such accesses) // really cause problems (since we don't expect real code to contain such accesses)
nodeFrom = instance() and nodeFrom = instance() and
nodeTo.asCfgNode().(Cfg::SubscriptNode).getObject() = nodeFrom.asCfgNode() nodeTo.asCfgNode().(SubscriptNode).getObject() = nodeFrom.asCfgNode()
} }
/** /**

View File

@@ -6,7 +6,6 @@ overlay[local?]
module; module;
private import python private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.dataflow.new.DataFlow private import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.TaintTracking private import semmle.python.dataflow.new.TaintTracking
private import semmle.python.dataflow.new.RemoteFlowSources private import semmle.python.dataflow.new.RemoteFlowSources
@@ -1247,7 +1246,7 @@ module StdlibPrivate {
/** An additional taint step for calls to `os.path.join` */ /** An additional taint step for calls to `os.path.join` */
private class OsPathJoinCallAdditionalTaintStep extends TaintTracking::AdditionalTaintStep { private class OsPathJoinCallAdditionalTaintStep extends TaintTracking::AdditionalTaintStep {
override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { override predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
exists(Cfg::CallNode call | exists(CallNode call |
nodeTo.asCfgNode() = call and nodeTo.asCfgNode() = call and
call = OS::OsPath::join().getACall().asCfgNode() and call = OS::OsPath::join().getACall().asCfgNode() and
call.getAnArg() = nodeFrom.asCfgNode() call.getAnArg() = nodeFrom.asCfgNode()
@@ -1318,13 +1317,13 @@ module StdlibPrivate {
// run, so if we're able to, we only mark the first element as the command // run, so if we're able to, we only mark the first element as the command
// (and not the arguments to the command). // (and not the arguments to the command).
// //
result.asCfgNode() = arg_args.asCfgNode().(Cfg::SequenceNode).getElement(0) result.asCfgNode() = arg_args.asCfgNode().(SequenceNode).getElement(0)
or or
// Either the "args" argument is not a sequence (which is valid) or we where // Either the "args" argument is not a sequence (which is valid) or we where
// just not able to figure it out. Simply mark the "args" argument as the // just not able to figure it out. Simply mark the "args" argument as the
// command. // command.
// //
not arg_args.asCfgNode() instanceof Cfg::SequenceNode and not arg_args.asCfgNode() instanceof SequenceNode and
result = arg_args result = arg_args
) )
) )
@@ -1543,7 +1542,7 @@ module StdlibPrivate {
* See https://docs.python.org/3/library/functions.html#eval * See https://docs.python.org/3/library/functions.html#eval
*/ */
private class BuiltinsEvalCall extends CodeExecution::Range, DataFlow::CallCfgNode { private class BuiltinsEvalCall extends CodeExecution::Range, DataFlow::CallCfgNode {
override Cfg::CallNode node; override CallNode node;
BuiltinsEvalCall() { this = API::builtin("eval").getACall() } BuiltinsEvalCall() { this = API::builtin("eval").getACall() }
@@ -1924,7 +1923,7 @@ module StdlibPrivate {
nodeFrom = instance().getAValueReachableFromSource() and nodeFrom = instance().getAValueReachableFromSource() and
nodeTo = [getvalueRef(), getfirstRef(), getlistRef()].getAValueReachableFromSource() nodeTo = [getvalueRef(), getfirstRef(), getlistRef()].getAValueReachableFromSource()
or or
nodeFrom.asCfgNode() = nodeTo.asCfgNode().(Cfg::CallNode).getFunction() and nodeFrom.asCfgNode() = nodeTo.asCfgNode().(CallNode).getFunction() and
( (
nodeFrom = getvalueRef().getAValueReachableFromSource() and nodeFrom = getvalueRef().getAValueReachableFromSource() and
nodeTo = getvalueResult().asSource() nodeTo = getvalueResult().asSource()
@@ -1940,7 +1939,7 @@ module StdlibPrivate {
nodeFrom in [ nodeFrom in [
instance().getAValueReachableFromSource(), fieldList().getAValueReachableFromSource() instance().getAValueReachableFromSource(), fieldList().getAValueReachableFromSource()
] and ] and
nodeTo.asCfgNode().(Cfg::SubscriptNode).getObject() = nodeFrom.asCfgNode() nodeTo.asCfgNode().(SubscriptNode).getObject() = nodeFrom.asCfgNode()
or or
// Attributes on Field // Attributes on Field
nodeFrom = field().getAValueReachableFromSource() and nodeFrom = field().getAValueReachableFromSource() and
@@ -2255,8 +2254,8 @@ module StdlibPrivate {
DataFlow::CfgNode DataFlow::CfgNode
{ {
WsgirefSimpleServerApplicationReturn() { WsgirefSimpleServerApplicationReturn() {
exists(Return ret | exists(WsgirefSimpleServerApplication requestHandler, Return ret |
ret.getScope() = any(WsgirefSimpleServerApplication requestHandler) and ret.getScope() = requestHandler and
node.getNode() = ret.getValue() node.getNode() = ret.getValue()
) )
} }
@@ -2339,9 +2338,9 @@ module StdlibPrivate {
DataFlow::Node value; DataFlow::Node value;
HeaderWriteSubscript() { HeaderWriteSubscript() {
exists(Cfg::SubscriptNode subscript | exists(SubscriptNode subscript |
this.asCfgNode() = subscript and this.asCfgNode() = subscript and
value.asCfgNode() = subscript.(Cfg::DefinitionNode).getValue() and value.asCfgNode() = subscript.(DefinitionNode).getValue() and
name.asCfgNode() = subscript.getIndex() and name.asCfgNode() = subscript.getIndex() and
subscript.getObject() = instance().asCfgNode() subscript.getObject() = instance().asCfgNode()
) )
@@ -2683,7 +2682,7 @@ module StdlibPrivate {
or or
// Data injection // Data injection
// Special handling of the `/` operator // Special handling of the `/` operator
exists(Cfg::BinaryExprNode slash, DataFlow::Node pathOperand, DataFlow::TypeTracker t2 | exists(BinaryExprNode slash, DataFlow::Node pathOperand, DataFlow::TypeTracker t2 |
slash.getOp() instanceof Div and slash.getOp() instanceof Div and
pathOperand.asCfgNode() = slash.getAnOperand() and pathOperand.asCfgNode() = slash.getAnOperand() and
pathlibPath(t2).flowsTo(pathOperand) and pathlibPath(t2).flowsTo(pathOperand) and
@@ -2808,7 +2807,7 @@ module StdlibPrivate {
pathlibPath().flowsTo(nodeTo) and pathlibPath().flowsTo(nodeTo) and
( (
// Special handling of the `/` operator // Special handling of the `/` operator
exists(Cfg::BinaryExprNode slash, DataFlow::Node pathOperand | exists(BinaryExprNode slash, DataFlow::Node pathOperand |
slash.getOp() instanceof Div and slash.getOp() instanceof Div and
pathOperand.asCfgNode() = slash.getAnOperand() and pathOperand.asCfgNode() = slash.getAnOperand() and
pathlibPath().flowsTo(pathOperand) pathlibPath().flowsTo(pathOperand)
@@ -4606,9 +4605,9 @@ module StdlibPrivate {
} }
override predicate propagatesFlow(string input, string output, boolean preservesValue) { override predicate propagatesFlow(string input, string output, boolean preservesValue) {
exists(Cfg::CallNode c, string name, Cfg::ControlFlowNode n, DataFlow::AttributeContent ac | exists(CallNode c, string name, ControlFlowNode n, DataFlow::AttributeContent ac |
c.getFunction().(Cfg::NameNode).getId() = "replace" or c.getFunction().(NameNode).getId() = "replace" or
c.getFunction().(Cfg::AttrNode).getName() = "replace" c.getFunction().(AttrNode).getName() = "replace"
| |
n = c.getArgByName(name) and n = c.getArgByName(name) and
ac.getAttribute() = name and ac.getAttribute() = name and
@@ -5172,10 +5171,10 @@ module StdlibPrivate {
* See https://docs.python.org/3.9/library/stdtypes.html#str.startswith * See https://docs.python.org/3.9/library/stdtypes.html#str.startswith
*/ */
private class StartswithCall extends Path::SafeAccessCheck::Range { private class StartswithCall extends Path::SafeAccessCheck::Range {
StartswithCall() { this.(Cfg::CallNode).getFunction().(Cfg::AttrNode).getName() = "startswith" } StartswithCall() { this.(CallNode).getFunction().(AttrNode).getName() = "startswith" }
override predicate checks(Cfg::ControlFlowNode node, boolean branch) { override predicate checks(ControlFlowNode node, boolean branch) {
node = this.(Cfg::CallNode).getFunction().(Cfg::AttrNode).getObject() and node = this.(CallNode).getFunction().(AttrNode).getObject() and
branch = true branch = true
} }
} }

View File

@@ -8,7 +8,6 @@
*/ */
private import python private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.Concepts private import semmle.python.Concepts
private import semmle.python.ApiGraphs private import semmle.python.ApiGraphs
private import semmle.python.security.dataflow.UrlRedirectCustomizations private import semmle.python.security.dataflow.UrlRedirectCustomizations
@@ -92,7 +91,7 @@ private module Urllib {
* A read of the `netloc` attribute of a parsed URL as returned by `urllib.parse.urlparse`, * A read of the `netloc` attribute of a parsed URL as returned by `urllib.parse.urlparse`,
* which is being checked in a way that is relevant for URL redirection vulnerabilities. * which is being checked in a way that is relevant for URL redirection vulnerabilities.
*/ */
private predicate netlocCheck(DataFlow::GuardNode g, Cfg::ControlFlowNode node, boolean branch) { private predicate netlocCheck(DataFlow::GuardNode g, ControlFlowNode node, boolean branch) {
exists(DataFlow::CallCfgNode urlParseCall, DataFlow::AttrRead netlocRead | exists(DataFlow::CallCfgNode urlParseCall, DataFlow::AttrRead netlocRead |
urlParseCall = getUrlParseCall() and urlParseCall = getUrlParseCall() and
netlocRead = urlParseCall.getAnAttributeRead("netloc") and netlocRead = urlParseCall.getAnAttributeRead("netloc") and

View File

@@ -4,7 +4,6 @@
*/ */
private import python private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.dataflow.new.DataFlow private import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.RemoteFlowSources private import semmle.python.dataflow.new.RemoteFlowSources
private import semmle.python.dataflow.new.TaintTracking private import semmle.python.dataflow.new.TaintTracking
@@ -73,9 +72,9 @@ module Tornado {
DataFlow::Node value; DataFlow::Node value;
TornadoHeaderSubscriptWrite() { TornadoHeaderSubscriptWrite() {
exists(Cfg::SubscriptNode subscript | exists(SubscriptNode subscript |
subscript.getObject() = instance().asCfgNode() and subscript.getObject() = instance().asCfgNode() and
value.asCfgNode() = subscript.(Cfg::DefinitionNode).getValue() and value.asCfgNode() = subscript.(DefinitionNode).getValue() and
index.asCfgNode() = subscript.getIndex() and index.asCfgNode() = subscript.getIndex() and
this.asCfgNode() = subscript this.asCfgNode() = subscript
) )
@@ -423,7 +422,7 @@ module Tornado {
// be able to do something more structured for providing modeling of the members // be able to do something more structured for providing modeling of the members
// of a container-object. // of a container-object.
exists(DataFlow::AttrRead files | files.accesses(instance(), "cookies") | exists(DataFlow::AttrRead files | files.accesses(instance(), "cookies") |
this.asCfgNode().(Cfg::SubscriptNode).getObject() = files.asCfgNode() this.asCfgNode().(SubscriptNode).getObject() = files.asCfgNode()
or or
this.(DataFlow::MethodCallNode).calls(files, "get") this.(DataFlow::MethodCallNode).calls(files, "get")
) )
@@ -480,20 +479,20 @@ module Tornado {
// routing // routing
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
/** Gets a sequence that defines a number of route rules */ /** Gets a sequence that defines a number of route rules */
Cfg::SequenceNode routeSetupRuleList() { SequenceNode routeSetupRuleList() {
exists(Cfg::CallNode call | exists(CallNode call |
call = any(TornadoModule::Web::Application::ClassInstantiation c).asCfgNode() call = any(TornadoModule::Web::Application::ClassInstantiation c).asCfgNode()
| |
result in [call.getArg(0), call.getArgByName("handlers")] result in [call.getArg(0), call.getArgByName("handlers")]
) )
or or
exists(Cfg::CallNode call | exists(CallNode call |
call.getFunction() = TornadoModule::Web::Application::add_handlers().asCfgNode() call.getFunction() = TornadoModule::Web::Application::add_handlers().asCfgNode()
| |
result in [call.getArg(1), call.getArgByName("host_handlers")] result in [call.getArg(1), call.getArgByName("host_handlers")]
) )
or or
result = routeSetupRuleList().getElement(_).(Cfg::TupleNode).getElement(1) result = routeSetupRuleList().getElement(_).(TupleNode).getElement(1)
} }
/** A tornado route setup. */ /** A tornado route setup. */
@@ -516,12 +515,12 @@ module Tornado {
/** A route setup using a tuple. */ /** A route setup using a tuple. */
private class TornadoTupleRouteSetup extends TornadoRouteSetup, DataFlow::CfgNode { private class TornadoTupleRouteSetup extends TornadoRouteSetup, DataFlow::CfgNode {
override Cfg::TupleNode node; override TupleNode node;
TornadoTupleRouteSetup() { TornadoTupleRouteSetup() {
node = routeSetupRuleList().getElement(_) and node = routeSetupRuleList().getElement(_) and
count(node.getElement(_)) = 2 and count(node.getElement(_)) = 2 and
not node.getElement(1) instanceof Cfg::SequenceNode not node.getElement(1) instanceof SequenceNode
} }
override DataFlow::Node getUrlPatternArg() { result.asCfgNode() = node.getElement(0) } override DataFlow::Node getUrlPatternArg() { result.asCfgNode() = node.getElement(0) }

View File

@@ -6,7 +6,6 @@
*/ */
private import python private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.dataflow.new.DataFlow private import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.TaintTracking private import semmle.python.dataflow.new.TaintTracking
private import semmle.python.ApiGraphs private import semmle.python.ApiGraphs
@@ -222,9 +221,9 @@ module Werkzeug {
DataFlow::Node value; DataFlow::Node value;
HeaderWriteSubscript() { HeaderWriteSubscript() {
exists(Cfg::SubscriptNode subscript | exists(SubscriptNode subscript |
this.asCfgNode() = subscript and this.asCfgNode() = subscript and
value.asCfgNode() = subscript.(Cfg::DefinitionNode).getValue() and value.asCfgNode() = subscript.(DefinitionNode).getValue() and
name.asCfgNode() = subscript.getIndex() and name.asCfgNode() = subscript.getIndex() and
subscript.getObject() = instance().asCfgNode() subscript.getObject() = instance().asCfgNode()
) )

View File

@@ -8,7 +8,6 @@
*/ */
private import python private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.dataflow.new.DataFlow private import semmle.python.dataflow.new.DataFlow
private import semmle.python.Concepts private import semmle.python.Concepts
private import semmle.python.ApiGraphs private import semmle.python.ApiGraphs
@@ -29,7 +28,7 @@ private module Yaml {
* See https://pyyaml.org/wiki/PyYAMLDocumentation (you will have to scroll down). * See https://pyyaml.org/wiki/PyYAMLDocumentation (you will have to scroll down).
*/ */
private class YamlLoadCall extends Decoding::Range, DataFlow::CallCfgNode { private class YamlLoadCall extends Decoding::Range, DataFlow::CallCfgNode {
override Cfg::CallNode node; override CallNode node;
string func_name; string func_name;
YamlLoadCall() { YamlLoadCall() {

View File

@@ -4,7 +4,6 @@
*/ */
private import python private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.dataflow.new.DataFlow private import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.TaintTracking private import semmle.python.dataflow.new.TaintTracking
private import semmle.python.Concepts private import semmle.python.Concepts
@@ -112,7 +111,7 @@ module Yarl {
} }
private predicate yarlUrlIsAbsoluteCall( private predicate yarlUrlIsAbsoluteCall(
DataFlow::GuardNode g, Cfg::ControlFlowNode node, boolean branch DataFlow::GuardNode g, ControlFlowNode node, boolean branch
) { ) {
exists(ClassInstantiation instance, DataFlow::MethodCallNode call | exists(ClassInstantiation instance, DataFlow::MethodCallNode call |
call.calls(instance, "is_absolute") and call.calls(instance, "is_absolute") and

View File

@@ -11,7 +11,6 @@ private import semmle.python.dataflow.new.internal.ImportResolution
private import semmle.python.ApiGraphs private import semmle.python.ApiGraphs
private import semmle.python.filters.Tests private import semmle.python.filters.Tests
private import semmle.python.Module private import semmle.python.Module
private import semmle.python.controlflow.internal.Cfg as Cfg
// very much inspired by the draft at https://github.com/github/codeql/pull/5632 // very much inspired by the draft at https://github.com/github/codeql/pull/5632
module NotExposed { module NotExposed {
@@ -207,7 +206,7 @@ module NotExposed {
string relevantName, Location loc string relevantName, Location loc
) { ) {
loc = mod.getLocation() and loc = mod.getLocation() and
exists(API::Node relevantClass, Cfg::ControlFlowNode value | exists(API::Node relevantClass, ControlFlowNode value |
relevantClass = newOrExistingModeling(spec).getASubclass*() and relevantClass = newOrExistingModeling(spec).getASubclass*() and
ImportResolution::module_export(mod, relevantName, def) and ImportResolution::module_export(mod, relevantName, def) and
value = relevantClass.getAValueReachableFromSource().asCfgNode() and value = relevantClass.getAValueReachableFromSource().asCfgNode() and

View File

@@ -3,7 +3,6 @@
*/ */
import python import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.dataflow.new.DataFlow private import semmle.python.dataflow.new.DataFlow
private import semmle.python.Concepts as Concepts private import semmle.python.Concepts as Concepts
private import semmle.python.regex private import semmle.python.regex
@@ -79,7 +78,7 @@ private module FindRegexMode {
t.start() and t.start() and
exists(API::Node flag | flag_name = canonical_name(flag) and result = flag.asSource()) exists(API::Node flag | flag_name = canonical_name(flag) and result = flag.asSource())
or or
exists(Cfg::BinaryExprNode binop, DataFlow::Node operand | exists(BinaryExprNode binop, DataFlow::Node operand |
operand.getALocalSource() = re_flag_tracker(flag_name, t.continue()) and operand.getALocalSource() = re_flag_tracker(flag_name, t.continue()) and
operand.asCfgNode() = binop.getAnOperand() and operand.asCfgNode() = binop.getAnOperand() and
(binop.getOp() instanceof BitOr or binop.getOp() instanceof Add) and (binop.getOp() instanceof BitOr or binop.getOp() instanceof Add) and

View File

@@ -3,7 +3,6 @@
import python import python
import semmle.python.dataflow.new.DataFlow import semmle.python.dataflow.new.DataFlow
private import semmle.python.ApiGraphs private import semmle.python.ApiGraphs
private import semmle.python.controlflow.internal.Cfg as Cfg
/** /**
* INTERNAL: Do not use. * INTERNAL: Do not use.
@@ -30,7 +29,7 @@ private class TracebackFunctionCall extends ExceptionInfo, DataFlow::CallCfgNode
private class CaughtException extends ExceptionInfo { private class CaughtException extends ExceptionInfo {
CaughtException() { CaughtException() {
this.asExpr() = any(ExceptStmt s).getName() and this.asExpr() = any(ExceptStmt s).getName() and
this.asCfgNode().(Cfg::NameNode).defines(_) this.asCfgNode() = any(EssaNodeDefinition def).getDefiningNode()
} }
} }

View File

@@ -11,7 +11,6 @@ private import semmle.python.dataflow.new.RemoteFlowSources
private import semmle.python.dataflow.new.BarrierGuards private import semmle.python.dataflow.new.BarrierGuards
private import semmle.python.ApiGraphs private import semmle.python.ApiGraphs
private import semmle.python.frameworks.data.internal.ApiGraphModels private import semmle.python.frameworks.data.internal.ApiGraphModels
private import semmle.python.controlflow.internal.Cfg as Cfg
/** /**
* Provides default sources, sinks and sanitizers for detecting * Provides default sources, sinks and sanitizers for detecting
@@ -96,7 +95,7 @@ module ServerSideRequestForgery {
class StringConstructionAsFullUrlControlSanitizer extends FullUrlControlSanitizer { class StringConstructionAsFullUrlControlSanitizer extends FullUrlControlSanitizer {
StringConstructionAsFullUrlControlSanitizer() { StringConstructionAsFullUrlControlSanitizer() {
// string concat // string concat
exists(Cfg::BinaryExprNode add | exists(BinaryExprNode add |
add.getOp() instanceof Add and add.getOp() instanceof Add and
add.getRight() = this.asCfgNode() and add.getRight() = this.asCfgNode() and
not add.getLeft().getNode().(StringLiteral).getText().toLowerCase() in [ not add.getLeft().getNode().(StringLiteral).getText().toLowerCase() in [
@@ -105,7 +104,7 @@ module ServerSideRequestForgery {
) )
or or
// % formatting // % formatting
exists(Cfg::BinaryExprNode fmt | exists(BinaryExprNode fmt |
fmt.getOp() instanceof Mod and fmt.getOp() instanceof Mod and
fmt.getRight() = this.asCfgNode() and fmt.getRight() = this.asCfgNode() and
// detecting %-formatting is not super easy, so we simplify it to only handle // detecting %-formatting is not super easy, so we simplify it to only handle
@@ -156,9 +155,7 @@ module ServerSideRequestForgery {
} }
} }
private predicate stringRestriction( private predicate stringRestriction(DataFlow::GuardNode g, ControlFlowNode node, boolean branch) {
DataFlow::GuardNode g, Cfg::ControlFlowNode node, boolean branch
) {
exists(DataFlow::MethodCallNode call, DataFlow::Node strNode | exists(DataFlow::MethodCallNode call, DataFlow::Node strNode |
call.asCfgNode() = g and strNode.asCfgNode() = node call.asCfgNode() = g and strNode.asCfgNode() = node
| |

View File

@@ -9,7 +9,6 @@ private import semmle.python.dataflow.new.DataFlow
private import semmle.python.Concepts private import semmle.python.Concepts
private import semmle.python.dataflow.new.BarrierGuards private import semmle.python.dataflow.new.BarrierGuards
private import semmle.python.ApiGraphs private import semmle.python.ApiGraphs
private import semmle.python.controlflow.internal.Cfg as Cfg
/** /**
* Provides default sources, sinks and sanitizers for detecting * Provides default sources, sinks and sanitizers for detecting
@@ -140,8 +139,8 @@ module TarSlip {
* where `<check_path>` is any function matching `"%path"`. * where `<check_path>` is any function matching `"%path"`.
* `info` is assumed to be a `TarInfo` instance. * `info` is assumed to be a `TarInfo` instance.
*/ */
predicate tarFileInfoSanitizer(DataFlow::GuardNode g, Cfg::ControlFlowNode tarInfo, boolean branch) { predicate tarFileInfoSanitizer(DataFlow::GuardNode g, ControlFlowNode tarInfo, boolean branch) {
exists(Cfg::CallNode call, Cfg::AttrNode attr | exists(CallNode call, AttrNode attr |
g = call and g = call and
// We must test the name of the tar info object. // We must test the name of the tar info object.
attr = call.getAnArg() and attr = call.getAnArg() and
@@ -149,9 +148,9 @@ module TarSlip {
attr.getObject() = tarInfo attr.getObject() = tarInfo
| |
// The assumption that any test that matches %path is a sanitizer might be too broad. // The assumption that any test that matches %path is a sanitizer might be too broad.
call.getAChild*().(Cfg::AttrNode).getName().matches("%path") call.getAChild*().(AttrNode).getName().matches("%path")
or or
call.getAChild*().(Cfg::NameNode).getId().matches("%path") call.getAChild*().(NameNode).getId().matches("%path")
) and ) and
branch = false branch = false
} }

View File

@@ -5,7 +5,6 @@
*/ */
private import python private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.dataflow.new.DataFlow private import semmle.python.dataflow.new.DataFlow
private import semmle.python.Concepts private import semmle.python.Concepts
private import semmle.python.ApiGraphs private import semmle.python.ApiGraphs
@@ -112,7 +111,7 @@ module UrlRedirect {
// Url redirection is a problem only if the user controls the prefix of the URL. // Url redirection is a problem only if the user controls the prefix of the URL.
// TODO: This is a copy of the taint-sanitizer from the old points-to query, which doesn't // TODO: This is a copy of the taint-sanitizer from the old points-to query, which doesn't
// cover formatting. // cover formatting.
exists(Cfg::BinaryExprNode string_concat | string_concat.getOp() instanceof Add | exists(BinaryExprNode string_concat | string_concat.getOp() instanceof Add |
string_concat.getRight() = this.asCfgNode() string_concat.getRight() = this.asCfgNode()
) )
} }

View File

@@ -1,5 +1,4 @@
import python import python
private import semmle.python.controlflow.internal.Cfg as Cfg
import semmle.python.dataflow.new.DataFlow import semmle.python.dataflow.new.DataFlow
private import semmle.python.dataflow.new.internal.DataFlowPrivate private import semmle.python.dataflow.new.internal.DataFlowPrivate
import FlowTest import FlowTest
@@ -24,7 +23,7 @@ import MakeTest<MakeTestSig<MaximalFlowTest>>
module MaximalFlowsConfig implements DataFlow::ConfigSig { module MaximalFlowsConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node node) { predicate isSource(DataFlow::Node node) {
exists(node.getLocation().getFile().getRelativePath()) and exists(node.getLocation().getFile().getRelativePath()) and
not node.asCfgNode() instanceof Cfg::CallNode and not node.asCfgNode() instanceof CallNode and
not node.asCfgNode().getNode() instanceof Return and not node.asCfgNode().getNode() instanceof Return and
not node instanceof DataFlow::ParameterNode and not node instanceof DataFlow::ParameterNode and
not node instanceof DataFlow::PostUpdateNode and not node instanceof DataFlow::PostUpdateNode and
@@ -35,9 +34,9 @@ module MaximalFlowsConfig implements DataFlow::ConfigSig {
predicate isSink(DataFlow::Node node) { predicate isSink(DataFlow::Node node) {
exists(node.getLocation().getFile().getRelativePath()) and exists(node.getLocation().getFile().getRelativePath()) and
not any(Cfg::CallNode c).getArg(_) = node.asCfgNode() and not any(CallNode c).getArg(_) = node.asCfgNode() and
not isArgumentNode(node, _, _) and not isArgumentNode(node, _, _) and
not node.asCfgNode().(Cfg::NameNode).getId().matches("SINK%") and not node.asCfgNode().(NameNode).getId().matches("SINK%") and
not DataFlow::localFlowStep(node, _) not DataFlow::localFlowStep(node, _)
} }
} }

View File

@@ -1,5 +1,4 @@
import python import python
private import semmle.python.controlflow.internal.Cfg as Cfg
import utils.test.dataflow.FlowTest import utils.test.dataflow.FlowTest
import utils.test.dataflow.testConfig import utils.test.dataflow.testConfig
private import semmle.python.dataflow.new.internal.PrintNode private import semmle.python.dataflow.new.internal.PrintNode
@@ -20,7 +19,7 @@ query predicate missingAnnotationOnSink(Location location, string error, string
TestConfig::isSink(sink) and TestConfig::isSink(sink) and
// note: we only care about `SINK` and not `SINK_F`, so we have to reconstruct manually. // note: we only care about `SINK` and not `SINK_F`, so we have to reconstruct manually.
exists(DataFlow::CallCfgNode call | exists(DataFlow::CallCfgNode call |
call.getFunction().asCfgNode().(Cfg::NameNode).getId() = "SINK" and call.getFunction().asCfgNode().(NameNode).getId() = "SINK" and
(sink = call.getArg(_) or sink = call.getArgByName(_)) (sink = call.getArg(_) or sink = call.getArgByName(_))
) and ) and
location = sink.getLocation() and location = sink.getLocation() and

View File

@@ -1,5 +1,4 @@
import python import python
private import semmle.python.controlflow.internal.Cfg as Cfg
import utils.test.dataflow.FlowTest import utils.test.dataflow.FlowTest
import utils.test.dataflow.testTaintConfig import utils.test.dataflow.testTaintConfig
private import semmle.python.dataflow.new.internal.PrintNode private import semmle.python.dataflow.new.internal.PrintNode
@@ -19,7 +18,7 @@ query predicate missingAnnotationOnSink(Location location, string error, string
exists(DataFlow::Node sink | exists(DataFlow::Node sink |
exists(DataFlow::CallCfgNode call | exists(DataFlow::CallCfgNode call |
// note: we only care about `SINK` and not `SINK_F`, so we have to reconstruct manually. // note: we only care about `SINK` and not `SINK_F`, so we have to reconstruct manually.
call.getFunction().asCfgNode().(Cfg::NameNode).getId() = "SINK" and call.getFunction().asCfgNode().(NameNode).getId() = "SINK" and
(sink = call.getArg(_) or sink = call.getArgByName(_)) (sink = call.getArg(_) or sink = call.getArgByName(_))
) and ) and
location = sink.getLocation() and location = sink.getLocation() and

View File

@@ -1,5 +1,4 @@
import python import python
private import semmle.python.controlflow.internal.Cfg as Cfg
import semmle.python.dataflow.new.DataFlow import semmle.python.dataflow.new.DataFlow
import utils.test.InlineExpectationsTest import utils.test.InlineExpectationsTest
private import semmle.python.dataflow.new.internal.PrintNode private import semmle.python.dataflow.new.internal.PrintNode
@@ -50,7 +49,7 @@ private string fromValue(DataFlow::Node fromNode) {
pragma[inline] pragma[inline]
private string fromFunc(DataFlow::ArgumentNode fromNode) { private string fromFunc(DataFlow::ArgumentNode fromNode) {
result = fromNode.getCall().getNode().(Cfg::CallNode).getFunction().getNode().(Name).getId() result = fromNode.getCall().getNode().(CallNode).getFunction().getNode().(Name).getId()
} }
pragma[inline] pragma[inline]

View File

@@ -1,16 +1,15 @@
import python import python
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.dataflow.new.internal.PrintNode private import semmle.python.dataflow.new.internal.PrintNode
private import semmle.python.dataflow.new.internal.DataFlowPrivate as DataFlowPrivate private import semmle.python.dataflow.new.internal.DataFlowPrivate as DataFlowPrivate
private import semmle.python.ApiGraphs private import semmle.python.ApiGraphs
import utils.test.InlineExpectationsTest import utils.test.InlineExpectationsTest
signature module UnresolvedCallExpectationsSig { signature module UnresolvedCallExpectationsSig {
predicate unresolvedCall(Cfg::CallNode call); predicate unresolvedCall(CallNode call);
} }
module DefaultUnresolvedCallExpectations implements UnresolvedCallExpectationsSig { module DefaultUnresolvedCallExpectations implements UnresolvedCallExpectationsSig {
predicate unresolvedCall(Cfg::CallNode call) { predicate unresolvedCall(CallNode call) {
not exists(DataFlowPrivate::DataFlowCall dfc | not exists(DataFlowPrivate::DataFlowCall dfc |
exists(dfc.getCallable()) and dfc.getNode() = call exists(dfc.getCallable()) and dfc.getNode() = call
) and ) and
@@ -25,7 +24,7 @@ module MakeUnresolvedCallExpectations<UnresolvedCallExpectationsSig Impl> {
predicate hasActualResult(Location location, string element, string tag, string value) { predicate hasActualResult(Location location, string element, string tag, string value) {
exists(location.getFile().getRelativePath()) and exists(location.getFile().getRelativePath()) and
exists(Cfg::CallNode call | Impl::unresolvedCall(call) and call.injects(_) | exists(CallNode call | Impl::unresolvedCall(call) |
location = call.getLocation() and location = call.getLocation() and
tag = "unresolved_call" and tag = "unresolved_call" and
value = prettyExpr(call.getNode()) and value = prettyExpr(call.getNode()) and

View File

@@ -21,12 +21,11 @@
*/ */
private import python private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
import semmle.python.dataflow.new.DataFlow import semmle.python.dataflow.new.DataFlow
module TestConfig implements DataFlow::ConfigSig { module TestConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node node) { predicate isSource(DataFlow::Node node) {
node.(DataFlow::CfgNode).getNode().(Cfg::NameNode).getId() = "SOURCE" node.(DataFlow::CfgNode).getNode().(NameNode).getId() = "SOURCE"
or or
node.(DataFlow::CfgNode).getNode().getNode().(StringLiteral).getS() = "source" node.(DataFlow::CfgNode).getNode().getNode().(StringLiteral).getS() = "source"
or or
@@ -38,7 +37,7 @@ module TestConfig implements DataFlow::ConfigSig {
predicate isSink(DataFlow::Node node) { predicate isSink(DataFlow::Node node) {
exists(DataFlow::CallCfgNode call | exists(DataFlow::CallCfgNode call |
call.getFunction().asCfgNode().(Cfg::NameNode).getId() in ["SINK", "SINK_F"] and call.getFunction().asCfgNode().(NameNode).getId() in ["SINK", "SINK_F"] and
(node = call.getArg(_) or node = call.getArgByName(_)) and (node = call.getArg(_) or node = call.getArgByName(_)) and
not node = call.getArgByName("not_present_at_runtime") not node = call.getArgByName("not_present_at_runtime")
) )

View File

@@ -21,13 +21,12 @@
*/ */
private import python private import python
private import semmle.python.controlflow.internal.Cfg as Cfg
import semmle.python.dataflow.new.DataFlow import semmle.python.dataflow.new.DataFlow
import semmle.python.dataflow.new.TaintTracking import semmle.python.dataflow.new.TaintTracking
module TestConfig implements DataFlow::ConfigSig { module TestConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node node) { predicate isSource(DataFlow::Node node) {
node.(DataFlow::CfgNode).getNode().(Cfg::NameNode).getId() = "SOURCE" node.(DataFlow::CfgNode).getNode().(NameNode).getId() = "SOURCE"
or or
node.(DataFlow::CfgNode).getNode().getNode().(StringLiteral).getS() = "source" node.(DataFlow::CfgNode).getNode().getNode().(StringLiteral).getS() = "source"
or or
@@ -38,8 +37,8 @@ module TestConfig implements DataFlow::ConfigSig {
} }
predicate isSink(DataFlow::Node node) { predicate isSink(DataFlow::Node node) {
exists(Cfg::CallNode call | exists(CallNode call |
call.getFunction().(Cfg::NameNode).getId() in ["SINK", "SINK_F"] and call.getFunction().(NameNode).getId() in ["SINK", "SINK_F"] and
node.(DataFlow::CfgNode).getNode() = call.getAnArg() node.(DataFlow::CfgNode).getNode() = call.getAnArg()
) )
} }

View File

@@ -12,8 +12,6 @@
import python import python
private import semmle.python.ApiGraphs private import semmle.python.ApiGraphs
private import semmle.python.controlflow.internal.Cfg as Cfg
private import semmle.python.Flow as Flow
API::Node iter() { result = API::builtin("iter") } API::Node iter() { result = API::builtin("iter") }
@@ -21,17 +19,17 @@ API::Node next() { result = API::builtin("next") }
API::Node stopIteration() { result = API::builtin("StopIteration") } API::Node stopIteration() { result = API::builtin("StopIteration") }
predicate call_to_iter(Flow::CallNode call, EssaVariable sequence) { predicate call_to_iter(CallNode call, EssaVariable sequence) {
call.getNode() = iter().getACall().asCfgNode().(Cfg::CallNode).getNode() and call = iter().getACall().asCfgNode() and
call.getArg(0) = sequence.getAUse() call.getArg(0) = sequence.getAUse()
} }
predicate call_to_next(Flow::CallNode call, Flow::ControlFlowNode iter) { predicate call_to_next(CallNode call, ControlFlowNode iter) {
call.getNode() = next().getACall().asCfgNode().(Cfg::CallNode).getNode() and call = next().getACall().asCfgNode() and
call.getArg(0) = iter call.getArg(0) = iter
} }
predicate call_to_next_has_default(Flow::CallNode call) { predicate call_to_next_has_default(CallNode call) {
exists(call.getArg(1)) or exists(call.getArgByName("default")) exists(call.getArg(1)) or exists(call.getArgByName("default"))
} }
@@ -51,14 +49,14 @@ predicate iter_not_exhausted(EssaVariable iterator) {
) )
} }
predicate stop_iteration_handled(Flow::CallNode call) { predicate stop_iteration_handled(CallNode call) {
exists(Try t | exists(Try t |
t.containsInScope(call.getNode()) and t.containsInScope(call.getNode()) and
t.getAHandler().getType() = stopIteration().getAValueReachableFromSource().asExpr() t.getAHandler().getType() = stopIteration().getAValueReachableFromSource().asExpr()
) )
} }
from Flow::CallNode call from CallNode call
where where
call_to_next(call, _) and call_to_next(call, _) and
not call_to_next_has_default(call) and not call_to_next_has_default(call) and

View File

@@ -11,9 +11,8 @@
import python import python
private import semmle.python.ApiGraphs private import semmle.python.ApiGraphs
private import semmle.python.controlflow.internal.Cfg as Cfg
from Cfg::CallNode call from CallNode call
where where
major_version() = 2 and major_version() = 2 and
call = API::builtin("apply").getACall().asCfgNode() call = API::builtin("apply").getACall().asCfgNode()

View File

@@ -15,7 +15,6 @@
import python import python
import semmle.python.dataflow.new.DataFlow import semmle.python.dataflow.new.DataFlow
import semmle.python.dataflow.new.internal.DataFlowDispatch import semmle.python.dataflow.new.internal.DataFlowDispatch
private import semmle.python.controlflow.internal.Cfg as Cfg
import codeql.util.Option import codeql.util.Option
/** Holds if `base` is overridden by `sub` */ /** Holds if `base` is overridden by `sub` */
@@ -144,7 +143,7 @@ predicate ignore(Function f) {
/** Gets a function that `call` may resolve to. */ /** Gets a function that `call` may resolve to. */
Function resolveCall(Call call) { Function resolveCall(Call call) {
exists(DataFlowCall dfc | call = dfc.getNode().(Cfg::CallNode).getNode() | exists(DataFlowCall dfc | call = dfc.getNode().(CallNode).getNode() |
result = viableCallable(dfc).(DataFlowFunction).getScope() result = viableCallable(dfc).(DataFlowFunction).getScope()
) )
} }

View File

@@ -4,7 +4,6 @@ import python
import semmle.python.dataflow.new.internal.DataFlowDispatch import semmle.python.dataflow.new.internal.DataFlowDispatch
import semmle.python.ApiGraphs import semmle.python.ApiGraphs
private import semmle.python.dataflow.new.internal.ReExposedInstance private import semmle.python.dataflow.new.internal.ReExposedInstance
private import semmle.python.controlflow.internal.Cfg as Cfg
/** A CFG node where a file is opened. */ /** A CFG node where a file is opened. */
abstract class FileOpenSource extends DataFlow::CfgNode { } abstract class FileOpenSource extends DataFlow::CfgNode { }
@@ -82,14 +81,12 @@ abstract class FileClose extends DataFlow::CfgNode {
} }
} }
private predicate bbSuccessor(Cfg::BasicBlock src, Cfg::BasicBlock sink) { private predicate bbSuccessor(BasicBlock src, BasicBlock sink) { sink = src.getASuccessor() }
sink = src.getASuccessor()
}
private predicate bbReachableStrict(Cfg::BasicBlock src, Cfg::BasicBlock sink) = private predicate bbReachableStrict(BasicBlock src, BasicBlock sink) =
fastTC(bbSuccessor/2)(src, sink) fastTC(bbSuccessor/2)(src, sink)
private predicate bbReachableRefl(Cfg::BasicBlock src, Cfg::BasicBlock sink) { private predicate bbReachableRefl(BasicBlock src, BasicBlock sink) {
bbReachableStrict(src, sink) or src = sink bbReachableStrict(src, sink) or src = sink
} }

View File

@@ -10,7 +10,6 @@ private import semmle.python.dataflow.new.RemoteFlowSources
private import semmle.python.ApiGraphs private import semmle.python.ApiGraphs
private import semmle.python.dataflow.new.internal.DataFlowPrivate as DataFlowPrivate private import semmle.python.dataflow.new.internal.DataFlowPrivate as DataFlowPrivate
private import semmle.python.dataflow.new.internal.TaintTrackingPrivate as TaintTrackingPrivate private import semmle.python.dataflow.new.internal.TaintTrackingPrivate as TaintTrackingPrivate
private import semmle.python.controlflow.internal.Cfg as Cfg
/** /**
* An external API that is considered "safe" from a security perspective. * An external API that is considered "safe" from a security perspective.
@@ -72,7 +71,7 @@ string apiNodeToStringRepr(API::Node node) {
) )
} }
predicate resolvedCall(Cfg::CallNode call) { predicate resolvedCall(CallNode call) {
DataFlowPrivate::resolveCall(call, _, _) or DataFlowPrivate::resolveCall(call, _, _) or
DataFlowPrivate::resolveClassCall(call, _) DataFlowPrivate::resolveClassCall(call, _)
} }

View File

@@ -14,7 +14,6 @@
import python import python
import semmle.python.dataflow.new.DataFlow import semmle.python.dataflow.new.DataFlow
import semmle.python.ApiGraphs import semmle.python.ApiGraphs
private import semmle.python.controlflow.internal.Cfg as Cfg
/* /*
* Jinja 2 Docs: * Jinja 2 Docs:
@@ -37,8 +36,8 @@ private API::Node jinja2EnvironmentOrTemplate() {
from API::CallNode call from API::CallNode call
where where
call = jinja2EnvironmentOrTemplate().getACall() and call = jinja2EnvironmentOrTemplate().getACall() and
not exists(call.asCfgNode().(Cfg::CallNode).getNode().getStarargs()) and not exists(call.asCfgNode().(CallNode).getNode().getStarargs()) and
not exists(call.asCfgNode().(Cfg::CallNode).getNode().getKwargs()) and not exists(call.asCfgNode().(CallNode).getNode().getKwargs()) and
( (
not exists(call.getArgByName("autoescape")) not exists(call.getArgByName("autoescape"))
or or

View File

@@ -5,7 +5,6 @@
private import python private import python
private import semmle.python.ApiGraphs private import semmle.python.ApiGraphs
private import semmle.python.controlflow.internal.Cfg as Cfg
import TlsLibraryModel import TlsLibraryModel
class PyOpenSslContextCreation extends ContextCreation, DataFlow::CallCfgNode { class PyOpenSslContextCreation extends ContextCreation, DataFlow::CallCfgNode {
@@ -38,10 +37,10 @@ class ConnectionCall extends ConnectionCreation, DataFlow::CallCfgNode {
// This cannot be used to unrestrict, // This cannot be used to unrestrict,
// see https://www.pyopenssl.org/en/stable/api/ssl.html#OpenSSL.SSL.Context.set_options // see https://www.pyopenssl.org/en/stable/api/ssl.html#OpenSSL.SSL.Context.set_options
class SetOptionsCall extends ProtocolRestriction, DataFlow::CallCfgNode { class SetOptionsCall extends ProtocolRestriction, DataFlow::CallCfgNode {
SetOptionsCall() { node.getFunction().(Cfg::AttrNode).getName() = "set_options" } SetOptionsCall() { node.getFunction().(AttrNode).getName() = "set_options" }
override DataFlow::CfgNode getContext() { override DataFlow::CfgNode getContext() {
result.getNode() = node.getFunction().(Cfg::AttrNode).getObject() result.getNode() = node.getFunction().(AttrNode).getObject()
} }
override ProtocolVersion getRestriction() { override ProtocolVersion getRestriction() {

View File

@@ -5,7 +5,6 @@
private import python private import python
private import semmle.python.ApiGraphs private import semmle.python.ApiGraphs
private import semmle.python.controlflow.internal.Cfg as Cfg
import TlsLibraryModel import TlsLibraryModel
class SslContextCreation extends ContextCreation, DataFlow::CallCfgNode { class SslContextCreation extends ContextCreation, DataFlow::CallCfgNode {
@@ -54,7 +53,7 @@ class OptionsAugOr extends ProtocolRestriction, DataFlow::CfgNode {
ProtocolVersion restriction; ProtocolVersion restriction;
OptionsAugOr() { OptionsAugOr() {
exists(AugAssign aa, Cfg::AttrNode attr, Expr flag | exists(AugAssign aa, AttrNode attr, Expr flag |
aa.getOperation().getOp() instanceof BitOr and aa.getOperation().getOp() instanceof BitOr and
aa.getTarget() = attr.getNode() and aa.getTarget() = attr.getNode() and
attr.getName() = "options" and attr.getName() = "options" and
@@ -81,7 +80,7 @@ class OptionsAugAndNot extends ProtocolUnrestriction, DataFlow::CfgNode {
ProtocolVersion restriction; ProtocolVersion restriction;
OptionsAugAndNot() { OptionsAugAndNot() {
exists(AugAssign aa, Cfg::AttrNode attr, Expr flag, UnaryExpr notFlag | exists(AugAssign aa, AttrNode attr, Expr flag, UnaryExpr notFlag |
aa.getOperation().getOp() instanceof BitAnd and aa.getOperation().getOp() instanceof BitAnd and
aa.getTarget() = attr.getNode() and aa.getTarget() = attr.getNode() and
attr.getName() = "options" and attr.getName() = "options" and

View File

@@ -19,7 +19,6 @@ import semmle.python.filters.Tests
private import semmle.python.dataflow.new.internal.DataFlowDispatch as DataFlowDispatch private import semmle.python.dataflow.new.internal.DataFlowDispatch as DataFlowDispatch
private import semmle.python.dataflow.new.internal.Builtins::Builtins as Builtins private import semmle.python.dataflow.new.internal.Builtins::Builtins as Builtins
private import semmle.python.frameworks.data.ModelsAsData private import semmle.python.frameworks.data.ModelsAsData
private import semmle.python.controlflow.internal.Cfg as Cfg
bindingset[char, fraction] bindingset[char, fraction]
predicate fewer_characters_than(StringLiteral str, string char, float fraction) { predicate fewer_characters_than(StringLiteral str, string char, float fraction) {
@@ -49,7 +48,7 @@ predicate capitalized_word(StringLiteral str) { str.getText().regexpMatch("[A-Z]
predicate format_string(StringLiteral str) { str.getText().matches("%{%}%") } predicate format_string(StringLiteral str) { str.getText().matches("%{%}%") }
predicate maybeCredential(Cfg::ControlFlowNode f) { predicate maybeCredential(ControlFlowNode f) {
/* A string that is not too short and unlikely to be text or an identifier. */ /* A string that is not too short and unlikely to be text or an identifier. */
exists(StringLiteral str | str = f.getNode() | exists(StringLiteral str | str = f.getNode() |
/* At least 10 characters */ /* At least 10 characters */
@@ -97,7 +96,7 @@ class CredentialSink extends DataFlow::Node {
or or
exists(Keyword k | k.getArg() = name and this.asCfgNode().getNode() = k.getValue()) exists(Keyword k | k.getArg() = name and this.asCfgNode().getNode() = k.getValue())
or or
exists(Cfg::CompareNode cmp, Cfg::NameNode n | n.getId() = name | exists(CompareNode cmp, NameNode n | n.getId() = name |
cmp.operands(this.asCfgNode(), any(Eq eq), n) cmp.operands(this.asCfgNode(), any(Eq eq), n)
or or
cmp.operands(n, any(Eq eq), this.asCfgNode()) cmp.operands(n, any(Eq eq), this.asCfgNode())

View File

@@ -14,9 +14,8 @@
import python import python
private import semmle.python.ApiGraphs private import semmle.python.ApiGraphs
private import semmle.python.dataflow.new.DataFlow private import semmle.python.dataflow.new.DataFlow
private import semmle.python.controlflow.internal.Cfg as Cfg
predicate originIsLocals(Cfg::ControlFlowNode n) { predicate originIsLocals(ControlFlowNode n) {
// Only consider the `locals()` dictionary within the scope that called `locals()`. // Only consider the `locals()` dictionary within the scope that called `locals()`.
// Once the dictionary is passed to another scope (e.g. as an argument or via an // Once the dictionary is passed to another scope (e.g. as an argument or via an
// instance attribute) it is just an ordinary mapping, and modifying it is both // instance attribute) it is just an ordinary mapping, and modifying it is both
@@ -29,19 +28,19 @@ predicate originIsLocals(Cfg::ControlFlowNode n) {
) )
} }
predicate modification_of_locals(Cfg::ControlFlowNode f) { predicate modification_of_locals(ControlFlowNode f) {
originIsLocals(f.(Cfg::SubscriptNode).getObject()) and originIsLocals(f.(SubscriptNode).getObject()) and
(f.isStore() or f.isDelete()) (f.isStore() or f.isDelete())
or or
exists(string mname, Cfg::AttrNode attr | exists(string mname, AttrNode attr |
attr = f.(Cfg::CallNode).getFunction() and attr = f.(CallNode).getFunction() and
originIsLocals(attr.getObject(mname)) originIsLocals(attr.getObject(mname))
| |
mname in ["pop", "popitem", "update", "clear"] mname in ["pop", "popitem", "update", "clear"]
) )
} }
from AstNode a, Cfg::ControlFlowNode f from AstNode a, ControlFlowNode f
where where
modification_of_locals(f) and modification_of_locals(f) and
a = f.getNode() and a = f.getNode() and

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