mirror of
https://github.com/github/codeql.git
synced 2026-06-30 17:15:34 +02:00
Compare commits
4 Commits
josefs/cyc
...
yoff/pytho
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e50e81390f | ||
|
|
66900c7d62 | ||
|
|
956d2dbec4 | ||
|
|
872c08148e |
@@ -1,4 +0,0 @@
|
||||
---
|
||||
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.
|
||||
@@ -40,24 +40,12 @@ module Input implements InputSig<Location, DataFlowImplSpecific::CppDataFlow> {
|
||||
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) {
|
||||
exists(FieldContent c, string namespace, string type, string base |
|
||||
exists(FieldContent c |
|
||||
cs.isSingleton(c) and
|
||||
// FieldContent indices have 0 for the address, 1 for content, so we need to subtract one.
|
||||
result = "Field" and
|
||||
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
|
||||
arg = repeatStars(c.getIndirectionIndex() - 1) + c.getField().getName()
|
||||
)
|
||||
or
|
||||
exists(ElementContent ec |
|
||||
|
||||
@@ -1378,8 +1378,6 @@ predicate nodeIsHidden(Node n) {
|
||||
n instanceof InitialGlobalValue
|
||||
or
|
||||
n instanceof SsaSynthNode
|
||||
or
|
||||
n.(FlowSummaryNode).getSummaryNode().isHidden()
|
||||
}
|
||||
|
||||
predicate neverSkipInPathGraph(Node n) {
|
||||
|
||||
@@ -48,20 +48,19 @@ models
|
||||
| 47 | Summary: ; ; false; callWithArgument; ; ; Argument[1]; Argument[0].Parameter[0]; 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 |
|
||||
| 50 | Summary: ; ; false; read_field_from_struct; ; ; Argument[*0].Field[MyNamespace::MyStructInNamespace::myField]; ReturnValue; value; manual |
|
||||
| 51 | Summary: ; ; false; read_field_from_struct_2; ; ; Argument[*0].Field[MyGlobalStruct::myField]; ReturnValue; value; manual |
|
||||
| 52 | Summary: ; ; false; ymlStepGenerated; ; ; Argument[0]; ReturnValue; taint; df-generated |
|
||||
| 53 | Summary: ; ; false; ymlStepManual; ; ; Argument[0]; ReturnValue; taint; manual |
|
||||
| 54 | Summary: ; ; false; ymlStepManual_with_body; ; ; Argument[0]; ReturnValue; taint; manual |
|
||||
| 55 | Summary: ; TemplateClass1; true; templateFunction2<U,V>; (U,V); ; Argument[1]; ReturnValue; value; manual |
|
||||
| 56 | Summary: ; TemplateClass1<T>; false; templateFunction<U>; (T,U); ; Argument[0]; ReturnValue; value; manual |
|
||||
| 57 | Summary: ; TemplateClass2<T,U>; true; function; (U,T); ; Argument[1]; ReturnValue; value; manual |
|
||||
| 58 | Summary: Azure::Core::IO; BodyStream; true; Read; ; ; Argument[-1]; Argument[*0]; taint; manual |
|
||||
| 59 | Summary: Azure::Core::IO; BodyStream; true; ReadToCount; ; ; Argument[-1]; Argument[*0]; 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 |
|
||||
| 50 | Summary: ; ; false; ymlStepGenerated; ; ; Argument[0]; ReturnValue; taint; df-generated |
|
||||
| 51 | Summary: ; ; false; ymlStepManual; ; ; Argument[0]; ReturnValue; taint; manual |
|
||||
| 52 | Summary: ; ; false; ymlStepManual_with_body; ; ; Argument[0]; ReturnValue; taint; manual |
|
||||
| 53 | Summary: ; TemplateClass1; true; templateFunction2<U,V>; (U,V); ; Argument[1]; ReturnValue; value; manual |
|
||||
| 54 | Summary: ; TemplateClass1<T>; false; templateFunction<U>; (T,U); ; Argument[0]; ReturnValue; value; manual |
|
||||
| 55 | Summary: ; TemplateClass2<T,U>; true; function; (U,T); ; Argument[1]; ReturnValue; value; manual |
|
||||
| 56 | Summary: Azure::Core::IO; BodyStream; true; Read; ; ; Argument[-1]; Argument[*0]; taint; manual |
|
||||
| 57 | Summary: Azure::Core::IO; BodyStream; true; ReadToCount; ; ; Argument[-1]; Argument[*0]; taint; manual |
|
||||
| 58 | Summary: Azure::Core::IO; BodyStream; true; ReadToEnd; ; ; Argument[-1]; ReturnValue.Element; taint; manual |
|
||||
| 59 | Summary: Azure; Nullable; true; Value; ; ; Argument[-1]; ReturnValue[*]; taint; manual |
|
||||
| 60 | Summary: boost::asio; ; false; buffer; ; ; Argument[*0]; ReturnValue; taint; manual |
|
||||
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: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 |
|
||||
@@ -69,16 +68,25 @@ 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: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: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: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: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: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:266:38:266:41 | *resp | 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: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:56 |
|
||||
| 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:262:23:262:28 | ReadToCount output argument | provenance | MaD:59 |
|
||||
| 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:57 |
|
||||
| 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:266:44:266:52 | call to ReadToEnd [element] | provenance | MaD:60 |
|
||||
| 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: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:267:10:267:12 | vec [element] | provenance | |
|
||||
| azure.cpp:267:10:267:12 | vec [element] | azure.cpp:267:10:267:12 | vec | provenance | |
|
||||
@@ -94,10 +102,12 @@ edges
|
||||
| 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:282:21:282:23 | *call to get | 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: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: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:28:282:36 | call to ReadToEnd [element] | 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: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: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: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 | |
|
||||
@@ -109,6 +119,9 @@ 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: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: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 |
|
||||
@@ -119,13 +132,16 @@ edges
|
||||
| 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:18:10:18:10 | y | provenance | Sink:MaD:1 |
|
||||
| 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: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: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:22:10:22:10 | z | provenance | Sink:MaD:1 |
|
||||
| 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: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: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:26:10:26:11 | y2 | provenance | Sink:MaD:1 |
|
||||
| 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: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: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:33:10:33:11 | z2 | provenance | Sink:MaD:1 |
|
||||
| test.cpp:32:41:32:41 | x | test.cpp:7:47:7:52 | value2 | provenance | |
|
||||
@@ -133,10 +149,20 @@ edges
|
||||
| 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: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: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:59:55:59:64 | *& ... [x] | test.cpp:46:30:46:32 | *arg [x] | provenance | MaD:49 |
|
||||
| test.cpp:59:55:59:64 | *& ... [x] | test.cpp:52:5:52:18 | [summary param] *3 in pthread_create [x] | provenance | |
|
||||
| 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: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 |
|
||||
@@ -146,61 +172,69 @@ 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: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:97:26:97:26 | x | test.cpp:68:22:68:22 | y | provenance | MaD:47 |
|
||||
| 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:82:22:82:22 | y | provenance | MaD:47 |
|
||||
| test.cpp:104:62:104:62 | x | test.cpp:88:22:88:22 | y | provenance | MaD:47 |
|
||||
| test.cpp:97:26:97:26 | x | test.cpp:63:6:63:21 | [summary param] 1 in callWithArgument | provenance | |
|
||||
| test.cpp:101:26:101:26 | x | test.cpp:63:6:63:21 | [summary param] 1 in callWithArgument | provenance | |
|
||||
| test.cpp:103:63:103:63 | x | test.cpp:63:6:63:21 | [summary param] 1 in callWithArgument | provenance | |
|
||||
| test.cpp:104:62:104:62 | x | test.cpp:63:6:63:21 | [summary param] 1 in callWithArgument | provenance | |
|
||||
| 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: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: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: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: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:135:10:135:10 | y | provenance | Sink:MaD:1 |
|
||||
| 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: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: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: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:149:10:149:10 | z | provenance | Sink:MaD:1 |
|
||||
| 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: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: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: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:158:10:158:10 | z | provenance | Sink:MaD:1 |
|
||||
| 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: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:55 |
|
||||
| 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:165:12:165:64 | call to 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: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: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: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: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:172:13:172:44 | call to templateFunction3 | provenance | MaD:55 |
|
||||
| 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 |
|
||||
| test.cpp:172:51:172:51 | x | test.cpp:172:13:172:44 | call to templateFunction3 | provenance | MaD:53 |
|
||||
| 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 |
|
||||
| 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: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: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: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: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: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 | |
|
||||
@@ -217,11 +251,11 @@ edges
|
||||
| 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:56 | *... = ... | windows.cpp:190:5:190:14 | *overlapped [post update] [*hEvent] | provenance | |
|
||||
| windows.cpp:192:53:192:63 | *& ... [*hEvent] | windows.cpp:147:16:147:27 | *lpOverlapped [*hEvent] | provenance | MaD:37 |
|
||||
| windows.cpp:192:53:192:63 | *& ... [*hEvent] | windows.cpp:90:6:90:15 | [summary param] *3 in ReadFileEx [*hEvent] | provenance | |
|
||||
| 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:57 | ... = ... | windows.cpp:199:5:199:14 | *overlapped [post update] [hEvent] | provenance | |
|
||||
| windows.cpp:201:53:201:63 | *& ... [hEvent] | windows.cpp:157:16:157:27 | *lpOverlapped [hEvent] | provenance | MaD:37 |
|
||||
| windows.cpp:201:53:201:63 | *& ... [hEvent] | windows.cpp:90:6:90:15 | [summary param] *3 in ReadFileEx [hEvent] | provenance | |
|
||||
| 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:287:20:287:52 | *pMapView | provenance | |
|
||||
@@ -244,6 +278,12 @@ 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:333:20:333:52 | *pMapView | 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: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 | |
|
||||
@@ -258,9 +298,22 @@ edges
|
||||
| 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:9:431:14 | call to source | windows.cpp:431:3:431:16 | ... = ... | 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:410:26:410:36 | *lpParameter [x] | provenance | MaD:34 |
|
||||
| windows.cpp:464:7:464:8 | *& ... [x] | windows.cpp:417:26:417:36 | *lpParameter [x] | provenance | MaD:35 |
|
||||
| windows.cpp:439:7:439:8 | *& ... [x] | windows.cpp:349:8:349:19 | [summary param] *3 in CreateThread [x] | provenance | |
|
||||
| windows.cpp:451:7:451:8 | *& ... [x] | windows.cpp:357:8:357:25 | [summary param] *4 in CreateRemoteThread [x] | provenance | |
|
||||
| windows.cpp:464:7:464:8 | *& ... [x] | windows.cpp:387:8:387:27 | [summary param] *4 in CreateRemoteThreadEx [x] | provenance | |
|
||||
| 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:537:40:537:41 | *& ... | provenance | |
|
||||
| windows.cpp:533:11:533:16 | call to source | windows.cpp:542:38:542:39 | *& ... | provenance | |
|
||||
@@ -269,29 +322,37 @@ 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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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 |
|
||||
@@ -299,8 +360,10 @@ edges
|
||||
| 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: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: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: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 | |
|
||||
@@ -323,6 +386,8 @@ edges
|
||||
| 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 | |
|
||||
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:91:7:91:17 | recv_buffer | semmle.label | recv_buffer |
|
||||
| asio_streams.cpp:93:29:93:39 | *recv_buffer | semmle.label | *recv_buffer |
|
||||
@@ -333,6 +398,15 @@ nodes
|
||||
| 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: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:257:5:257:8 | *resp | semmle.label | *resp |
|
||||
@@ -377,6 +451,12 @@ 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 |
|
||||
| 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:47:7:52 | value2 | semmle.label | value2 |
|
||||
| test.cpp:7:64:7:69 | value2 | semmle.label | value2 |
|
||||
@@ -403,10 +483,20 @@ nodes
|
||||
| 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: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:18 | ... = ... | semmle.label | ... = ... |
|
||||
| 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: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:69:11:69:11 | y | semmle.label | y |
|
||||
| test.cpp:74:22:74:22 | y | semmle.label | y |
|
||||
@@ -421,18 +511,28 @@ nodes
|
||||
| test.cpp:101:26:101:26 | 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: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: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: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: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: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:148:10:148:27 | call to function | semmle.label | call to function |
|
||||
@@ -456,20 +556,8 @@ nodes
|
||||
| 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:173:10:173:10 | y | semmle.label | y |
|
||||
| test.cpp:186:2:186:2 | *s [post update] [myField] | semmle.label | *s [post update] [myField] |
|
||||
| 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:17:8:17:25 | [summary param] *0 in CommandLineToArgvA | semmle.label | [summary param] *0 in CommandLineToArgvA |
|
||||
| windows.cpp:17:8:17:25 | [summary] to write: ReturnValue[**] in CommandLineToArgvA | semmle.label | [summary] to write: ReturnValue[**] in CommandLineToArgvA |
|
||||
| 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 | * ... |
|
||||
@@ -482,6 +570,14 @@ nodes
|
||||
| windows.cpp:36:10:36:13 | * ... | semmle.label | * ... |
|
||||
| windows.cpp:39:36:39:38 | GetEnvironmentVariableA output argument | semmle.label | GetEnvironmentVariableA output argument |
|
||||
| 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:149:18:149:62 | *hEvent | semmle.label | *hEvent |
|
||||
| windows.cpp:149:18:149:62 | *hEvent | semmle.label | *hEvent |
|
||||
@@ -535,6 +631,12 @@ nodes
|
||||
| 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: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:405:10:405:25 | *lpParameter [x] | semmle.label | *lpParameter [x] |
|
||||
| windows.cpp:406:8:406:8 | *s [x] | semmle.label | *s [x] |
|
||||
@@ -553,6 +655,27 @@ nodes
|
||||
| windows.cpp:439:7:439: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: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:537:27:537:37 | RtlCopyVolatileMemory output argument | semmle.label | RtlCopyVolatileMemory output argument |
|
||||
@@ -597,6 +720,8 @@ nodes
|
||||
| windows.cpp:671:10:671:16 | * ... | semmle.label | * ... |
|
||||
| windows.cpp:673:10:673:29 | * ... | 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:12:728:28 | call to source | semmle.label | call to source |
|
||||
| windows.cpp:729:35:729:35 | *x | semmle.label | *x |
|
||||
@@ -625,6 +750,30 @@ nodes
|
||||
| windows.cpp:939:10:939:11 | * ... | semmle.label | * ... |
|
||||
| windows.cpp:941:10:941:31 | * ... | semmle.label | * ... |
|
||||
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: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 |
|
||||
| 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
|
||||
|
||||
@@ -21,6 +21,4 @@ extensions:
|
||||
- ["", "", False, "callWithNonTypeTemplate<T>", "(const T &)", "", "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"]
|
||||
- ["", "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"]
|
||||
- ["", "TemplateClass2<T,U>", True, "function", "(U,T)", "", "Argument[1]", "ReturnValue", "value", "manual"]
|
||||
@@ -19,5 +19,3 @@
|
||||
| test.cpp:149:10:149:10 | z | test-sink |
|
||||
| test.cpp:158:10:158:10 | z | 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 |
|
||||
|
||||
@@ -13,8 +13,6 @@
|
||||
| test.cpp:146:10:146: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: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:34:17:34:38 | *call to GetEnvironmentStringsA | local |
|
||||
| windows.cpp:39:36:39:38 | GetEnvironmentVariableA output argument | local |
|
||||
|
||||
@@ -171,32 +171,4 @@ void test_class1() {
|
||||
Class1<int> c;
|
||||
auto y = c.templateFunction3<unsigned long>(0UL, x);
|
||||
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
|
||||
}
|
||||
@@ -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] 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] read: Argument[0].Field[MyContainer::value]/Field[value] in madArg0FieldToReturn | | 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] 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] read: Argument[*0].Field[MyContainer::value]/Field[value] in madArg0IndirectFieldToReturn | | 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] 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] read: Argument[0].Field[*MyContainer::ptr]/Field[*ptr] in madArg0FieldIndirectToReturn | | 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] 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] to write: ReturnValue in madArg0ToReturnField | ReturnNode | madArg0ToReturnField | madArg0ToReturnField |
|
||||
| tests.cpp:163:13:163:32 | [summary] to write: ReturnValue.Field[MyContainer::value]/Field[value] in madArg0ToReturnField | | madArg0ToReturnField | madArg0ToReturnField |
|
||||
| tests.cpp:163:13:163:32 | [summary] to write: ReturnValue.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] to write: ReturnValue[*] in madArg0ToReturnIndirectField | ReturnNode | madArg0ToReturnIndirectField | madArg0ToReturnIndirectField |
|
||||
| tests.cpp:164:14:164:41 | [summary] to write: ReturnValue[*].Field[MyContainer::value]/Field[value] in madArg0ToReturnIndirectField | | madArg0ToReturnIndirectField | madArg0ToReturnIndirectField |
|
||||
| tests.cpp:164:14:164:41 | [summary] to write: ReturnValue[*].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] to write: ReturnValue in madArg0ToReturnFieldIndirect | ReturnNode | madArg0ToReturnFieldIndirect | madArg0ToReturnFieldIndirect |
|
||||
| tests.cpp:165:13:165:40 | [summary] to write: ReturnValue.Field[*MyContainer::ptr]/Field[*ptr] in madArg0ToReturnFieldIndirect | | madArg0ToReturnFieldIndirect | madArg0ToReturnFieldIndirect |
|
||||
| tests.cpp:165:13:165:40 | [summary] to write: ReturnValue.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] this in madArg0ToSelf | ParameterNode | 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] 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].Field[MyClass::val]/Field[val] in madArg0ToField | | madArg0ToField | madArg0ToField |
|
||||
| tests.cpp:287:7:287:20 | [summary] to write: Argument[this].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] read: Argument[this].Field[MyClass::val]/Field[val] in madFieldToReturn | | 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] 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] 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] 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.Field[first]/Field[intPair::first] in madCallArg0ReturnToReturnFirst | | madCallArg0ReturnToReturnFirst | madCallArg0ReturnToReturnFirst |
|
||||
| tests.cpp:435:9:435:38 | [summary] to write: ReturnValue.Field[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] 1 in madCallArg0WithValue | ParameterNode | madCallArg0WithValue | madCallArg0WithValue |
|
||||
| tests.cpp:436:6:436:25 | [summary] read: Argument[0].Parameter[0] in madCallArg0WithValue | PostUpdateNode | madCallArg0WithValue | madCallArg0WithValue |
|
||||
|
||||
@@ -11,10 +11,12 @@ edges
|
||||
| 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: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: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: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: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 | |
|
||||
@@ -58,6 +60,8 @@ nodes
|
||||
| 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: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:195:20:195:23 | StringCchPrintfW output argument | semmle.label | StringCchPrintfW output argument |
|
||||
| test.cpp:195:31:195:33 | *str | semmle.label | *str |
|
||||
@@ -93,6 +97,7 @@ nodes
|
||||
| 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 |
|
||||
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
|
||||
| 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 |
|
||||
|
||||
@@ -33,6 +33,7 @@ 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:17: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:143:34:143:45 | *message_data | provenance | |
|
||||
| tests2.cpp:134:17:134:22 | *call to getenv | tests2.cpp:134:2:134:30 | *... = ... | provenance | |
|
||||
@@ -40,6 +41,7 @@ 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: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: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 |
|
||||
| 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 | |
|
||||
@@ -76,6 +78,8 @@ nodes
|
||||
| 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: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:17:134:22 | *call to getenv | semmle.label | *call to getenv |
|
||||
| tests2.cpp:138:23:138:34 | *message_data | semmle.label | *message_data |
|
||||
@@ -96,3 +100,4 @@ nodes
|
||||
| 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 |
|
||||
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 |
|
||||
|
||||
@@ -88,12 +88,12 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
private IEnumerable<string> GetFeedsFromNugetConfig(string nugetConfigPath) =>
|
||||
GetFeeds(() => dotnet.GetNugetFeeds(nugetConfigPath));
|
||||
|
||||
public string FeedsToRestoreArgument(IEnumerable<string> feeds, string sourceArgumentPrefix)
|
||||
private string FeedsToRestoreArgument(IEnumerable<string> feeds)
|
||||
{
|
||||
// If there are no feeds, we want to override any default feeds that `restore` would use by passing a dummy source argument.
|
||||
// If there are no feeds, we want to override any default feeds that `dotnet restore` would use by passing a dummy source argument.
|
||||
if (!feeds.Any())
|
||||
{
|
||||
return $" {sourceArgumentPrefix} \"{emptyPackageDirectory.DirInfo.FullName}\"";
|
||||
return $" -s \"{emptyPackageDirectory.DirInfo.FullName}\"";
|
||||
}
|
||||
|
||||
// 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();
|
||||
foreach (var feed in feeds)
|
||||
{
|
||||
feedArgs.Append($" {sourceArgumentPrefix} \"{feed}\"");
|
||||
feedArgs.Append($" -s \"{feed}\"");
|
||||
}
|
||||
|
||||
return feedArgs.ToString();
|
||||
@@ -112,11 +112,17 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
/// (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/packages.config</param>
|
||||
/// <param name="path">Path to project/solution</param>
|
||||
/// <param name="reachableFeeds">The set of reachable NuGet feeds.</param>
|
||||
/// <returns>The list of NuGet feeds to use for this restore.</returns>
|
||||
public IEnumerable<string> FeedsToUse(string path, HashSet<string> reachableFeeds)
|
||||
/// <returns>A string representing the NuGet sources argument for the restore command.</returns>
|
||||
public string? MakeRestoreSourcesArgument(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.
|
||||
var folder = GetDirectoryName(path);
|
||||
var feedsToConsider = folder is not null ? GetFeedsFromFolder(folder).ToHashSet() : new HashSet<string>();
|
||||
@@ -130,28 +136,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
? feedsToConsider.Where(reachableFeeds.Contains)
|
||||
: feedsToConsider;
|
||||
|
||||
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");
|
||||
return FeedsToRestoreArgument(feedsToUse);
|
||||
}
|
||||
|
||||
private (int initialTimeout, int tryCount) GetFeedRequestSettings(bool isFallback)
|
||||
|
||||
@@ -110,55 +110,58 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
logger.LogInfo($"Checking NuGet feed responsiveness: {feedManager.CheckNugetFeedResponsiveness}");
|
||||
compilationInfoContainer.CompilationInfos.Add(("NuGet feed responsiveness checked", feedManager.CheckNugetFeedResponsiveness ? "1" : "0"));
|
||||
|
||||
HashSet<string> explicitFeeds = [];
|
||||
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
|
||||
{
|
||||
var packagesConfigRestore = PackagesConfigRestoreFactory.Create(fileProvider, legacyPackageDirectory, logger, feedManager, reachableFeeds);
|
||||
var count = packagesConfigRestore.InstallPackages();
|
||||
if (packagesConfigRestore.PackageCount > 0)
|
||||
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.
|
||||
(explicitFeeds, var allFeeds) = feedManager.GetAllFeeds();
|
||||
|
||||
if (feedManager.CheckNugetFeedResponsiveness)
|
||||
{
|
||||
compilationInfoContainer.CompilationInfos.Add(("packages.config files", packagesConfigRestore.PackageCount.ToString()));
|
||||
compilationInfoContainer.CompilationInfos.Add(("Successfully restored packages.config files", count.ToString()));
|
||||
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);
|
||||
}
|
||||
|
||||
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 nugetPackageDllPaths = nugetPackageDlls.Select(f => f.FullName).ToHashSet();
|
||||
@@ -236,7 +239,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
var projects = fileProvider.Solutions.SelectMany(solution =>
|
||||
{
|
||||
logger.LogInfo($"Restoring solution {solution}...");
|
||||
var nugetSources = feedManager.MakeDotnetRestoreSourcesArgument(solution, reachableFeeds);
|
||||
var nugetSources = feedManager.MakeRestoreSourcesArgument(solution, reachableFeeds);
|
||||
var res = dotnet.Restore(new(solution, PackageDirectory.DirInfo.FullName, ForceDotnetRefAssemblyFetching: true, NugetSources: nugetSources, TargetWindows: isWindows));
|
||||
if (res.Success)
|
||||
{
|
||||
@@ -285,7 +288,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
foreach (var project in projectGroup)
|
||||
{
|
||||
logger.LogInfo($"Restoring project {project}...");
|
||||
var nugetSources = feedManager.MakeDotnetRestoreSourcesArgument(project, reachableFeeds);
|
||||
var nugetSources = feedManager.MakeRestoreSourcesArgument(project, reachableFeeds);
|
||||
var res = dotnet.Restore(new(project, PackageDirectory.DirInfo.FullName, ForceDotnetRefAssemblyFetching: true, NugetSources: nugetSources, TargetWindows: isWindows));
|
||||
assets.AddDependenciesRange(res.AssetsFilePaths);
|
||||
lock (sync)
|
||||
|
||||
@@ -7,7 +7,7 @@ using Semmle.Util;
|
||||
|
||||
namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
{
|
||||
internal interface IPackagesConfigRestore
|
||||
internal interface IPackagesConfigRestore : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// The number of packages.config files found in the source tree.
|
||||
@@ -33,11 +33,11 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
/// </summary>
|
||||
internal class PackagesConfigRestoreFactory
|
||||
{
|
||||
public static IPackagesConfigRestore Create(FileProvider fileProvider, DependencyDirectory packageDirectory, Semmle.Util.Logging.ILogger logger, FeedManager feedManager, HashSet<string> reachableFeeds)
|
||||
public static IPackagesConfigRestore Create(FileProvider fileProvider, DependencyDirectory packageDirectory, Semmle.Util.Logging.ILogger logger, Func<bool> useDefaultFeed)
|
||||
{
|
||||
if (SystemBuildActions.Instance.IsWindows() || SystemBuildActions.Instance.IsMonoInstalled())
|
||||
{
|
||||
return new NugetExeWrapper(fileProvider, packageDirectory, logger, feedManager, reachableFeeds);
|
||||
return new NugetExeWrapper(fileProvider, packageDirectory, logger, useDefaultFeed);
|
||||
}
|
||||
|
||||
return new NoOpPackagesConfig(fileProvider.PackagesConfigs, logger);
|
||||
@@ -55,6 +55,8 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
|
||||
public int PackageCount => fileProvider.PackagesConfigs.Count;
|
||||
|
||||
private readonly string? backupNugetConfig;
|
||||
private readonly string? nugetConfigPath;
|
||||
private readonly FileProvider fileProvider;
|
||||
|
||||
/// <summary>
|
||||
@@ -63,30 +65,57 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
/// so as to not trample the source tree.
|
||||
/// </summary>
|
||||
private readonly DependencyDirectory packageDirectory;
|
||||
private readonly FeedManager feedManager;
|
||||
private readonly HashSet<string> reachableFeeds;
|
||||
|
||||
private bool IsWindows => SystemBuildActions.Instance.IsWindows();
|
||||
|
||||
private bool? isDefaultFeedReachable;
|
||||
private bool IsDefaultFeedReachable =>
|
||||
isDefaultFeedReachable ??= feedManager.IsDefaultFeedReachable();
|
||||
|
||||
/// <summary>
|
||||
/// Create the package manager for a specified source tree.
|
||||
/// </summary>
|
||||
public NugetExeWrapper(FileProvider fileProvider, DependencyDirectory packageDirectory, Semmle.Util.Logging.ILogger logger, FeedManager feedManager, HashSet<string> reachableFeeds)
|
||||
public NugetExeWrapper(FileProvider fileProvider, DependencyDirectory packageDirectory, Semmle.Util.Logging.ILogger logger, Func<bool> useDefaultFeed)
|
||||
{
|
||||
this.fileProvider = fileProvider;
|
||||
this.packageDirectory = packageDirectory;
|
||||
this.logger = logger;
|
||||
this.feedManager = feedManager;
|
||||
this.reachableFeeds = reachableFeeds;
|
||||
|
||||
if (fileProvider.PackagesConfigs.Count > 0)
|
||||
{
|
||||
logger.LogInfo($"Found packages.config files, trying to use nuget.exe for package restore");
|
||||
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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,21 +198,6 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
{
|
||||
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.
|
||||
* 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
|
||||
@@ -194,12 +208,12 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
if (RunWithMono)
|
||||
{
|
||||
exe = "mono";
|
||||
args = $"\"{nugetExe}\" install -OutputDirectory \"{packageDirectory}\" {sourcesArgument} \"{packagesConfig}\"";
|
||||
args = $"\"{nugetExe}\" install -OutputDirectory \"{packageDirectory}\" \"{packagesConfig}\"";
|
||||
}
|
||||
else
|
||||
{
|
||||
exe = nugetExe!;
|
||||
args = $"install -OutputDirectory \"{packageDirectory}\" {sourcesArgument} \"{packagesConfig}\"";
|
||||
args = $"install -OutputDirectory \"{packageDirectory}\" \"{packagesConfig}\"";
|
||||
}
|
||||
|
||||
var pi = new ProcessStartInfo(exe, args)
|
||||
@@ -232,6 +246,98 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
{
|
||||
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
|
||||
@@ -255,6 +361,8 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void Dispose() { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
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).
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
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.
|
||||
@@ -27,27 +27,3 @@ extensions:
|
||||
- ["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, "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"]
|
||||
|
||||
@@ -37,9 +37,4 @@ func slogTest() {
|
||||
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.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
|
||||
}
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
reverseRead
|
||||
| test.go:114:21:114:33 | call to Group | Origin of readStep is missing a PostUpdateNode. |
|
||||
@@ -1,2 +0,0 @@
|
||||
invalidModelRow
|
||||
testFailures
|
||||
@@ -1,14 +0,0 @@
|
||||
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>
|
||||
@@ -1,3 +0,0 @@
|
||||
module codeql-go-tests/frameworks/slog
|
||||
|
||||
go 1.26
|
||||
@@ -1,115 +0,0 @@
|
||||
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"
|
||||
}
|
||||
@@ -29,3 +29,8 @@ nodes
|
||||
| BadMacUse.java:146:48:146:57 | ciphertext : byte[] | semmle.label | ciphertext : byte[] |
|
||||
| BadMacUse.java:152:42:152:51 | ciphertext | semmle.label | ciphertext |
|
||||
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 |
|
||||
|
||||
@@ -30,3 +30,8 @@ nodes
|
||||
| BadMacUse.java:118:83:118:84 | iv : byte[] | semmle.label | iv : byte[] |
|
||||
| BadMacUse.java:124:42:124:51 | ciphertext | semmle.label | ciphertext |
|
||||
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 |
|
||||
|
||||
@@ -44,3 +44,8 @@ nodes
|
||||
| BadMacUse.java:146:48:146:57 | ciphertext : byte[] [[]] : Object | semmle.label | ciphertext : byte[] [[]] : Object |
|
||||
| BadMacUse.java:152:42:152:51 | ciphertext | semmle.label | ciphertext |
|
||||
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 |
|
||||
|
||||
@@ -47,7 +47,7 @@ class BadMacUse {
|
||||
SecretKey encryptionKey = new SecretKeySpec(encryptionKeyBytes, "AES");
|
||||
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
|
||||
cipher.init(Cipher.DECRYPT_MODE, encryptionKey, new SecureRandom());
|
||||
byte[] plaintext = cipher.doFinal(ciphertext); // $ Source[java/quantum/examples/bad-mac-order-decrypt-to-mac]
|
||||
byte[] plaintext = cipher.doFinal(ciphertext); // $ Source
|
||||
|
||||
// Now verify MAC (too late)
|
||||
SecretKey macKey = new SecretKeySpec(macKeyBytes, "HmacSHA256");
|
||||
@@ -60,7 +60,7 @@ class BadMacUse {
|
||||
}
|
||||
}
|
||||
|
||||
public void BadMacOnPlaintext(byte[] encryptionKeyBytes, byte[] macKeyBytes, byte[] plaintext) throws Exception {// $ Source[java/quantum/examples/bad-mac-order-encrypt-plaintext-also-in-mac]
|
||||
public void BadMacOnPlaintext(byte[] encryptionKeyBytes, byte[] macKeyBytes, byte[] plaintext) throws Exception {// $ Source
|
||||
// Create keys directly from provided byte arrays
|
||||
SecretKey encryptionKey = new SecretKeySpec(encryptionKeyBytes, "AES");
|
||||
SecretKey macKey = new SecretKeySpec(macKeyBytes, "HmacSHA256");
|
||||
@@ -89,7 +89,7 @@ class BadMacUse {
|
||||
|
||||
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
|
||||
cipher.init(mode, secretKeySpec, ivParameterSpec);
|
||||
return cipher.doFinal(bytes); // $ Source[java/quantum/examples/bad-mac-order-decrypt-then-mac] Source[java/quantum/examples/bad-mac-order-decrypt-to-mac]
|
||||
return cipher.doFinal(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -121,7 +121,7 @@ class BadMacUse {
|
||||
SecretKey macKey = new SecretKeySpec(macKeyBytes, "HmacSHA256");
|
||||
Mac mac = Mac.getInstance("HmacSHA256");
|
||||
mac.init(macKey);
|
||||
byte[] computedMac = mac.doFinal(ciphertext); // $ SPURIOUS: Alert[java/quantum/examples/bad-mac-order-decrypt-to-mac]
|
||||
byte[] computedMac = mac.doFinal(ciphertext); // False Positive
|
||||
|
||||
// Concatenate ciphertext and MAC
|
||||
byte[] output = new byte[ciphertext.length + computedMac.length];
|
||||
@@ -136,20 +136,20 @@ class BadMacUse {
|
||||
* The function decrypts THEN computes the MAC on the plaintext.
|
||||
* It should have the MAC computed on the ciphertext first.
|
||||
*/
|
||||
public void decryptThenMac(byte[] encryptionKeyBytes, byte[] macKeyBytes, byte[] input) throws Exception { // $ SPURIOUS: Source[java/quantum/examples/bad-mac-order-encrypt-plaintext-also-in-mac]
|
||||
public void decryptThenMac(byte[] encryptionKeyBytes, byte[] macKeyBytes, byte[] input) throws Exception {
|
||||
// Split input into ciphertext and MAC
|
||||
int macLength = 32; // HMAC-SHA256 output length
|
||||
byte[] ciphertext = Arrays.copyOfRange(input, 0, input.length - macLength);
|
||||
byte[] receivedMac = Arrays.copyOfRange(input, input.length - macLength, input.length);
|
||||
|
||||
// Decrypt first (unsafe)
|
||||
byte[] plaintext = decryptUsingWrapper(ciphertext, encryptionKeyBytes, new byte[16]);
|
||||
byte[] plaintext = decryptUsingWrapper(ciphertext, encryptionKeyBytes, new byte[16]); // $ Source
|
||||
|
||||
// Now verify MAC (too late)
|
||||
SecretKey macKey = new SecretKeySpec(macKeyBytes, "HmacSHA256");
|
||||
Mac mac = Mac.getInstance("HmacSHA256");
|
||||
mac.init(macKey);
|
||||
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]
|
||||
byte[] computedMac = mac.doFinal(ciphertext); // $ Alert[java/quantum/examples/bad-mac-order-decrypt-then-mac], False positive for Plaintext reuse
|
||||
|
||||
if (!MessageDigest.isEqual(receivedMac, computedMac)) {
|
||||
throw new SecurityException("MAC verification failed");
|
||||
|
||||
@@ -126,3 +126,5 @@ nodes
|
||||
| InsecureIVorNonceSource.java:202:54:202:55 | iv : byte[] | semmle.label | iv : byte[] |
|
||||
| InsecureIVorNonceSource.java:206:51:206:56 | ivSpec | semmle.label | ivSpec |
|
||||
subpaths
|
||||
testFailures
|
||||
| InsecureIVorNonceSource.java:42:21:42:21 | 1 : Number | Unexpected result: Source |
|
||||
|
||||
@@ -39,7 +39,7 @@ public class InsecureIVorNonceSource {
|
||||
public byte[] encryptWithStaticIvByteArray(byte[] key, byte[] plaintext) throws Exception {
|
||||
byte[] iv = new byte[16];
|
||||
for (byte i = 0; i < iv.length; i++) {
|
||||
iv[i] = 1; // $ Source[java/quantum/examples/insecure-iv-or-nonce]
|
||||
iv[i] = 1;
|
||||
}
|
||||
|
||||
IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
||||
|
||||
@@ -40,11 +40,11 @@ public class Test {
|
||||
* SAST/CBOM: - Parent: PBKDF2. - Iteration count is only 10, which is far
|
||||
* below acceptable security standards. - Flagged as insecure.
|
||||
*/
|
||||
public void pbkdf2LowIteration(String password, int iterationCount) throws Exception { // $ Source[java/quantum/examples/unknown-kdf-iteration-count]
|
||||
public void pbkdf2LowIteration(String password, int iterationCount) throws Exception { // $ Source
|
||||
byte[] salt = generateSalt(16);
|
||||
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, iterationCount, 256);
|
||||
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, iterationCount, 256); // $ Alert[java/quantum/examples/unknown-kdf-iteration-count]
|
||||
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
|
||||
byte[] key = factory.generateSecret(spec).getEncoded(); // $ Alert[java/quantum/examples/unknown-kdf-iteration-count]
|
||||
byte[] key = factory.generateSecret(spec).getEncoded();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1 +1,5 @@
|
||||
#select
|
||||
| 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 |
|
||||
|
||||
@@ -12,3 +12,5 @@ nodes
|
||||
| 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 |
|
||||
subpaths
|
||||
testFailures
|
||||
| Test.java:43:92:43:102 | // $ Source | Missing result: Source |
|
||||
|
||||
2
python/ql/consistency-queries/CfgConsistency.ql
Normal file
2
python/ql/consistency-queries/CfgConsistency.ql
Normal file
@@ -0,0 +1,2 @@
|
||||
import semmle.python.controlflow.internal.AstNodeImpl
|
||||
import ControlFlow::Consistency
|
||||
4
python/ql/lib/change-notes/2026-05-19-add-shared-cfg.md
Normal file
4
python/ql/lib/change-notes/2026-05-19-add-shared-cfg.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
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.
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
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.
|
||||
42
python/ql/lib/ide-contextual-queries/printCfg.ql
Normal file
42
python/ql/lib/ide-contextual-queries/printCfg.ql
Normal file
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* @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>
|
||||
1771
python/ql/lib/semmle/python/controlflow/internal/AstNodeImpl.qll
Normal file
1771
python/ql/lib/semmle/python/controlflow/internal/AstNodeImpl.qll
Normal file
File diff suppressed because it is too large
Load Diff
1022
python/ql/lib/semmle/python/controlflow/internal/Cfg.qll
Normal file
1022
python/ql/lib/semmle/python/controlflow/internal/Cfg.qll
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,4 @@
|
||||
consistencyOverview
|
||||
| deadEnd | 1 |
|
||||
deadEnd
|
||||
| without_loop.py:7:5:7:9 | Break |
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Phase -1 of the dataflow CFG migration: verifies that every variable
|
||||
* binding visible to the AST (`Name.defines(v)`) corresponds to a CFG node
|
||||
* in the new CFG (`semmle.python.controlflow.internal.AstNodeImpl`).
|
||||
*
|
||||
* The expected tag is `cfgdefines=<name>`. Each binding annotation in the
|
||||
* test sources looks like `# $ cfgdefines=x` for a binding currently
|
||||
* covered by the new CFG, or `# $ MISSING: cfgdefines=x` for a binding
|
||||
* that is known to be uncovered (a "red" test case that should be
|
||||
* green-flipped once the corresponding `cfg-ext-*` extension lands).
|
||||
*/
|
||||
|
||||
import python
|
||||
import semmle.python.controlflow.internal.AstNodeImpl as CfgImpl
|
||||
import utils.test.InlineExpectationsTest
|
||||
|
||||
module CfgBindingsTest implements TestSig {
|
||||
string getARelevantTag() { result = "cfgdefines" }
|
||||
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(Name n, Variable v, CfgImpl::ControlFlowNode cfg |
|
||||
n.defines(v) and
|
||||
cfg.getAstNode().asExpr() = n and
|
||||
location = n.getLocation() and
|
||||
element = n.toString() and
|
||||
tag = "cfgdefines" and
|
||||
value = v.getId()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import MakeTest<CfgBindingsTest>
|
||||
@@ -0,0 +1,13 @@
|
||||
# Annotated assignment (PEP 526). Both with and without an initializer.
|
||||
|
||||
a: int = 1 # $ cfgdefines=a
|
||||
b: str = "hi" # $ cfgdefines=b
|
||||
|
||||
# Annotation without value: the AST records `c` as defined,
|
||||
# and the new CFG now visits it via the AnnAssignStmt wrapper.
|
||||
c: int # $ cfgdefines=c
|
||||
|
||||
class K: # $ cfgdefines=K
|
||||
field: int = 0 # $ cfgdefines=field
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
# Compound (tuple/list) assignment targets — actually wired in the new CFG.
|
||||
|
||||
a, b = (1, 2) # $ cfgdefines=a cfgdefines=b
|
||||
[c, d] = [3, 4] # $ cfgdefines=c cfgdefines=d
|
||||
|
||||
# Nested unpacking.
|
||||
(e, (f, g)) = (1, (2, 3)) # $ cfgdefines=e cfgdefines=f cfgdefines=g
|
||||
|
||||
# Star unpacking.
|
||||
h, *i = [1, 2, 3] # $ cfgdefines=h cfgdefines=i
|
||||
|
||||
# Chained assignment with compound target.
|
||||
j = k, l = (5, 6) # $ cfgdefines=j cfgdefines=k cfgdefines=l
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
# Comprehension and `for` loop targets — wired in the new CFG.
|
||||
# Comprehensions are nested function scopes with a synthetic `.0` parameter
|
||||
# bound to the iterable.
|
||||
|
||||
# Bare-name `for` target.
|
||||
for i in range(3): # $ cfgdefines=i
|
||||
pass
|
||||
|
||||
# Compound `for` target.
|
||||
for k, v in [(1, 2)]: # $ cfgdefines=k cfgdefines=v
|
||||
pass
|
||||
|
||||
# Comprehension targets.
|
||||
_ = [x for x in range(3)] # $ cfgdefines=_ cfgdefines=x cfgdefines=.0
|
||||
_ = {y: z for y, z in []} # $ cfgdefines=_ cfgdefines=y cfgdefines=z cfgdefines=.0
|
||||
_ = (a for a in []) # $ cfgdefines=_ cfgdefines=a cfgdefines=.0
|
||||
|
||||
# Nested comprehensions.
|
||||
_ = [b for c in [] for b in c] # $ cfgdefines=_ cfgdefines=c cfgdefines=b cfgdefines=.0
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
# Reachability of code following a try whose body always returns.
|
||||
#
|
||||
# The new CFG models exception edges for raise-prone expressions when
|
||||
# they appear inside a `try` (or `with`) statement, mirroring Java's
|
||||
# `mayThrow`. This means the body of a `try` has both a normal
|
||||
# completion edge and an exception edge to its handlers, so code
|
||||
# following the try-statement is reachable via the except-handler path
|
||||
# even when the try-body would otherwise always return.
|
||||
#
|
||||
# Code that is not reachable under either normal or exception flow
|
||||
# (for example, the `else` clause of a try whose body unconditionally
|
||||
# raises) remains correctly classified as dead.
|
||||
|
||||
|
||||
def f(obj): # $ cfgdefines=f cfgdefines=obj
|
||||
try:
|
||||
return len(obj)
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
# The try-body always returns, but `len(obj)` can raise (it is
|
||||
# inside the try, so we model its exception edge). The
|
||||
# `except TypeError: pass` handler falls through to here, making
|
||||
# the code below reachable.
|
||||
try:
|
||||
hint = type(obj).__length_hint__ # $ cfgdefines=hint
|
||||
except AttributeError:
|
||||
return None
|
||||
return hint
|
||||
|
||||
|
||||
def g(): # $ cfgdefines=g
|
||||
try:
|
||||
raise Exception("inner")
|
||||
except:
|
||||
raise Exception("outer")
|
||||
else:
|
||||
# Unreachable: the inner try body always raises (via an explicit
|
||||
# `raise`, which is modelled unconditionally), so the `else:`
|
||||
# clause never runs.
|
||||
hit_inner_else = True
|
||||
|
||||
|
||||
def h(cache, key): # $ cfgdefines=h cfgdefines=cache cfgdefines=key
|
||||
try:
|
||||
return cache[key]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
# Same pattern as `f`: reachable via the except-handler fall-through.
|
||||
value = compute(key) # $ cfgdefines=value
|
||||
cache[key] = value
|
||||
return value
|
||||
@@ -0,0 +1,30 @@
|
||||
# Decorated `def`/`class` — wired in the new CFG.
|
||||
|
||||
|
||||
def deco(f): # $ cfgdefines=deco cfgdefines=f
|
||||
return f
|
||||
|
||||
|
||||
@deco
|
||||
def decorated_func(): # $ cfgdefines=decorated_func
|
||||
pass
|
||||
|
||||
|
||||
@deco
|
||||
class DecoratedClass: # $ cfgdefines=DecoratedClass
|
||||
pass
|
||||
|
||||
|
||||
# Stacked decorators.
|
||||
@deco
|
||||
@deco
|
||||
def doubly(): # $ cfgdefines=doubly
|
||||
pass
|
||||
|
||||
|
||||
# Inside a class body.
|
||||
class Outer: # $ cfgdefines=Outer
|
||||
@staticmethod
|
||||
def inner(): # $ cfgdefines=inner
|
||||
pass
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
# Exception-handler name bindings. These are already wired in the new
|
||||
# CFG provided the try body can raise; `raise` statements are reliably
|
||||
# treated as exception sources.
|
||||
|
||||
try:
|
||||
raise ValueError("oops")
|
||||
except ValueError as e: # $ cfgdefines=e
|
||||
pass
|
||||
|
||||
try:
|
||||
raise TypeError("oops")
|
||||
except (TypeError, KeyError) as err: # $ cfgdefines=err
|
||||
pass
|
||||
|
||||
# Exception groups (Python 3.11+).
|
||||
try:
|
||||
raise ValueError("oops")
|
||||
except* ValueError as eg: # $ cfgdefines=eg
|
||||
pass
|
||||
14
python/ql/test/library-tests/ControlFlow/bindings/imports.py
Normal file
14
python/ql/test/library-tests/ControlFlow/bindings/imports.py
Normal file
@@ -0,0 +1,14 @@
|
||||
# Import aliases — all bound names below are now reachable via the new
|
||||
# CFG's `ImportStmt` wrapper.
|
||||
|
||||
import os # $ cfgdefines=os
|
||||
import os.path # $ cfgdefines=os
|
||||
import os as o # $ cfgdefines=o
|
||||
from os import path # $ cfgdefines=path
|
||||
from os import path as p # $ cfgdefines=p
|
||||
from os import sep, linesep # $ cfgdefines=sep cfgdefines=linesep
|
||||
from os import (
|
||||
getcwd, # $ cfgdefines=getcwd
|
||||
getcwdb, # $ cfgdefines=getcwdb
|
||||
)
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
# Match-statement pattern bindings — wired in the new CFG.
|
||||
|
||||
def f(subject): # $ cfgdefines=f cfgdefines=subject
|
||||
match subject:
|
||||
case x: # $ cfgdefines=x
|
||||
pass
|
||||
case [a, b]: # $ cfgdefines=a cfgdefines=b
|
||||
pass
|
||||
case {"k": v}: # $ cfgdefines=v
|
||||
pass
|
||||
case Point(p, q): # $ cfgdefines=p cfgdefines=q
|
||||
pass
|
||||
case [_, *rest]: # $ cfgdefines=rest
|
||||
pass
|
||||
case (1 | 2) as n: # $ cfgdefines=n
|
||||
pass
|
||||
|
||||
|
||||
class Point: # $ cfgdefines=Point
|
||||
__match_args__ = ("x", "y") # $ cfgdefines=__match_args__
|
||||
x: int # $ cfgdefines=x
|
||||
y: int # $ cfgdefines=y
|
||||
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
# Function parameters.
|
||||
|
||||
def positional(a, b): # $ cfgdefines=positional cfgdefines=a cfgdefines=b
|
||||
pass
|
||||
|
||||
|
||||
def with_default(x=1, y=2): # $ cfgdefines=with_default cfgdefines=x cfgdefines=y
|
||||
pass
|
||||
|
||||
|
||||
def with_vararg(*args): # $ cfgdefines=with_vararg cfgdefines=args
|
||||
pass
|
||||
|
||||
|
||||
def with_kwarg(**kwargs): # $ cfgdefines=with_kwarg cfgdefines=kwargs
|
||||
pass
|
||||
|
||||
|
||||
def with_kwonly(*, k1, k2=5): # $ cfgdefines=with_kwonly cfgdefines=k1 cfgdefines=k2
|
||||
pass
|
||||
|
||||
|
||||
def kitchen_sink(a, b=2, *args, k1, k2=5, **kw): # $ cfgdefines=kitchen_sink cfgdefines=a cfgdefines=b cfgdefines=args cfgdefines=k1 cfgdefines=k2 cfgdefines=kw
|
||||
pass
|
||||
|
||||
|
||||
# Methods get `self` / `cls`.
|
||||
class C: # $ cfgdefines=C
|
||||
def method(self, x): # $ cfgdefines=method cfgdefines=self cfgdefines=x
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def cmethod(cls, x): # $ cfgdefines=cmethod cfgdefines=cls cfgdefines=x
|
||||
pass
|
||||
|
||||
|
||||
# Lambda parameter.
|
||||
_ = lambda p: p + 1 # $ cfgdefines=_ cfgdefines=p
|
||||
|
||||
# PEP 570 positional-only.
|
||||
def pos_only(a, b, /, c): # $ cfgdefines=pos_only cfgdefines=a cfgdefines=b cfgdefines=c
|
||||
pass
|
||||
14
python/ql/test/library-tests/ControlFlow/bindings/simple.py
Normal file
14
python/ql/test/library-tests/ControlFlow/bindings/simple.py
Normal file
@@ -0,0 +1,14 @@
|
||||
# Simple bindings that should already work in the new CFG.
|
||||
# No MISSING annotations expected.
|
||||
|
||||
x = 1 # $ cfgdefines=x
|
||||
y = x + 1 # $ cfgdefines=y
|
||||
|
||||
def f(): # $ cfgdefines=f
|
||||
pass
|
||||
|
||||
class C: # $ cfgdefines=C
|
||||
pass
|
||||
|
||||
# Re-assignment.
|
||||
x = 2 # $ cfgdefines=x
|
||||
@@ -0,0 +1,21 @@
|
||||
# PEP 695 type parameters (Python 3.12+).
|
||||
|
||||
# PEP 695 type-param names on `def`/`class` bind in an annotation scope
|
||||
# that nests the function/class body — they have no CFG node in the
|
||||
# enclosing scope (matching the legacy CFG).
|
||||
def func[T](x: T) -> T: # $ cfgdefines=func cfgdefines=x
|
||||
return x
|
||||
|
||||
|
||||
class Box[T]: # $ cfgdefines=Box
|
||||
item: T # $ cfgdefines=item
|
||||
|
||||
|
||||
# Multi-parameter, with bound and variadics.
|
||||
def multi[T: int, *Ts, **P](x: T, *args: *Ts, **kwargs: P.kwargs) -> T: # $ cfgdefines=multi cfgdefines=x cfgdefines=args cfgdefines=kwargs
|
||||
return x
|
||||
|
||||
|
||||
# `type` statement (PEP 695).
|
||||
type Alias[T] = list[T] # $ cfgdefines=Alias cfgdefines=T
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
# Walrus and starred-target edge cases — wired in the new CFG.
|
||||
|
||||
# Walrus in expression context.
|
||||
if (y := 5) > 0: # $ cfgdefines=y
|
||||
pass
|
||||
|
||||
# Walrus in a comprehension. The comprehension introduces a synthetic
|
||||
# `.0` parameter bound to the iterable.
|
||||
_ = [w for _ in range(3) if (w := 1)] # $ cfgdefines=_ cfgdefines=w cfgdefines=.0
|
||||
|
||||
# Starred target in a Tuple LHS.
|
||||
*head, tail = [1, 2, 3] # $ cfgdefines=head cfgdefines=tail
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
# `with cm() as x:` bindings — wired in the new CFG.
|
||||
|
||||
class CM: # $ cfgdefines=CM
|
||||
def __enter__(self): return self # $ cfgdefines=__enter__ cfgdefines=self
|
||||
def __exit__(self, *a): pass # $ cfgdefines=__exit__ cfgdefines=self cfgdefines=a
|
||||
|
||||
with CM() as x: # $ cfgdefines=x
|
||||
pass
|
||||
|
||||
# Multiple items.
|
||||
with CM() as a, CM() as b: # $ cfgdefines=a cfgdefines=b
|
||||
pass
|
||||
|
||||
# Parenthesised form (Python 3.10+).
|
||||
with (CM() as p, CM() as q): # $ cfgdefines=p cfgdefines=q
|
||||
pass
|
||||
|
||||
# Compound target in `with`.
|
||||
with CM() as (m, n): # $ cfgdefines=m cfgdefines=n
|
||||
pass
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
/** New-CFG version of AllLiveReachable. */
|
||||
|
||||
import python
|
||||
import TimerUtils
|
||||
import NewCfgImpl
|
||||
|
||||
private module Utils = EvalOrderCfgUtils<NewCfg>;
|
||||
|
||||
private import Utils
|
||||
private import Utils::CfgTests
|
||||
|
||||
from TimerCfgNode a, TestFunction f
|
||||
where allLiveReachable(a, f)
|
||||
select a, "Unreachable live annotation; entry of $@ does not reach this node", f, f.getName()
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* New-CFG version of AnnotationHasCfgNode.
|
||||
*
|
||||
* Checks that every timer annotation has a corresponding CFG node.
|
||||
*/
|
||||
|
||||
import python
|
||||
import TimerUtils
|
||||
import NewCfgImpl
|
||||
|
||||
private module Utils = EvalOrderCfgUtils<NewCfg>;
|
||||
|
||||
private import Utils::CfgTests
|
||||
|
||||
from TimerAnnotation ann
|
||||
where annotationWithoutCfgNode(ann)
|
||||
select ann, "Annotation in $@ has no CFG node", ann.getTestFunction(),
|
||||
ann.getTestFunction().getName()
|
||||
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* New-CFG version of BasicBlockAnnotationGap.
|
||||
*
|
||||
* Original:
|
||||
* Checks that within a basic block, if a node is annotated then its
|
||||
* successor is also annotated (or excluded). A gap in annotations
|
||||
* within a basic block indicates a missing annotation, since there
|
||||
* are no branches to justify the gap.
|
||||
*
|
||||
* Nodes with exceptional successors are excluded, as the exception
|
||||
* edge leaves the basic block and the normal successor may be dead.
|
||||
*/
|
||||
|
||||
import python
|
||||
import TimerUtils
|
||||
import NewCfgImpl
|
||||
|
||||
private module Utils = EvalOrderCfgUtils<NewCfg>;
|
||||
|
||||
private import Utils
|
||||
private import Utils::CfgTests
|
||||
|
||||
from TimerCfgNode a, CfgNode succ
|
||||
where basicBlockAnnotationGap(a, succ)
|
||||
select a, "Annotated node followed by unannotated $@ in the same basic block", succ,
|
||||
succ.getNode().toString()
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* New-CFG version of BasicBlockOrdering.
|
||||
*
|
||||
* Original:
|
||||
* Checks that within a single basic block, annotations appear in
|
||||
* increasing minimum-timestamp order.
|
||||
*/
|
||||
|
||||
import python
|
||||
import TimerUtils
|
||||
import NewCfgImpl
|
||||
|
||||
private module Utils = EvalOrderCfgUtils<NewCfg>;
|
||||
|
||||
private import Utils
|
||||
private import Utils::CfgTests
|
||||
|
||||
from TimerCfgNode a, TimerCfgNode b, int minA, int minB
|
||||
where basicBlockOrdering(a, b, minA, minB)
|
||||
select a, "Basic block ordering: $@ appears before $@", a.getTimestampExpr(minA),
|
||||
"timestamp " + minA, b.getTimestampExpr(minB), "timestamp " + minB
|
||||
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* New-CFG version of BranchTimestamps.
|
||||
*
|
||||
* Checks that when a node has both a true and false successor, the
|
||||
* live timestamps on one branch are marked as dead on the other.
|
||||
* This ensures that boolean branches are fully annotated with dead()
|
||||
* markers for the paths not taken.
|
||||
*
|
||||
* Limitation: the `@ t[ts, ...]` / `dead(ts)` annotation scheme can only
|
||||
* model branch-dead-ness for plain boolean control flow that reconverges
|
||||
* linearly after the split — i.e. `if`-with-else and `if`-expression.
|
||||
* It cannot model:
|
||||
*
|
||||
* * loops (`while` / `for`): body timestamps repeat across iterations,
|
||||
* so the loop-exit annotation can't list them as dead;
|
||||
* * `match` statements: each `case` body is a syntactically distinct
|
||||
* sub-tree, and the branches don't reconverge through a common
|
||||
* annotation point in the timeline;
|
||||
* * `try` / `with` and `raise` / `assert`: exception edges are modelled
|
||||
* as true/false but flow to syntactically distinct handlers, with no
|
||||
* reconvergence in the linear annotation order;
|
||||
* * short-circuit `and` / `or` (`BoolExpr`): the branches reconverge at
|
||||
* the BoolExpr's after-node, so timestamps on one branch are live
|
||||
* downstream of the other rather than dead;
|
||||
* * `if` without an `else` clause, and `if`/`elif` chains: the false
|
||||
* branch reconverges with the true branch at the post-if statement
|
||||
* (no-else) or fans out across multiple elif-test annotations,
|
||||
* neither of which fit the binary annotation scheme.
|
||||
*
|
||||
* Branch nodes inside those constructs are therefore whitelisted out
|
||||
* below. The check still fires (and is useful) for plain `if`/`else`
|
||||
* and conditional-expression branching.
|
||||
*/
|
||||
|
||||
import python
|
||||
import TimerUtils
|
||||
import NewCfgImpl
|
||||
|
||||
private module Utils = EvalOrderCfgUtils<NewCfg>;
|
||||
|
||||
private import Utils
|
||||
private import Utils::CfgTests
|
||||
|
||||
/**
|
||||
* Holds if `f` contains a construct whose branches the linear-timestamp
|
||||
* annotation scheme cannot describe (see file-level comment).
|
||||
*/
|
||||
private predicate hasUnmodellableBranching(Function f) {
|
||||
exists(AstNode bad |
|
||||
bad.getScope() = f and
|
||||
(
|
||||
bad instanceof While
|
||||
or
|
||||
bad instanceof For
|
||||
or
|
||||
bad instanceof MatchStmt
|
||||
or
|
||||
bad instanceof Try
|
||||
or
|
||||
bad instanceof With
|
||||
or
|
||||
bad instanceof Raise
|
||||
or
|
||||
bad instanceof Assert
|
||||
or
|
||||
bad instanceof BoolExpr
|
||||
or
|
||||
bad instanceof If and
|
||||
(not exists(bad.(If).getAnOrelse()) or bad.(If).isElif())
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
from TimerCfgNode node, int ts, string branch
|
||||
where
|
||||
missingBranchTimestamp(node, ts, branch) and
|
||||
not hasUnmodellableBranching(node.getTestFunction())
|
||||
select node,
|
||||
"Timestamp " + ts + " on true/false branch is missing a dead() annotation on the " + branch +
|
||||
" successor in $@", node.getTestFunction(), node.getTestFunction().getName()
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* New-CFG version of ConsecutivePredecessorTimestamps.
|
||||
*
|
||||
* Checks that each annotated node (except the minimum timestamp) has
|
||||
* a predecessor annotation with timestamp `a - 1`. This is the reverse
|
||||
* of ConsecutiveTimestamps: it catches nodes that are reachable but
|
||||
* arrived at from the wrong place (skipping an intermediate node).
|
||||
*/
|
||||
|
||||
import python
|
||||
import TimerUtils
|
||||
import NewCfgImpl
|
||||
|
||||
private module Utils = EvalOrderCfgUtils<NewCfg>;
|
||||
|
||||
private import Utils
|
||||
private import Utils::CfgTests
|
||||
|
||||
from TimerAnnotation ann, int a
|
||||
where consecutivePredecessorTimestamps(ann, a)
|
||||
select ann, "$@ in $@ has no consecutive predecessor (expected " + (a - 1) + ")",
|
||||
ann.getTimestampExpr(a), "Timestamp " + a, ann.getTestFunction(), ann.getTestFunction().getName()
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* New-CFG version of ConsecutiveTimestamps.
|
||||
*
|
||||
* Original:
|
||||
* Checks that consecutive annotated nodes have consecutive timestamps:
|
||||
* for each annotation with timestamp `a`, some CFG node for that annotation
|
||||
* must have a next annotation containing `a + 1`.
|
||||
*
|
||||
* Handles CFG splitting (e.g., finally blocks duplicated for normal/exceptional
|
||||
* flow) by checking that at least one split has the required successor.
|
||||
*
|
||||
* Only applies to functions where all annotations are in the function's
|
||||
* own scope (excludes tests with generators, async, comprehensions, or
|
||||
* lambdas that have annotations in nested scopes).
|
||||
*/
|
||||
|
||||
import python
|
||||
import TimerUtils
|
||||
import NewCfgImpl
|
||||
|
||||
private module Utils = EvalOrderCfgUtils<NewCfg>;
|
||||
|
||||
private import Utils
|
||||
private import Utils::CfgTests
|
||||
|
||||
from TimerAnnotation ann, int a
|
||||
where consecutiveTimestamps(ann, a)
|
||||
select ann, "$@ in $@ has no consecutive successor (expected " + (a + 1) + ")",
|
||||
ann.getTimestampExpr(a), "Timestamp " + a, ann.getTestFunction(), ann.getTestFunction().getName()
|
||||
@@ -0,0 +1,120 @@
|
||||
/**
|
||||
* Implementation of the evaluation-order CFG signature using the new
|
||||
* shared control flow graph from AstNodeImpl.
|
||||
*/
|
||||
|
||||
private import python as Py
|
||||
import TimerUtils
|
||||
private import semmle.python.controlflow.internal.AstNodeImpl as CfgImpl
|
||||
private import codeql.controlflow.SuccessorType
|
||||
|
||||
private class NewControlFlowNode = CfgImpl::ControlFlowNode;
|
||||
|
||||
private class NewBasicBlock = CfgImpl::BasicBlock;
|
||||
|
||||
/** New (shared) CFG implementation of the evaluation-order signature. */
|
||||
module NewCfg implements EvalOrderCfgSig {
|
||||
class CfgNode instanceof NewControlFlowNode {
|
||||
// We must pick a *unique* representative CFG node for each AST node. The
|
||||
// shared CFG has several nodes per AST node (before / in-post-order / after
|
||||
// / after-value splits), but the timer test framework keys annotations on
|
||||
// `getNode()` and assumes one CFG node per annotated AST node. Without a
|
||||
// filter, an annotated `f()` would map to both `f()` and `After f()`, which
|
||||
// breaks two framework invariants: (1) the "no shared reachable" check
|
||||
// requires that two distinct nodes sharing a timestamp be mutually
|
||||
// unreachable (true/false branches of a condition), but `Before f()`,
|
||||
// `f()` and `After f()` share the annotation's timestamp *and* lie on one
|
||||
// linear path; and (2) the annotation walk (`nextTimerAnnotation`) halts at
|
||||
// the first reachable representative, so a second node for the same AST
|
||||
// node would stall the walk on the same timestamp instead of advancing to
|
||||
// the next evaluation event.
|
||||
//
|
||||
// We use the "after" node (`isAfter`) rather than the canonical `injects`
|
||||
// node, because `injects` represents short-circuit / conditional
|
||||
// expressions (`and`/`or`/`not`/ternary) by their *before* node, placing
|
||||
// them ahead of their operands — wrong for evaluation order. `isAfter`
|
||||
// instead picks the post-evaluation node: the merged before/after node for
|
||||
// simple leaves, the `TAfterNode` for post-order expressions, and the
|
||||
// `AfterValueNode`(s) for pre-order conditionals, all positioned after the
|
||||
// operands. The two value-split nodes of a conditional are genuinely
|
||||
// distinct evaluation outcomes (handled by `getATrueSuccessor` /
|
||||
// `getAFalseSuccessor`), so they do not violate the uniqueness assumption.
|
||||
CfgNode() { NewControlFlowNode.super.isAfter(_) }
|
||||
|
||||
string toString() { result = NewControlFlowNode.super.toString() }
|
||||
|
||||
Py::Location getLocation() { result = NewControlFlowNode.super.getLocation() }
|
||||
|
||||
Py::AstNode getNode() {
|
||||
result = CfgImpl::astNodeToPyNode(NewControlFlowNode.super.getAstNode())
|
||||
}
|
||||
|
||||
CfgNode getASuccessor() { nextCfgNode(this, result) }
|
||||
|
||||
CfgNode getATrueSuccessor() {
|
||||
NewControlFlowNode.super.isAfterTrue(_) and
|
||||
// Only where there's also a false branch (true boolean split)
|
||||
exists(NewControlFlowNode other | other.isAfterFalse(NewControlFlowNode.super.getAstNode())) and
|
||||
nextCfgNodeFrom(this, result)
|
||||
}
|
||||
|
||||
CfgNode getAFalseSuccessor() {
|
||||
NewControlFlowNode.super.isAfterFalse(_) and
|
||||
// Only where there's also a true branch (true boolean split)
|
||||
exists(NewControlFlowNode other | other.isAfterTrue(NewControlFlowNode.super.getAstNode())) and
|
||||
nextCfgNodeFrom(this, result)
|
||||
}
|
||||
|
||||
CfgNode getAnExceptionalSuccessor() {
|
||||
exists(NewControlFlowNode mid |
|
||||
mid = NewControlFlowNode.super.getAnExceptionSuccessor() and
|
||||
nextCfgNodeFrom(mid, result)
|
||||
)
|
||||
}
|
||||
|
||||
Py::Scope getScope() { result = NewControlFlowNode.super.getEnclosingCallable().asScope() }
|
||||
|
||||
BasicBlock getBasicBlock() {
|
||||
exists(NewBasicBlock bb, int i | bb.getNode(i) = this and result = bb)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `next` is the nearest CfgNode reachable from `n` via
|
||||
* one or more raw CFG successor edges, skipping non-CfgNode intermediaries.
|
||||
*/
|
||||
private predicate nextCfgNodeFrom(NewControlFlowNode n, CfgNode next) {
|
||||
next = n.getASuccessor()
|
||||
or
|
||||
exists(NewControlFlowNode mid |
|
||||
mid = n.getASuccessor() and
|
||||
not mid instanceof CfgNode and
|
||||
nextCfgNodeFrom(mid, next)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `next` is the nearest CfgNode successor of `n`,
|
||||
* skipping synthetic intermediate nodes.
|
||||
*/
|
||||
private predicate nextCfgNode(CfgNode n, CfgNode next) { nextCfgNodeFrom(n, next) }
|
||||
|
||||
class BasicBlock instanceof NewBasicBlock {
|
||||
string toString() { result = NewBasicBlock.super.toString() }
|
||||
|
||||
CfgNode getNode(int n) { result = NewBasicBlock.super.getNode(n) }
|
||||
|
||||
predicate reaches(BasicBlock bb) { this = bb or this.strictlyReaches(bb) }
|
||||
|
||||
predicate strictlyReaches(BasicBlock bb) { NewBasicBlock.super.getASuccessor+() = bb }
|
||||
|
||||
predicate strictlyDominates(BasicBlock bb) { NewBasicBlock.super.strictlyDominates(bb) }
|
||||
}
|
||||
|
||||
CfgNode scopeGetEntryNode(Py::Scope s) {
|
||||
exists(CfgImpl::ControlFlow::EntryNode entry |
|
||||
entry.getEnclosingCallable().asScope() = s and
|
||||
nextCfgNodeFrom(entry, result)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* New-CFG version of NeverReachable.
|
||||
*
|
||||
* Original:
|
||||
* Checks that expressions annotated with `t.never` either have no CFG
|
||||
* node, or if they do, that the node is not reachable from its scope's
|
||||
* entry (including within the same basic block).
|
||||
*/
|
||||
|
||||
import python
|
||||
import TimerUtils
|
||||
import NewCfgImpl
|
||||
|
||||
private module Utils = EvalOrderCfgUtils<NewCfg>;
|
||||
|
||||
private import Utils::CfgTests
|
||||
|
||||
from TimerAnnotation ann
|
||||
where neverReachable(ann)
|
||||
select ann, "Node annotated with t.never is reachable in $@", ann.getTestFunction(),
|
||||
ann.getTestFunction().getName()
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* New-CFG version of NoBackwardFlow.
|
||||
*
|
||||
* Original:
|
||||
* Checks that time never flows backward between consecutive timer annotations
|
||||
* in the CFG. For each pair of consecutive annotated nodes (A -> B), there must
|
||||
* exist timestamps a in A and b in B with a < b.
|
||||
*/
|
||||
|
||||
import python
|
||||
import TimerUtils
|
||||
import NewCfgImpl
|
||||
|
||||
private module Utils = EvalOrderCfgUtils<NewCfg>;
|
||||
|
||||
private import Utils
|
||||
private import Utils::CfgTests
|
||||
|
||||
from TimerCfgNode a, TimerCfgNode b, int minA, int maxB
|
||||
where noBackwardFlow(a, b, minA, maxB)
|
||||
select a, "Backward flow: $@ flows to $@ (max timestamp $@)", a.getTimestampExpr(minA),
|
||||
minA.toString(), b, b.getNode().toString(), b.getTimestampExpr(maxB), maxB.toString()
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* New-CFG version of NoBasicBlock.
|
||||
*
|
||||
* Checks that every annotated CFG node belongs to a basic block.
|
||||
*/
|
||||
|
||||
import python
|
||||
import TimerUtils
|
||||
import NewCfgImpl
|
||||
|
||||
private module Utils = EvalOrderCfgUtils<NewCfg>;
|
||||
|
||||
private import Utils
|
||||
private import Utils::CfgTests
|
||||
|
||||
from CfgNode n, TestFunction f
|
||||
where noBasicBlock(n, f)
|
||||
select n, "CFG node in $@ does not belong to any basic block", f, f.getName()
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* New-CFG version of NoSharedReachable.
|
||||
*
|
||||
* Original:
|
||||
* Checks that two annotations sharing a timestamp value are on
|
||||
* mutually exclusive CFG paths (neither can reach the other).
|
||||
*/
|
||||
|
||||
import python
|
||||
import TimerUtils
|
||||
import NewCfgImpl
|
||||
|
||||
private module Utils = EvalOrderCfgUtils<NewCfg>;
|
||||
|
||||
private import Utils
|
||||
private import Utils::CfgTests
|
||||
|
||||
from TimerCfgNode a, TimerCfgNode b, int ts
|
||||
where noSharedReachable(a, b, ts)
|
||||
select a, "Shared timestamp $@ but this node reaches $@", a.getTimestampExpr(ts), ts.toString(), b,
|
||||
b.getNode().toString()
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* New-CFG version of StrictForward.
|
||||
*
|
||||
* Original:
|
||||
* Stronger version of NoBackwardFlow: for consecutive annotated nodes
|
||||
* A -> B that both have a single timestamp (non-loop code) and B does
|
||||
* NOT dominate A (forward edge), requires max(A) < min(B).
|
||||
*/
|
||||
|
||||
import python
|
||||
import TimerUtils
|
||||
import NewCfgImpl
|
||||
|
||||
private module Utils = EvalOrderCfgUtils<NewCfg>;
|
||||
|
||||
private import Utils
|
||||
private import Utils::CfgTests
|
||||
|
||||
from TimerCfgNode a, TimerCfgNode b, int maxA, int minB
|
||||
where strictForward(a, b, maxA, minB)
|
||||
select a, "Strict forward violation: $@ flows to $@", a.getTimestampExpr(maxA), "timestamp " + maxA,
|
||||
b.getTimestampExpr(minB), "timestamp " + minB
|
||||
@@ -3,14 +3,14 @@
|
||||
* Python control flow graph.
|
||||
*/
|
||||
|
||||
private import python as PY
|
||||
private import python as Py
|
||||
import TimerUtils
|
||||
|
||||
/** Existing Python CFG implementation of the evaluation-order signature. */
|
||||
module OldCfg implements EvalOrderCfgSig {
|
||||
class CfgNode = PY::ControlFlowNode;
|
||||
class CfgNode = Py::ControlFlowNode;
|
||||
|
||||
class BasicBlock = PY::BasicBlock;
|
||||
class BasicBlock = Py::BasicBlock;
|
||||
|
||||
CfgNode scopeGetEntryNode(PY::Scope s) { result = s.getEntryNode() }
|
||||
CfgNode scopeGetEntryNode(Py::Scope s) { result = s.getEntryNode() }
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ def test_nested_if_else(t):
|
||||
else:
|
||||
z = 2 @ t[dead(4)]
|
||||
else:
|
||||
z = 3 @ t[dead(4)]
|
||||
z = 3 @ t[dead(3), dead(4)]
|
||||
w = 0 @ t[5]
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* Inline-expectations test for the store/load/delete/parameter
|
||||
* classification predicates on the new-CFG facade.
|
||||
*
|
||||
* Each tag fires when the corresponding predicate (`isLoad`,
|
||||
* `isStore`, `isDelete`, `isParameter`, `isAugLoad`, `isAugStore`)
|
||||
* holds on the canonical CFG node wrapping a `Py::Name` with the
|
||||
* given identifier. Subscript and attribute stores are not covered
|
||||
* by these tags — only the `Name`-typed targets/loads they involve.
|
||||
*/
|
||||
|
||||
import python
|
||||
import semmle.python.controlflow.internal.Cfg as Cfg
|
||||
import utils.test.InlineExpectationsTest
|
||||
|
||||
module StoreLoadTest implements TestSig {
|
||||
string getARelevantTag() { result = ["load", "store", "delete", "param", "augload", "augstore"] }
|
||||
|
||||
predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(Cfg::NameNode n |
|
||||
location = n.getLocation() and
|
||||
element = n.toString() and
|
||||
value = n.getId() and
|
||||
(
|
||||
n.isLoad() and not n.isAugLoad() and tag = "load"
|
||||
or
|
||||
n.isStore() and not n.isAugStore() and tag = "store"
|
||||
or
|
||||
n.isDelete() and tag = "delete"
|
||||
or
|
||||
n.isParameter() and tag = "param"
|
||||
or
|
||||
n.isAugLoad() and tag = "augload"
|
||||
or
|
||||
n.isAugStore() and tag = "augstore"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import MakeTest<StoreLoadTest>
|
||||
56
python/ql/test/library-tests/ControlFlow/store-load/test.py
Normal file
56
python/ql/test/library-tests/ControlFlow/store-load/test.py
Normal file
@@ -0,0 +1,56 @@
|
||||
# Store/load/delete/parameter classification on the new-CFG facade.
|
||||
#
|
||||
# Each annotated location carries the (sorted, deduplicated) set of
|
||||
# kinds the CFG facade reports there. Comparing against the legacy
|
||||
# 'semmle.python.Flow' classification is done by the comparison query
|
||||
# 'StoreLoadParity.ql' — annotations here are only the positive
|
||||
# assertions for the new facade.
|
||||
#
|
||||
# Tags:
|
||||
# load=<id> -- isLoad() fires on the Name
|
||||
# store=<id> -- isStore() fires
|
||||
# delete=<id> -- isDelete() fires
|
||||
# param=<id> -- isParameter() fires
|
||||
# augload=<id> -- isAugLoad() fires (the LHS of x += ... when read)
|
||||
# augstore=<id> -- isAugStore() fires (the LHS of x += ... when written)
|
||||
|
||||
|
||||
# --- plain load / store / delete ---
|
||||
|
||||
x = 1 # $ store=x
|
||||
y = x + 1 # $ store=y load=x
|
||||
print(y) # $ load=print load=y
|
||||
del x # $ delete=x
|
||||
|
||||
|
||||
# --- function definitions (parameters) ---
|
||||
|
||||
def f(a, b=2, *args, c, **kwargs): # $ store=f param=a param=b param=args param=c param=kwargs
|
||||
return a + b + c # $ load=a load=b load=c
|
||||
|
||||
|
||||
# --- augmented assignment splits one Name into load + store halves ---
|
||||
|
||||
def aug(): # $ store=aug
|
||||
n = 0 # $ store=n
|
||||
n += 1 # $ augload=n augstore=n
|
||||
return n # $ load=n
|
||||
|
||||
|
||||
# --- subscript / attribute stores ---
|
||||
|
||||
class C: # $ store=C
|
||||
pass
|
||||
|
||||
|
||||
def stores(obj, container, idx): # $ store=stores param=obj param=container param=idx
|
||||
obj.attr = 1 # $ load=obj
|
||||
container[idx] = 2 # $ load=container load=idx
|
||||
return obj # $ load=obj
|
||||
|
||||
|
||||
# --- tuple unpacking ---
|
||||
|
||||
def unpack(pair): # $ store=unpack param=pair
|
||||
a, b = pair # $ store=a store=b load=pair
|
||||
return a + b # $ load=a load=b
|
||||
BIN
ql/Cargo.lock
generated
BIN
ql/Cargo.lock
generated
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -1,571 +0,0 @@
|
||||
/**
|
||||
* @name Cyclic join
|
||||
* @description Finds non-recursive predicate bodies and query `where` clauses whose
|
||||
* join graph contains an irreducible cycle (a triangle or a chordless
|
||||
* cycle of length 4, 5, 6, 7, 8, 9 or 10). Such alpha-cyclic conjunctive joins cannot
|
||||
* be evaluated optimally by any binary join plan and are the canonical case
|
||||
* that worst-case-optimal join algorithms (e.g. Leapfrog Triejoin) accelerate.
|
||||
* @id ql/cyclic-join
|
||||
* @tags performance
|
||||
* join-order
|
||||
*/
|
||||
|
||||
import ql
|
||||
import codeql_ql.ast.internal.AstNodeNumbering
|
||||
|
||||
/**
|
||||
* A scope that gives rise to a conjunctive join: a predicate body or a query's
|
||||
* `from`/`where`. Atoms within the same scope are (positively) conjoined.
|
||||
*/
|
||||
class JoinScope extends AstNode {
|
||||
JoinScope() {
|
||||
this instanceof Predicate or
|
||||
this instanceof Select
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the nearest enclosing predicate or select of `n`. */
|
||||
JoinScope getScope(AstNode n) {
|
||||
result = n.getParent+() and
|
||||
not exists(JoinScope closer |
|
||||
closer = n.getParent+() and
|
||||
result = closer.getParent+()
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `f` combines sub-formulas, i.e. it is not a single join atom. */
|
||||
predicate isConnective(Formula f) {
|
||||
f instanceof Conjunction or
|
||||
f instanceof Disjunction or
|
||||
f instanceof Negation or
|
||||
f instanceof Quantifier or
|
||||
f instanceof IfFormula or
|
||||
f instanceof Implication or
|
||||
f instanceof HigherOrderFormula
|
||||
}
|
||||
|
||||
/**
|
||||
* An atomic formula: a leaf formula sitting in formula position inside a
|
||||
* conjunctive context (its parent is a connective, a `select`, or a predicate
|
||||
* body). Atoms nested inside an expression/another atom, or inside a `not`,
|
||||
* are excluded because they do not contribute positive join edges.
|
||||
*/
|
||||
class Atom extends Formula {
|
||||
Atom() {
|
||||
not isConnective(this) and
|
||||
exists(AstNode p | p = this.getParent() |
|
||||
isConnective(p) or
|
||||
p instanceof Select or
|
||||
p instanceof Predicate
|
||||
) and
|
||||
not this.getParent+() instanceof Negation
|
||||
}
|
||||
}
|
||||
|
||||
/** Holds if atom `a` references variable `v`. */
|
||||
predicate touches(Atom a, VarDef v) {
|
||||
exists(VarAccess va | va = a.getAChild*() and va.getDeclaration() = v)
|
||||
}
|
||||
|
||||
/** Holds if atom `a` uses a transitive-closure (`+`/`*`) call, i.e. is recursive. */
|
||||
predicate isRecursiveAtom(Atom a) {
|
||||
exists(Call c | c = a.getAChild*() and c.isClosure(_))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if, within scope `s`, there is a (non-recursive) atom joining the two
|
||||
* distinct variables `u` and `v` -- an edge of the primal (join) graph.
|
||||
*/
|
||||
predicate primalEdge(JoinScope s, VarDef u, VarDef v) {
|
||||
u != v and
|
||||
exists(Atom a |
|
||||
getScope(a) = s and
|
||||
not isRecursiveAtom(a) and
|
||||
touches(a, u) and
|
||||
touches(a, v)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if scope `s` contains an irreducible triangle over the distinct
|
||||
* variables `x < y < z`. "Irreducible" means no single atom covers all three
|
||||
* variables, so the three edges necessarily come from three different atoms and
|
||||
* the join is genuinely alpha-cyclic (not GYO-reducible).
|
||||
*/
|
||||
predicate triangle(JoinScope s, VarDef x, VarDef y, VarDef z) {
|
||||
getPreOrderId(x) < getPreOrderId(y) and
|
||||
getPreOrderId(y) < getPreOrderId(z) and
|
||||
primalEdge(s, x, y) and
|
||||
primalEdge(s, y, z) and
|
||||
primalEdge(s, x, z) and
|
||||
// conformality: no atom covers the whole clique (which would make it acyclic)
|
||||
not exists(Atom cover |
|
||||
getScope(cover) = s and
|
||||
touches(cover, x) and
|
||||
touches(cover, y) and
|
||||
touches(cover, z)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if scope `s` contains a chordless 4-cycle `p0 - p1 - p2 - p3 - p0` over
|
||||
* four distinct variables. Chordlessness (no diagonal edge) guarantees the cycle
|
||||
* is irreducible/alpha-cyclic and that no atom covers it. `p0` is the minimum and
|
||||
* `p1 < p3` to report each such cycle once.
|
||||
*/
|
||||
predicate square(JoinScope s, VarDef p0, VarDef p1, VarDef p2, VarDef p3) {
|
||||
getPreOrderId(p0) < getPreOrderId(p1) and
|
||||
getPreOrderId(p0) < getPreOrderId(p2) and
|
||||
getPreOrderId(p0) < getPreOrderId(p3) and
|
||||
getPreOrderId(p1) < getPreOrderId(p3) and
|
||||
p1 != p2 and
|
||||
p2 != p3 and
|
||||
// the four sides
|
||||
primalEdge(s, p0, p1) and
|
||||
primalEdge(s, p1, p2) and
|
||||
primalEdge(s, p2, p3) and
|
||||
primalEdge(s, p3, p0) and
|
||||
// the two diagonals must be absent (chordless)
|
||||
not primalEdge(s, p0, p2) and
|
||||
not primalEdge(s, p1, p3)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if scope `s` contains a chordless 5-cycle `p0 - p1 - p2 - p3 - p4 - p0`
|
||||
* over five distinct variables. `p0` is the minimum and `p1 < p4` orients the
|
||||
* cycle so each one is reported once; all five chords are required absent, which
|
||||
* also rules out any covering atom (irreducible/alpha-cyclic).
|
||||
*/
|
||||
predicate pentagon(JoinScope s, VarDef p0, VarDef p1, VarDef p2, VarDef p3, VarDef p4) {
|
||||
getPreOrderId(p0) < getPreOrderId(p1) and
|
||||
getPreOrderId(p0) < getPreOrderId(p2) and
|
||||
getPreOrderId(p0) < getPreOrderId(p3) and
|
||||
getPreOrderId(p0) < getPreOrderId(p4) and
|
||||
getPreOrderId(p1) < getPreOrderId(p4) and
|
||||
p1 != p3 and
|
||||
p2 != p4 and
|
||||
// the five sides
|
||||
primalEdge(s, p0, p1) and
|
||||
primalEdge(s, p1, p2) and
|
||||
primalEdge(s, p2, p3) and
|
||||
primalEdge(s, p3, p4) and
|
||||
primalEdge(s, p4, p0) and
|
||||
// all five chords must be absent (chordless)
|
||||
not primalEdge(s, p0, p2) and
|
||||
not primalEdge(s, p0, p3) and
|
||||
not primalEdge(s, p1, p3) and
|
||||
not primalEdge(s, p1, p4) and
|
||||
not primalEdge(s, p2, p4)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if scope `s` contains a chordless 6-cycle
|
||||
* `p0 - p1 - p2 - p3 - p4 - p5 - p0` over six distinct variables. `p0` is the
|
||||
* minimum and `p1 < p5` orients the cycle so each one is reported once; all nine
|
||||
* chords are required absent, which also rules out any covering atom.
|
||||
*/
|
||||
predicate hexagon(JoinScope s, VarDef p0, VarDef p1, VarDef p2, VarDef p3, VarDef p4, VarDef p5) {
|
||||
getPreOrderId(p0) < getPreOrderId(p1) and
|
||||
getPreOrderId(p0) < getPreOrderId(p2) and
|
||||
getPreOrderId(p0) < getPreOrderId(p3) and
|
||||
getPreOrderId(p0) < getPreOrderId(p4) and
|
||||
getPreOrderId(p0) < getPreOrderId(p5) and
|
||||
getPreOrderId(p1) < getPreOrderId(p5) and
|
||||
p1 != p3 and
|
||||
p1 != p4 and
|
||||
p2 != p4 and
|
||||
p2 != p5 and
|
||||
p3 != p5 and
|
||||
// the six sides
|
||||
primalEdge(s, p0, p1) and
|
||||
primalEdge(s, p1, p2) and
|
||||
primalEdge(s, p2, p3) and
|
||||
primalEdge(s, p3, p4) and
|
||||
primalEdge(s, p4, p5) and
|
||||
primalEdge(s, p5, p0) and
|
||||
// all nine chords must be absent (chordless)
|
||||
not primalEdge(s, p0, p2) and
|
||||
not primalEdge(s, p0, p3) and
|
||||
not primalEdge(s, p0, p4) and
|
||||
not primalEdge(s, p1, p3) and
|
||||
not primalEdge(s, p1, p4) and
|
||||
not primalEdge(s, p1, p5) and
|
||||
not primalEdge(s, p2, p4) and
|
||||
not primalEdge(s, p2, p5) and
|
||||
not primalEdge(s, p3, p5)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if scope `s` contains a chordless 7-cycle
|
||||
* `p0 - p1 - p2 - p3 - p4 - p5 - p6 - p0` over seven distinct variables. `p0` is
|
||||
* the minimum and `p1 < p6` orients the cycle so each one is reported once; all 14
|
||||
* chords are required absent, which also rules out any covering atom.
|
||||
*/
|
||||
predicate heptagon(
|
||||
JoinScope s, VarDef p0, VarDef p1, VarDef p2, VarDef p3, VarDef p4, VarDef p5, VarDef p6
|
||||
) {
|
||||
getPreOrderId(p0) < getPreOrderId(p1) and
|
||||
getPreOrderId(p0) < getPreOrderId(p2) and
|
||||
getPreOrderId(p0) < getPreOrderId(p3) and
|
||||
getPreOrderId(p0) < getPreOrderId(p4) and
|
||||
getPreOrderId(p0) < getPreOrderId(p5) and
|
||||
getPreOrderId(p0) < getPreOrderId(p6) and
|
||||
getPreOrderId(p1) < getPreOrderId(p6) and
|
||||
p1 != p3 and
|
||||
p1 != p4 and
|
||||
p1 != p5 and
|
||||
p2 != p4 and
|
||||
p2 != p5 and
|
||||
p2 != p6 and
|
||||
p3 != p5 and
|
||||
p3 != p6 and
|
||||
p4 != p6 and
|
||||
// the seven sides
|
||||
primalEdge(s, p0, p1) and
|
||||
primalEdge(s, p1, p2) and
|
||||
primalEdge(s, p2, p3) and
|
||||
primalEdge(s, p3, p4) and
|
||||
primalEdge(s, p4, p5) and
|
||||
primalEdge(s, p5, p6) and
|
||||
primalEdge(s, p6, p0) and
|
||||
// all 14 chords must be absent (chordless)
|
||||
not primalEdge(s, p0, p2) and
|
||||
not primalEdge(s, p0, p3) and
|
||||
not primalEdge(s, p0, p4) and
|
||||
not primalEdge(s, p0, p5) and
|
||||
not primalEdge(s, p1, p3) and
|
||||
not primalEdge(s, p1, p4) and
|
||||
not primalEdge(s, p1, p5) and
|
||||
not primalEdge(s, p1, p6) and
|
||||
not primalEdge(s, p2, p4) and
|
||||
not primalEdge(s, p2, p5) and
|
||||
not primalEdge(s, p2, p6) and
|
||||
not primalEdge(s, p3, p5) and
|
||||
not primalEdge(s, p3, p6) and
|
||||
not primalEdge(s, p4, p6)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if scope `s` contains a chordless 8-cycle
|
||||
* `p0 - p1 - p2 - p3 - p4 - p5 - p6 - p7 - p0` over eight distinct variables. `p0`
|
||||
* is the minimum and `p1 < p7` orients the cycle so each one is reported once; all
|
||||
* 20 chords are required absent, which also rules out any covering atom.
|
||||
*/
|
||||
predicate octagon(
|
||||
JoinScope s, VarDef p0, VarDef p1, VarDef p2, VarDef p3, VarDef p4, VarDef p5, VarDef p6,
|
||||
VarDef p7
|
||||
) {
|
||||
getPreOrderId(p0) < getPreOrderId(p1) and
|
||||
getPreOrderId(p0) < getPreOrderId(p2) and
|
||||
getPreOrderId(p0) < getPreOrderId(p3) and
|
||||
getPreOrderId(p0) < getPreOrderId(p4) and
|
||||
getPreOrderId(p0) < getPreOrderId(p5) and
|
||||
getPreOrderId(p0) < getPreOrderId(p6) and
|
||||
getPreOrderId(p0) < getPreOrderId(p7) and
|
||||
getPreOrderId(p1) < getPreOrderId(p7) and
|
||||
p1 != p3 and
|
||||
p1 != p4 and
|
||||
p1 != p5 and
|
||||
p1 != p6 and
|
||||
p2 != p4 and
|
||||
p2 != p5 and
|
||||
p2 != p6 and
|
||||
p2 != p7 and
|
||||
p3 != p5 and
|
||||
p3 != p6 and
|
||||
p3 != p7 and
|
||||
p4 != p6 and
|
||||
p4 != p7 and
|
||||
p5 != p7 and
|
||||
// the eight sides
|
||||
primalEdge(s, p0, p1) and
|
||||
primalEdge(s, p1, p2) and
|
||||
primalEdge(s, p2, p3) and
|
||||
primalEdge(s, p3, p4) and
|
||||
primalEdge(s, p4, p5) and
|
||||
primalEdge(s, p5, p6) and
|
||||
primalEdge(s, p6, p7) and
|
||||
primalEdge(s, p7, p0) and
|
||||
// all 20 chords must be absent (chordless)
|
||||
not primalEdge(s, p0, p2) and
|
||||
not primalEdge(s, p0, p3) and
|
||||
not primalEdge(s, p0, p4) and
|
||||
not primalEdge(s, p0, p5) and
|
||||
not primalEdge(s, p0, p6) and
|
||||
not primalEdge(s, p1, p3) and
|
||||
not primalEdge(s, p1, p4) and
|
||||
not primalEdge(s, p1, p5) and
|
||||
not primalEdge(s, p1, p6) and
|
||||
not primalEdge(s, p1, p7) and
|
||||
not primalEdge(s, p2, p4) and
|
||||
not primalEdge(s, p2, p5) and
|
||||
not primalEdge(s, p2, p6) and
|
||||
not primalEdge(s, p2, p7) and
|
||||
not primalEdge(s, p3, p5) and
|
||||
not primalEdge(s, p3, p6) and
|
||||
not primalEdge(s, p3, p7) and
|
||||
not primalEdge(s, p4, p6) and
|
||||
not primalEdge(s, p4, p7) and
|
||||
not primalEdge(s, p5, p7)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if scope `s` contains a chordless 9-cycle
|
||||
* `p0 - p1 - p2 - p3 - p4 - p5 - p6 - p7 - p8 - p0` over nine distinct variables.
|
||||
* `p0` is the minimum and `p1 < p8` orients the cycle so each one is reported once;
|
||||
* all 27 chords are required absent, which also rules out any covering atom.
|
||||
*/
|
||||
predicate enneagon(
|
||||
JoinScope s, VarDef p0, VarDef p1, VarDef p2, VarDef p3, VarDef p4, VarDef p5, VarDef p6,
|
||||
VarDef p7, VarDef p8
|
||||
) {
|
||||
getPreOrderId(p0) < getPreOrderId(p1) and
|
||||
getPreOrderId(p0) < getPreOrderId(p2) and
|
||||
getPreOrderId(p0) < getPreOrderId(p3) and
|
||||
getPreOrderId(p0) < getPreOrderId(p4) and
|
||||
getPreOrderId(p0) < getPreOrderId(p5) and
|
||||
getPreOrderId(p0) < getPreOrderId(p6) and
|
||||
getPreOrderId(p0) < getPreOrderId(p7) and
|
||||
getPreOrderId(p0) < getPreOrderId(p8) and
|
||||
getPreOrderId(p1) < getPreOrderId(p8) and
|
||||
p1 != p3 and
|
||||
p1 != p4 and
|
||||
p1 != p5 and
|
||||
p1 != p6 and
|
||||
p1 != p7 and
|
||||
p2 != p4 and
|
||||
p2 != p5 and
|
||||
p2 != p6 and
|
||||
p2 != p7 and
|
||||
p2 != p8 and
|
||||
p3 != p5 and
|
||||
p3 != p6 and
|
||||
p3 != p7 and
|
||||
p3 != p8 and
|
||||
p4 != p6 and
|
||||
p4 != p7 and
|
||||
p4 != p8 and
|
||||
p5 != p7 and
|
||||
p5 != p8 and
|
||||
p6 != p8 and
|
||||
// the nine sides
|
||||
primalEdge(s, p0, p1) and
|
||||
primalEdge(s, p1, p2) and
|
||||
primalEdge(s, p2, p3) and
|
||||
primalEdge(s, p3, p4) and
|
||||
primalEdge(s, p4, p5) and
|
||||
primalEdge(s, p5, p6) and
|
||||
primalEdge(s, p6, p7) and
|
||||
primalEdge(s, p7, p8) and
|
||||
primalEdge(s, p8, p0) and
|
||||
// all 27 chords must be absent (chordless)
|
||||
not primalEdge(s, p0, p2) and
|
||||
not primalEdge(s, p0, p3) and
|
||||
not primalEdge(s, p0, p4) and
|
||||
not primalEdge(s, p0, p5) and
|
||||
not primalEdge(s, p0, p6) and
|
||||
not primalEdge(s, p0, p7) and
|
||||
not primalEdge(s, p1, p3) and
|
||||
not primalEdge(s, p1, p4) and
|
||||
not primalEdge(s, p1, p5) and
|
||||
not primalEdge(s, p1, p6) and
|
||||
not primalEdge(s, p1, p7) and
|
||||
not primalEdge(s, p1, p8) and
|
||||
not primalEdge(s, p2, p4) and
|
||||
not primalEdge(s, p2, p5) and
|
||||
not primalEdge(s, p2, p6) and
|
||||
not primalEdge(s, p2, p7) and
|
||||
not primalEdge(s, p2, p8) and
|
||||
not primalEdge(s, p3, p5) and
|
||||
not primalEdge(s, p3, p6) and
|
||||
not primalEdge(s, p3, p7) and
|
||||
not primalEdge(s, p3, p8) and
|
||||
not primalEdge(s, p4, p6) and
|
||||
not primalEdge(s, p4, p7) and
|
||||
not primalEdge(s, p4, p8) and
|
||||
not primalEdge(s, p5, p7) and
|
||||
not primalEdge(s, p5, p8) and
|
||||
not primalEdge(s, p6, p8)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if scope `s` contains a chordless 10-cycle
|
||||
* `p0 - p1 - p2 - p3 - p4 - p5 - p6 - p7 - p8 - p9 - p0` over ten distinct
|
||||
* variables. `p0` is the minimum and `p1 < p9` orients the cycle so each one is
|
||||
* reported once; all 35 chords are required absent, which also rules out any
|
||||
* covering atom.
|
||||
*/
|
||||
predicate decagon(
|
||||
JoinScope s, VarDef p0, VarDef p1, VarDef p2, VarDef p3, VarDef p4, VarDef p5, VarDef p6,
|
||||
VarDef p7, VarDef p8, VarDef p9
|
||||
) {
|
||||
getPreOrderId(p0) < getPreOrderId(p1) and
|
||||
getPreOrderId(p0) < getPreOrderId(p2) and
|
||||
getPreOrderId(p0) < getPreOrderId(p3) and
|
||||
getPreOrderId(p0) < getPreOrderId(p4) and
|
||||
getPreOrderId(p0) < getPreOrderId(p5) and
|
||||
getPreOrderId(p0) < getPreOrderId(p6) and
|
||||
getPreOrderId(p0) < getPreOrderId(p7) and
|
||||
getPreOrderId(p0) < getPreOrderId(p8) and
|
||||
getPreOrderId(p0) < getPreOrderId(p9) and
|
||||
getPreOrderId(p1) < getPreOrderId(p9) and
|
||||
p1 != p3 and
|
||||
p1 != p4 and
|
||||
p1 != p5 and
|
||||
p1 != p6 and
|
||||
p1 != p7 and
|
||||
p1 != p8 and
|
||||
p2 != p4 and
|
||||
p2 != p5 and
|
||||
p2 != p6 and
|
||||
p2 != p7 and
|
||||
p2 != p8 and
|
||||
p2 != p9 and
|
||||
p3 != p5 and
|
||||
p3 != p6 and
|
||||
p3 != p7 and
|
||||
p3 != p8 and
|
||||
p3 != p9 and
|
||||
p4 != p6 and
|
||||
p4 != p7 and
|
||||
p4 != p8 and
|
||||
p4 != p9 and
|
||||
p5 != p7 and
|
||||
p5 != p8 and
|
||||
p5 != p9 and
|
||||
p6 != p8 and
|
||||
p6 != p9 and
|
||||
p7 != p9 and
|
||||
// the ten sides
|
||||
primalEdge(s, p0, p1) and
|
||||
primalEdge(s, p1, p2) and
|
||||
primalEdge(s, p2, p3) and
|
||||
primalEdge(s, p3, p4) and
|
||||
primalEdge(s, p4, p5) and
|
||||
primalEdge(s, p5, p6) and
|
||||
primalEdge(s, p6, p7) and
|
||||
primalEdge(s, p7, p8) and
|
||||
primalEdge(s, p8, p9) and
|
||||
primalEdge(s, p9, p0) and
|
||||
// all 35 chords must be absent (chordless)
|
||||
not primalEdge(s, p0, p2) and
|
||||
not primalEdge(s, p0, p3) and
|
||||
not primalEdge(s, p0, p4) and
|
||||
not primalEdge(s, p0, p5) and
|
||||
not primalEdge(s, p0, p6) and
|
||||
not primalEdge(s, p0, p7) and
|
||||
not primalEdge(s, p0, p8) and
|
||||
not primalEdge(s, p1, p3) and
|
||||
not primalEdge(s, p1, p4) and
|
||||
not primalEdge(s, p1, p5) and
|
||||
not primalEdge(s, p1, p6) and
|
||||
not primalEdge(s, p1, p7) and
|
||||
not primalEdge(s, p1, p8) and
|
||||
not primalEdge(s, p1, p9) and
|
||||
not primalEdge(s, p2, p4) and
|
||||
not primalEdge(s, p2, p5) and
|
||||
not primalEdge(s, p2, p6) and
|
||||
not primalEdge(s, p2, p7) and
|
||||
not primalEdge(s, p2, p8) and
|
||||
not primalEdge(s, p2, p9) and
|
||||
not primalEdge(s, p3, p5) and
|
||||
not primalEdge(s, p3, p6) and
|
||||
not primalEdge(s, p3, p7) and
|
||||
not primalEdge(s, p3, p8) and
|
||||
not primalEdge(s, p3, p9) and
|
||||
not primalEdge(s, p4, p6) and
|
||||
not primalEdge(s, p4, p7) and
|
||||
not primalEdge(s, p4, p8) and
|
||||
not primalEdge(s, p4, p9) and
|
||||
not primalEdge(s, p5, p7) and
|
||||
not primalEdge(s, p5, p8) and
|
||||
not primalEdge(s, p5, p9) and
|
||||
not primalEdge(s, p6, p8) and
|
||||
not primalEdge(s, p6, p9) and
|
||||
not primalEdge(s, p7, p9)
|
||||
}
|
||||
|
||||
/** Gets a printable name for `v`. */
|
||||
string varName(VarDef v) { result = v.getName() }
|
||||
|
||||
/**
|
||||
* Holds if scope `s` contains an irreducible cyclic join of length `len` over
|
||||
* the variables described by `vars`.
|
||||
*/
|
||||
predicate cyclicJoin(JoinScope s, int len, string vars) {
|
||||
exists(VarDef x, VarDef y, VarDef z |
|
||||
triangle(s, x, y, z) and
|
||||
len = 3 and
|
||||
vars = varName(x) + ", " + varName(y) + ", " + varName(z)
|
||||
)
|
||||
or
|
||||
exists(VarDef p0, VarDef p1, VarDef p2, VarDef p3 |
|
||||
square(s, p0, p1, p2, p3) and
|
||||
len = 4 and
|
||||
vars = varName(p0) + ", " + varName(p1) + ", " + varName(p2) + ", " + varName(p3)
|
||||
)
|
||||
or
|
||||
exists(VarDef p0, VarDef p1, VarDef p2, VarDef p3, VarDef p4 |
|
||||
pentagon(s, p0, p1, p2, p3, p4) and
|
||||
len = 5 and
|
||||
vars =
|
||||
varName(p0) + ", " + varName(p1) + ", " + varName(p2) + ", " + varName(p3) + ", " +
|
||||
varName(p4)
|
||||
)
|
||||
or
|
||||
exists(VarDef p0, VarDef p1, VarDef p2, VarDef p3, VarDef p4, VarDef p5 |
|
||||
hexagon(s, p0, p1, p2, p3, p4, p5) and
|
||||
len = 6 and
|
||||
vars =
|
||||
varName(p0) + ", " + varName(p1) + ", " + varName(p2) + ", " + varName(p3) + ", " +
|
||||
varName(p4) + ", " + varName(p5)
|
||||
)
|
||||
or
|
||||
exists(VarDef p0, VarDef p1, VarDef p2, VarDef p3, VarDef p4, VarDef p5, VarDef p6 |
|
||||
heptagon(s, p0, p1, p2, p3, p4, p5, p6) and
|
||||
len = 7 and
|
||||
vars =
|
||||
varName(p0) + ", " + varName(p1) + ", " + varName(p2) + ", " + varName(p3) + ", " +
|
||||
varName(p4) + ", " + varName(p5) + ", " + varName(p6)
|
||||
)
|
||||
or
|
||||
exists(
|
||||
VarDef p0, VarDef p1, VarDef p2, VarDef p3, VarDef p4, VarDef p5, VarDef p6, VarDef p7
|
||||
|
|
||||
octagon(s, p0, p1, p2, p3, p4, p5, p6, p7) and
|
||||
len = 8 and
|
||||
vars =
|
||||
varName(p0) + ", " + varName(p1) + ", " + varName(p2) + ", " + varName(p3) + ", " +
|
||||
varName(p4) + ", " + varName(p5) + ", " + varName(p6) + ", " + varName(p7)
|
||||
)
|
||||
or
|
||||
exists(
|
||||
VarDef p0, VarDef p1, VarDef p2, VarDef p3, VarDef p4, VarDef p5, VarDef p6, VarDef p7,
|
||||
VarDef p8
|
||||
|
|
||||
enneagon(s, p0, p1, p2, p3, p4, p5, p6, p7, p8) and
|
||||
len = 9 and
|
||||
vars =
|
||||
varName(p0) + ", " + varName(p1) + ", " + varName(p2) + ", " + varName(p3) + ", " +
|
||||
varName(p4) + ", " + varName(p5) + ", " + varName(p6) + ", " + varName(p7) + ", " +
|
||||
varName(p8)
|
||||
)
|
||||
or
|
||||
exists(
|
||||
VarDef p0, VarDef p1, VarDef p2, VarDef p3, VarDef p4, VarDef p5, VarDef p6, VarDef p7,
|
||||
VarDef p8, VarDef p9
|
||||
|
|
||||
decagon(s, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9) and
|
||||
len = 10 and
|
||||
vars =
|
||||
varName(p0) + ", " + varName(p1) + ", " + varName(p2) + ", " + varName(p3) + ", " +
|
||||
varName(p4) + ", " + varName(p5) + ", " + varName(p6) + ", " + varName(p7) + ", " +
|
||||
varName(p8) + ", " + varName(p9)
|
||||
)
|
||||
}
|
||||
|
||||
from JoinScope s, int len, string vars
|
||||
where cyclicJoin(s, len, vars)
|
||||
select s,
|
||||
"This " + s.getAPrimaryQlClass() + " contains a non-recursive cyclic (" + len.toString() +
|
||||
"-cycle) join over variables: " + vars + "."
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,35 +4,11 @@ private import codeql.util.test.InlineExpectationsTest
|
||||
module Impl implements InlineExpectationsTestSig {
|
||||
private import codeql.ruby.ast.internal.TreeSitter
|
||||
|
||||
private newtype TAnyComment =
|
||||
RubyComment(Ruby::Comment comment) or
|
||||
ErbComment(R::ErbComment comment)
|
||||
|
||||
/**
|
||||
* A class representing comments that may contain inline expectations (Ruby line comments and ERB comments).
|
||||
* A class representing line comments in Ruby.
|
||||
*/
|
||||
class ExpectationComment extends TAnyComment {
|
||||
Ruby::Comment asRubyComment() { this = RubyComment(result) }
|
||||
|
||||
R::ErbComment asErbComment() { this = ErbComment(result) }
|
||||
|
||||
string toString() {
|
||||
result = this.asRubyComment().toString()
|
||||
or
|
||||
result = this.asErbComment().toString()
|
||||
}
|
||||
|
||||
Location getLocation() {
|
||||
result = this.asRubyComment().getLocation()
|
||||
or
|
||||
result = this.asErbComment().getLocation()
|
||||
}
|
||||
|
||||
string getContents() {
|
||||
result = this.asRubyComment().getValue().suffix(1)
|
||||
or
|
||||
result = this.asErbComment().getValue().suffix(1)
|
||||
}
|
||||
class ExpectationComment extends Ruby::Comment {
|
||||
string getContents() { result = this.getValue().suffix(1) }
|
||||
}
|
||||
|
||||
class Location = R::Location;
|
||||
|
||||
@@ -23,6 +23,7 @@ nodes
|
||||
| views/index.erb:2:10:2:12 | call to foo | semmle.label | call to foo |
|
||||
subpaths
|
||||
testFailures
|
||||
| views/index.erb:2:10:2:12 | call to foo | Unexpected result: hasTaintFlow |
|
||||
#select
|
||||
| app.rb:95:10:95:14 | @user | app.rb:103:13:103:22 | call to source | app.rb:95:10:95:14 | @user | $@ | app.rb:103:13:103:22 | call to source | call to source |
|
||||
| views/index.erb:2:10:2:12 | call to foo | app.rb:75:12:75:17 | call to params | views/index.erb:2:10:2:12 | call to foo | $@ | app.rb:75:12:75:17 | call to params | call to params |
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
<%= @foo %>
|
||||
<%= sink foo %> <%# $ hasTaintFlow %>
|
||||
<%= sink foo %>
|
||||
@@ -1,4 +1,5 @@
|
||||
testFailures
|
||||
| improper_memoization.rb:100:1:104:3 | m14 | Unexpected result: result=BAD |
|
||||
#select
|
||||
| improper_memoization.rb:50:1:55:3 | m7 | improper_memoization.rb:50:8:50:10 | arg | improper_memoization.rb:51:3:53:5 | ... \|\|= ... |
|
||||
| improper_memoization.rb:58:1:63:3 | m8 | improper_memoization.rb:58:8:58:10 | arg | improper_memoization.rb:59:3:61:5 | ... \|\|= ... |
|
||||
|
||||
@@ -101,4 +101,4 @@ def m14(arg)
|
||||
@m14 ||= {}
|
||||
key = "foo/#{arg}"
|
||||
@m14[key] ||= long_running_method(arg)
|
||||
end # $ SPURIOUS: result=BAD
|
||||
end
|
||||
|
||||
@@ -66,7 +66,7 @@ impl<'a> AstNode for Node<'a> {
|
||||
|
||||
impl AstNode for yeast::Node {
|
||||
fn kind(&self) -> &str {
|
||||
yeast::Node::kind_name(self)
|
||||
yeast::Node::kind(self)
|
||||
}
|
||||
fn is_named(&self) -> bool {
|
||||
yeast::Node::is_named(self)
|
||||
@@ -882,6 +882,7 @@ fn emit_extras_in(visitor: &mut Visitor, node: Node<'_>) {
|
||||
}
|
||||
|
||||
fn traverse_yeast(tree: &yeast::Ast, visitor: &mut Visitor) {
|
||||
use yeast::Cursor;
|
||||
let mut cursor = tree.walk();
|
||||
visitor.enter_node(cursor.node());
|
||||
let mut recurse = true;
|
||||
|
||||
@@ -120,20 +120,14 @@ pub fn generate(
|
||||
)));
|
||||
dbscheme::write(&mut dbscheme_writer, &dbscheme_tail)?;
|
||||
|
||||
let mut body = vec![];
|
||||
|
||||
for c in ql_gen::create_ast_node_class(
|
||||
&ast_node_name,
|
||||
&node_location_table_name,
|
||||
&node_parent_table_name,
|
||||
) {
|
||||
body.push(ql::TopLevel::Class(c));
|
||||
}
|
||||
|
||||
for c in ql_gen::create_token_class(&token_name, &tokeninfo_name) {
|
||||
body.push(ql::TopLevel::Class(c));
|
||||
}
|
||||
|
||||
let mut body = vec![
|
||||
ql::TopLevel::Class(ql_gen::create_ast_node_class(
|
||||
&ast_node_name,
|
||||
&node_location_table_name,
|
||||
&node_parent_table_name,
|
||||
)),
|
||||
ql::TopLevel::Class(ql_gen::create_token_class(&token_name, &tokeninfo_name)),
|
||||
];
|
||||
if has_trivia_tokens {
|
||||
body.push(ql::TopLevel::Class(ql_gen::create_trivia_token_class(
|
||||
&trivia_token_name,
|
||||
|
||||
@@ -40,12 +40,9 @@ pub struct Class<'a> {
|
||||
pub qldoc: Option<String>,
|
||||
pub name: &'a str,
|
||||
pub is_abstract: bool,
|
||||
pub is_final: bool,
|
||||
pub is_private: bool,
|
||||
pub supertypes: BTreeSet<Type<'a>>,
|
||||
pub characteristic_predicate: Option<Expression<'a>>,
|
||||
pub predicates: Vec<Predicate<'a>>,
|
||||
pub alias: Option<String>,
|
||||
}
|
||||
|
||||
impl fmt::Display for Class<'_> {
|
||||
@@ -53,16 +50,6 @@ impl fmt::Display for Class<'_> {
|
||||
if let Some(qldoc) = &self.qldoc {
|
||||
write!(f, "/** {qldoc} */")?;
|
||||
}
|
||||
if self.is_final {
|
||||
write!(f, "final ")?;
|
||||
}
|
||||
if self.is_private {
|
||||
write!(f, "private ")?;
|
||||
}
|
||||
if let Some(alias) = &self.alias {
|
||||
write!(f, "class {} = {alias};", &self.name)?;
|
||||
return Ok(());
|
||||
}
|
||||
if self.is_abstract {
|
||||
write!(f, "abstract ")?;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ pub fn create_ast_node_class<'a>(
|
||||
ast_node: &'a str,
|
||||
node_location_table: &'a str,
|
||||
node_parent_table: &'a str,
|
||||
) -> [ql::Class<'a>; 2] {
|
||||
) -> ql::Class<'a> {
|
||||
// Default implementation of `toString` calls `this.getAPrimaryQlClass()`
|
||||
let to_string = ql::Predicate {
|
||||
qldoc: Some(String::from(
|
||||
@@ -132,41 +132,25 @@ pub fn create_ast_node_class<'a>(
|
||||
),
|
||||
overlay: None,
|
||||
};
|
||||
[
|
||||
ql::Class {
|
||||
qldoc: Some(String::from("The base class for all AST nodes")),
|
||||
name: "AstNodeImpl",
|
||||
is_abstract: false,
|
||||
is_final: false,
|
||||
is_private: true,
|
||||
alias: None,
|
||||
supertypes: vec![ql::Type::At(ast_node)].into_iter().collect(),
|
||||
characteristic_predicate: None,
|
||||
predicates: vec![
|
||||
to_string,
|
||||
get_location,
|
||||
get_parent,
|
||||
get_parent_index,
|
||||
get_a_field_or_child,
|
||||
get_a_primary_ql_class,
|
||||
get_primary_ql_classes,
|
||||
],
|
||||
},
|
||||
ql::Class {
|
||||
qldoc: None,
|
||||
name: "AstNode",
|
||||
is_abstract: false,
|
||||
is_final: true,
|
||||
is_private: false,
|
||||
alias: Some("AstNodeImpl".to_string()),
|
||||
supertypes: vec![].into_iter().collect(),
|
||||
characteristic_predicate: None,
|
||||
predicates: vec![],
|
||||
},
|
||||
]
|
||||
ql::Class {
|
||||
qldoc: Some(String::from("The base class for all AST nodes")),
|
||||
name: "AstNode",
|
||||
is_abstract: false,
|
||||
supertypes: vec![ql::Type::At(ast_node)].into_iter().collect(),
|
||||
characteristic_predicate: None,
|
||||
predicates: vec![
|
||||
to_string,
|
||||
get_location,
|
||||
get_parent,
|
||||
get_parent_index,
|
||||
get_a_field_or_child,
|
||||
get_a_primary_ql_class,
|
||||
get_primary_ql_classes,
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_token_class<'a>(token_type: &'a str, tokeninfo: &'a str) -> [ql::Class<'a>; 2] {
|
||||
pub fn create_token_class<'a>(token_type: &'a str, tokeninfo: &'a str) -> ql::Class<'a> {
|
||||
let tokeninfo_arity = 3; // id, kind, value
|
||||
let get_value = ql::Predicate {
|
||||
qldoc: Some(String::from("Gets the value of this token.")),
|
||||
@@ -199,36 +183,20 @@ pub fn create_token_class<'a>(token_type: &'a str, tokeninfo: &'a str) -> [ql::C
|
||||
),
|
||||
overlay: None,
|
||||
};
|
||||
[
|
||||
ql::Class {
|
||||
qldoc: Some(String::from("A token.")),
|
||||
name: "TokenImpl",
|
||||
is_abstract: false,
|
||||
is_final: false,
|
||||
is_private: true,
|
||||
alias: None,
|
||||
supertypes: vec![ql::Type::At(token_type), ql::Type::Normal("AstNodeImpl")]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
characteristic_predicate: None,
|
||||
predicates: vec![
|
||||
get_value,
|
||||
to_string,
|
||||
create_get_a_primary_ql_class("Token", false),
|
||||
],
|
||||
},
|
||||
ql::Class {
|
||||
qldoc: None,
|
||||
name: "Token",
|
||||
is_abstract: false,
|
||||
is_final: true,
|
||||
is_private: false,
|
||||
alias: Some("TokenImpl".to_string()),
|
||||
supertypes: vec![].into_iter().collect(),
|
||||
characteristic_predicate: None,
|
||||
predicates: vec![],
|
||||
},
|
||||
]
|
||||
ql::Class {
|
||||
qldoc: Some(String::from("A token.")),
|
||||
name: "Token",
|
||||
is_abstract: false,
|
||||
supertypes: vec![ql::Type::At(token_type), ql::Type::Normal("AstNode")]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
characteristic_predicate: None,
|
||||
predicates: vec![
|
||||
get_value,
|
||||
to_string,
|
||||
create_get_a_primary_ql_class("Token", false),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates the `TriviaToken` class. Trivia tokens (e.g. comments) are
|
||||
@@ -283,15 +251,9 @@ pub fn create_trivia_token_class<'a>(
|
||||
)),
|
||||
name: "TriviaToken",
|
||||
is_abstract: false,
|
||||
is_final: true,
|
||||
is_private: false,
|
||||
alias: None,
|
||||
supertypes: vec![
|
||||
ql::Type::At(trivia_token_type),
|
||||
ql::Type::Normal("AstNodeImpl"),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
supertypes: vec![ql::Type::At(trivia_token_type), ql::Type::Normal("AstNode")]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
characteristic_predicate: None,
|
||||
predicates: vec![
|
||||
get_value,
|
||||
@@ -309,10 +271,7 @@ pub fn create_reserved_word_class(db_name: &str) -> ql::Class<'_> {
|
||||
qldoc: Some(String::from("A reserved word.")),
|
||||
name: class_name,
|
||||
is_abstract: false,
|
||||
is_final: true,
|
||||
is_private: false,
|
||||
alias: None,
|
||||
supertypes: vec![ql::Type::At(db_name), ql::Type::Normal("TokenImpl")]
|
||||
supertypes: vec![ql::Type::At(db_name), ql::Type::Normal("Token")]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
characteristic_predicate: None,
|
||||
@@ -816,14 +775,11 @@ pub fn convert_nodes(nodes: &node_types::NodeTypeMap) -> Vec<ql::TopLevel<'_>> {
|
||||
create_get_a_primary_ql_class(&node.ql_class_name, true);
|
||||
let mut supertypes: BTreeSet<ql::Type> = BTreeSet::new();
|
||||
supertypes.insert(ql::Type::At(&node.dbscheme_name));
|
||||
supertypes.insert(ql::Type::Normal("TokenImpl"));
|
||||
supertypes.insert(ql::Type::Normal("Token"));
|
||||
classes.push(ql::TopLevel::Class(ql::Class {
|
||||
qldoc: Some(format!("A class representing `{}` tokens.", type_name.kind)),
|
||||
name: &node.ql_class_name,
|
||||
is_abstract: false,
|
||||
is_final: true,
|
||||
is_private: false,
|
||||
alias: None,
|
||||
supertypes,
|
||||
characteristic_predicate: None,
|
||||
predicates: vec![get_a_primary_ql_class],
|
||||
@@ -837,12 +793,9 @@ pub fn convert_nodes(nodes: &node_types::NodeTypeMap) -> Vec<ql::TopLevel<'_>> {
|
||||
qldoc: None,
|
||||
name: &node.ql_class_name,
|
||||
is_abstract: false,
|
||||
is_final: true,
|
||||
is_private: false,
|
||||
alias: None,
|
||||
supertypes: vec![
|
||||
ql::Type::At(&node.dbscheme_name),
|
||||
ql::Type::Normal("AstNodeImpl"),
|
||||
ql::Type::Normal("AstNode"),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
@@ -871,12 +824,9 @@ pub fn convert_nodes(nodes: &node_types::NodeTypeMap) -> Vec<ql::TopLevel<'_>> {
|
||||
qldoc: Some(format!("A class representing `{}` nodes.", type_name.kind)),
|
||||
name: main_class_name,
|
||||
is_abstract: false,
|
||||
is_final: true,
|
||||
is_private: false,
|
||||
alias: None,
|
||||
supertypes: vec![
|
||||
ql::Type::At(&node.dbscheme_name),
|
||||
ql::Type::Normal("AstNodeImpl"),
|
||||
ql::Type::Normal("AstNode"),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
|
||||
@@ -41,14 +41,22 @@ pub fn query(input: TokenStream) -> TokenStream {
|
||||
/// (kind "literal") - leaf with static content
|
||||
/// (kind #{expr}) - leaf with computed content (expr.to_string())
|
||||
/// (kind $fresh) - leaf with auto-generated unique name
|
||||
/// {expr} - embed a Rust expression, dispatched via
|
||||
/// the `IntoFieldIds` trait: `Id` pushes a
|
||||
/// single id; iterables (`Vec<Id>`,
|
||||
/// `Option<Id>`, iterator chains) splice
|
||||
/// their elements
|
||||
/// field: {expr} - extend a named field with `{expr}`'s ids
|
||||
/// {expr} - embed a Rust expression returning Id
|
||||
/// {..expr} - splice an iterable of Id (in child/field position)
|
||||
/// field: {..expr} - splice into a named field
|
||||
/// {expr}.map(p -> tpl) - apply tpl to each element; splice result
|
||||
/// {expr}.reduce_left(f -> init, acc, e -> fold)
|
||||
/// - fold with per-element init; splice 0 or 1 result
|
||||
/// ```
|
||||
///
|
||||
/// Chain syntax after `{expr}` or `{..expr}`:
|
||||
/// - `.map(param -> template)` — one output node per input element.
|
||||
/// - `.reduce_left(first -> init, acc, elem -> fold)` — fold left; the first
|
||||
/// element is converted by `init`, subsequent elements are folded by `fold`
|
||||
/// with the accumulator bound to `acc`. An empty iterable yields nothing.
|
||||
/// - Chains always splice (the result is iterable).
|
||||
/// - Multiple chains can be chained, e.g. `.map(...).reduce_left(...)`.
|
||||
///
|
||||
/// Can be called with an explicit context or using the implicit context
|
||||
/// from an enclosing `rule!`:
|
||||
///
|
||||
@@ -92,7 +100,7 @@ pub fn trees(input: TokenStream) -> TokenStream {
|
||||
/// rule!(
|
||||
/// (query_pattern field: (_) @name (kind)* @repeated (_)? @optional)
|
||||
/// =>
|
||||
/// (output_template field: {name} {repeated})
|
||||
/// (output_template field: {name} {..repeated})
|
||||
/// )
|
||||
///
|
||||
/// // Shorthand: captures become fields on the output node
|
||||
|
||||
@@ -304,8 +304,7 @@ fn parse_ctx_or_implicit(tokens: &mut Tokens) -> Ident {
|
||||
&& matches!(lookahead.next(), Some(TokenTree::Punct(p)) if p.as_char() == ',');
|
||||
|
||||
if is_explicit {
|
||||
let ctx = expect_ident(tokens, "unreachable: ident was just peeked")
|
||||
.expect("unreachable: ident was just peeked");
|
||||
let ctx = expect_ident(tokens, "").unwrap();
|
||||
let _ = tokens.next(); // consume comma
|
||||
ctx
|
||||
} else {
|
||||
@@ -343,7 +342,7 @@ pub fn parse_trees_top(input: TokenStream) -> Result<TokenStream> {
|
||||
}
|
||||
Ok(quote! {
|
||||
{
|
||||
let mut __nodes: Vec<yeast::Id> = Vec::new();
|
||||
let mut __nodes: Vec<usize> = Vec::new();
|
||||
#(#items)*
|
||||
__nodes
|
||||
}
|
||||
@@ -357,7 +356,7 @@ fn parse_direct_node(tokens: &mut Tokens, ctx: &Ident) -> Result<TokenStream> {
|
||||
Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Brace => {
|
||||
let group = expect_group(tokens, Delimiter::Brace)?;
|
||||
let expr = group.stream();
|
||||
Ok(quote! { ::std::convert::Into::<yeast::Id>::into({ #expr }) })
|
||||
Ok(quote! { ::std::convert::Into::<usize>::into({ #expr }) })
|
||||
}
|
||||
Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Parenthesis => {
|
||||
let group = expect_group(tokens, Delimiter::Parenthesis)?;
|
||||
@@ -430,24 +429,49 @@ fn parse_direct_node_inner(tokens: &mut Tokens, ctx: &Ident) -> Result<TokenStre
|
||||
);
|
||||
field_counter += 1;
|
||||
|
||||
// Plain `field: {expr}` — trait-dispatched extend.
|
||||
// Check for field: {..expr}.chain or field: {expr}.chain — splice a Vec<Id> into the field
|
||||
if peek_is_group(tokens, Delimiter::Brace) {
|
||||
let group = expect_group(tokens, Delimiter::Brace)?;
|
||||
let expr = group.stream();
|
||||
stmts.push(quote! {
|
||||
let mut #temp: Vec<yeast::Id> = Vec::new();
|
||||
yeast::IntoFieldIds::extend_into({ #expr }, &mut #temp);
|
||||
});
|
||||
// An empty `{expr}` means the field is absent — skip it
|
||||
// entirely rather than emitting an empty named field.
|
||||
field_args.push(quote! {
|
||||
if !#temp.is_empty() { __fields.push((#field_str, #temp)); }
|
||||
});
|
||||
continue;
|
||||
let group_clone = tokens.clone().next().unwrap();
|
||||
if let TokenTree::Group(g) = &group_clone {
|
||||
let mut inner_check = g.stream().into_iter();
|
||||
let is_splice = matches!(inner_check.next(), Some(TokenTree::Punct(p)) if p.as_char() == '.')
|
||||
&& matches!(inner_check.next(), Some(TokenTree::Punct(p)) if p.as_char() == '.');
|
||||
// Determine if a chain (.map(..)) follows the `{}` group.
|
||||
let mut after = tokens.clone();
|
||||
after.next(); // skip the brace group
|
||||
let has_chain =
|
||||
matches!(after.peek(), Some(TokenTree::Punct(p)) if p.as_char() == '.');
|
||||
|
||||
if is_splice || has_chain {
|
||||
let group = expect_group(tokens, Delimiter::Brace)?;
|
||||
let base: TokenStream = if is_splice {
|
||||
let mut inner = group.stream().into_iter().peekable();
|
||||
inner.next(); // consume first .
|
||||
inner.next(); // consume second .
|
||||
let expr: TokenStream = inner.collect();
|
||||
quote! {
|
||||
{ #expr }.into_iter().map(::std::convert::Into::<usize>::into)
|
||||
}
|
||||
} else {
|
||||
let expr = group.stream();
|
||||
quote! { { #expr }.into_iter() }
|
||||
};
|
||||
let chained = parse_chain_suffix(tokens, ctx, base)?;
|
||||
stmts.push(quote! {
|
||||
let #temp: Vec<usize> = #chained.collect();
|
||||
});
|
||||
// An empty splice means the field is absent — skip it
|
||||
// entirely rather than emitting an empty named field.
|
||||
field_args.push(quote! {
|
||||
if !#temp.is_empty() { __fields.push((#field_str, #temp)); }
|
||||
});
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let value = parse_direct_node(tokens, ctx)?;
|
||||
stmts.push(quote! { let #temp: yeast::Id = #value; });
|
||||
stmts.push(quote! { let #temp: usize = #value; });
|
||||
field_args.push(quote! { __fields.push((#field_str, vec![#temp])); });
|
||||
}
|
||||
|
||||
@@ -464,13 +488,101 @@ fn parse_direct_node_inner(tokens: &mut Tokens, ctx: &Ident) -> Result<TokenStre
|
||||
Ok(quote! {
|
||||
{
|
||||
#(#stmts)*
|
||||
let mut __fields: Vec<(&str, Vec<yeast::Id>)> = Vec::new();
|
||||
let mut __fields: Vec<(&str, Vec<usize>)> = Vec::new();
|
||||
#(#field_args)*
|
||||
#ctx.node(#kind_str, __fields)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Parse a chain of `.method(args)` suffixes after a `{expr}` or `{..expr}`
|
||||
/// placeholder in tree templates. Currently supports:
|
||||
///
|
||||
/// ```text
|
||||
/// .map(param -> template) -- iterator map: produces Vec<usize>
|
||||
/// ```
|
||||
///
|
||||
/// The chain may be empty (returns `base` unchanged). Multiple chained calls
|
||||
/// are supported, e.g. `.map(p -> ...).map(q -> ...)`.
|
||||
///
|
||||
/// Each call expects the receiver to be an iterator. The `base` argument
|
||||
/// should therefore already be an iterator (use `.into_iter()` on it before
|
||||
/// calling this function).
|
||||
fn parse_chain_suffix(tokens: &mut Tokens, ctx: &Ident, base: TokenStream) -> Result<TokenStream> {
|
||||
let mut current = base;
|
||||
while matches!(tokens.peek(), Some(TokenTree::Punct(p)) if p.as_char() == '.') {
|
||||
tokens.next(); // consume .
|
||||
let method = expect_ident(tokens, "expected method name after `.`")?;
|
||||
let method_str = method.to_string();
|
||||
let args_group = expect_group(tokens, Delimiter::Parenthesis)?;
|
||||
match method_str.as_str() {
|
||||
"map" => {
|
||||
let mut inner = args_group.stream().into_iter().peekable();
|
||||
let param = expect_ident(&mut inner, "expected lambda parameter name")?;
|
||||
expect_punct(&mut inner, '-', "expected `->` after lambda parameter")?;
|
||||
expect_punct(&mut inner, '>', "expected `->` after lambda parameter")?;
|
||||
let body = parse_direct_node(&mut inner, ctx)?;
|
||||
if let Some(tok) = inner.next() {
|
||||
return Err(syn::Error::new_spanned(
|
||||
tok,
|
||||
"unexpected token after lambda body",
|
||||
));
|
||||
}
|
||||
current = quote! {
|
||||
#current.map(|#param| #body)
|
||||
};
|
||||
}
|
||||
"reduce_left" => {
|
||||
// Syntax: reduce_left(first -> init_tpl, acc, elem -> fold_tpl)
|
||||
// - first -> init_tpl : converts the first element to the initial accumulator
|
||||
// - acc, elem -> fold_tpl : fold step (acc = current accumulator, elem = next element)
|
||||
// Empty iterator produces an empty iterator; non-empty produces a single-element iterator.
|
||||
let mut inner = args_group.stream().into_iter().peekable();
|
||||
let init_param = expect_ident(&mut inner, "expected initial lambda parameter")?;
|
||||
expect_punct(&mut inner, '-', "expected `->` after init parameter")?;
|
||||
expect_punct(&mut inner, '>', "expected `->` after init parameter")?;
|
||||
let init_body = parse_direct_node(&mut inner, ctx)?;
|
||||
expect_punct(&mut inner, ',', "expected `,` after init template")?;
|
||||
let acc_param = expect_ident(&mut inner, "expected accumulator parameter")?;
|
||||
expect_punct(&mut inner, ',', "expected `,` after accumulator parameter")?;
|
||||
let elem_param = expect_ident(&mut inner, "expected element parameter")?;
|
||||
expect_punct(&mut inner, '-', "expected `->` after element parameter")?;
|
||||
expect_punct(&mut inner, '>', "expected `->` after element parameter")?;
|
||||
let fold_body = parse_direct_node(&mut inner, ctx)?;
|
||||
if let Some(tok) = inner.next() {
|
||||
return Err(syn::Error::new_spanned(
|
||||
tok,
|
||||
"unexpected token after fold template",
|
||||
));
|
||||
}
|
||||
current = quote! {
|
||||
{
|
||||
let mut __iter = #current;
|
||||
let __result: Option<usize> = if let Some(#init_param) = __iter.next() {
|
||||
let mut __acc: usize = #init_body;
|
||||
for #elem_param in __iter {
|
||||
let #acc_param: usize = __acc;
|
||||
__acc = #fold_body;
|
||||
}
|
||||
Some(__acc)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
__result.into_iter()
|
||||
}
|
||||
};
|
||||
}
|
||||
_ => {
|
||||
return Err(syn::Error::new_spanned(
|
||||
method,
|
||||
format!("unknown builtin method `.{method_str}()`"),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(current)
|
||||
}
|
||||
|
||||
/// Parse the top-level list of a `trees!` template.
|
||||
/// Each item is a node template or `{expr}` splice.
|
||||
fn parse_direct_list(tokens: &mut Tokens, ctx: &Ident) -> Result<Vec<TokenStream>> {
|
||||
@@ -491,14 +603,35 @@ fn parse_direct_list(tokens: &mut Tokens, ctx: &Ident) -> Result<Vec<TokenStream
|
||||
continue;
|
||||
}
|
||||
|
||||
// `{expr}` — extend `__nodes` via `IntoFieldIds`, which handles
|
||||
// single ids and iterables uniformly.
|
||||
// {expr} or {..expr} (with optional .chain) — single node or splice
|
||||
if peek_is_group(tokens, Delimiter::Brace) {
|
||||
let group = expect_group(tokens, Delimiter::Brace)?;
|
||||
let expr = group.stream();
|
||||
items.push(quote! {
|
||||
yeast::IntoFieldIds::extend_into({ #expr }, &mut __nodes);
|
||||
});
|
||||
let has_chain =
|
||||
matches!(tokens.peek(), Some(TokenTree::Punct(p)) if p.as_char() == '.');
|
||||
let mut inner = group.stream().into_iter().peekable();
|
||||
let is_splice = peek_is_dotdot(&inner);
|
||||
if is_splice || has_chain {
|
||||
let base: TokenStream = if is_splice {
|
||||
inner.next(); // consume first .
|
||||
inner.next(); // consume second .
|
||||
let expr: TokenStream = inner.collect();
|
||||
quote! {
|
||||
{ #expr }.into_iter().map(::std::convert::Into::<usize>::into)
|
||||
}
|
||||
} else {
|
||||
let expr = group.stream();
|
||||
quote! { { #expr }.into_iter() }
|
||||
};
|
||||
let chained = parse_chain_suffix(tokens, ctx, base)?;
|
||||
items.push(quote! {
|
||||
__nodes.extend(#chained);
|
||||
});
|
||||
} else {
|
||||
let expr = group.stream();
|
||||
items.push(quote! {
|
||||
__nodes.push(::std::convert::Into::<usize>::into({ #expr }));
|
||||
});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -516,7 +649,7 @@ struct CaptureInfo {
|
||||
name: String,
|
||||
multiplicity: CaptureMultiplicity,
|
||||
/// `true` for `@@name` captures: the auto-translate prefix skips them,
|
||||
/// so the bound `Id` refers to the raw (input-schema) node.
|
||||
/// so the bound `NodeRef` refers to the raw (input-schema) node.
|
||||
raw: bool,
|
||||
}
|
||||
|
||||
@@ -671,17 +804,22 @@ pub fn parse_rule_top(input: TokenStream) -> Result<TokenStream> {
|
||||
match cap.multiplicity {
|
||||
CaptureMultiplicity::Repeated => {
|
||||
quote! {
|
||||
let #name: Vec<yeast::Id> = __captures.get_all(#name_str);
|
||||
let #name: Vec<yeast::NodeRef> = __captures.get_all(#name_str)
|
||||
.into_iter()
|
||||
.map(yeast::NodeRef)
|
||||
.collect();
|
||||
}
|
||||
}
|
||||
CaptureMultiplicity::Optional => {
|
||||
quote! {
|
||||
let #name: Option<yeast::Id> = __captures.get_opt(#name_str);
|
||||
let #name: Option<yeast::NodeRef> =
|
||||
__captures.get_opt(#name_str).map(yeast::NodeRef);
|
||||
}
|
||||
}
|
||||
CaptureMultiplicity::Single => {
|
||||
quote! {
|
||||
let #name: yeast::Id = __captures.get_var(#name_str).unwrap();
|
||||
let #name: yeast::NodeRef =
|
||||
yeast::NodeRef(__captures.get_var(#name_str).unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -712,7 +850,7 @@ pub fn parse_rule_top(input: TokenStream) -> Result<TokenStream> {
|
||||
__fields.insert(
|
||||
__field_id,
|
||||
#name.into_iter()
|
||||
.map(::std::convert::Into::<yeast::Id>::into)
|
||||
.map(::std::convert::Into::<usize>::into)
|
||||
.collect(),
|
||||
);
|
||||
},
|
||||
@@ -721,14 +859,14 @@ pub fn parse_rule_top(input: TokenStream) -> Result<TokenStream> {
|
||||
.unwrap_or_else(|| panic!("field '{}' not found", #name_str));
|
||||
if let Some(__id) = #name {
|
||||
__fields.entry(__field_id).or_insert_with(Vec::new)
|
||||
.push(::std::convert::Into::<yeast::Id>::into(__id));
|
||||
.push(::std::convert::Into::<usize>::into(__id));
|
||||
}
|
||||
},
|
||||
CaptureMultiplicity::Single => quote! {
|
||||
let __field_id = #ctx_ident.ast.field_id_for_name(#name_str)
|
||||
.unwrap_or_else(|| panic!("field '{}' not found", #name_str));
|
||||
__fields.entry(__field_id).or_insert_with(Vec::new)
|
||||
.push(::std::convert::Into::<yeast::Id>::into(#name));
|
||||
.push(::std::convert::Into::<usize>::into(#name));
|
||||
},
|
||||
}
|
||||
})
|
||||
@@ -760,7 +898,7 @@ pub fn parse_rule_top(input: TokenStream) -> Result<TokenStream> {
|
||||
}
|
||||
|
||||
quote! {
|
||||
let mut __nodes: Vec<yeast::Id> = Vec::new();
|
||||
let mut __nodes: Vec<usize> = Vec::new();
|
||||
#(#transform_items)*
|
||||
__nodes
|
||||
}
|
||||
@@ -781,7 +919,7 @@ pub fn parse_rule_top(input: TokenStream) -> Result<TokenStream> {
|
||||
__translator.auto_translate_captures(&mut __captures, __ast, __user_ctx, __skip)?;
|
||||
#(#bindings)*
|
||||
let mut #ctx_ident = yeast::build::BuildCtx::with_translator(__ast, &__captures, __fresh, __source_range, __user_ctx, __translator);
|
||||
let __result: Vec<yeast::Id> = { #transform_body };
|
||||
let __result: Vec<usize> = { #transform_body };
|
||||
Ok(__result)
|
||||
}))
|
||||
}
|
||||
@@ -818,6 +956,13 @@ fn peek_is_hash(tokens: &mut Tokens) -> bool {
|
||||
matches!(tokens.peek(), Some(TokenTree::Punct(p)) if p.as_char() == '#')
|
||||
}
|
||||
|
||||
/// Check for `..` (two consecutive dot punctuation tokens).
|
||||
fn peek_is_dotdot(tokens: &Tokens) -> bool {
|
||||
let mut lookahead = tokens.clone();
|
||||
matches!(lookahead.next(), Some(TokenTree::Punct(p)) if p.as_char() == '.')
|
||||
&& matches!(lookahead.next(), Some(TokenTree::Punct(p)) if p.as_char() == '.')
|
||||
}
|
||||
|
||||
fn peek_is_underscore(tokens: &mut Tokens) -> bool {
|
||||
matches!(tokens.peek(), Some(TokenTree::Ident(id)) if *id == "_")
|
||||
}
|
||||
|
||||
@@ -214,7 +214,7 @@ yeast::tree!(ctx,
|
||||
```rust
|
||||
yeast::trees!(ctx,
|
||||
(assignment left: {tmp} right: {right})
|
||||
{body}
|
||||
{..body}
|
||||
)
|
||||
```
|
||||
|
||||
@@ -256,26 +256,12 @@ occurrences of the same `$name` within one `BuildCtx` share the same value:
|
||||
|
||||
### Embedded Rust expressions
|
||||
|
||||
`{expr}` embeds a Rust expression whose value is appended to the
|
||||
enclosing field (or to the rule body's id list). Dispatch happens via
|
||||
the [`IntoFieldIds`] trait, which is implemented for:
|
||||
|
||||
- `Id` — pushes the single id.
|
||||
- Any `IntoIterator<Item: Into<Id>>` — extends with all yielded ids
|
||||
(covers `Vec<Id>`, `Option<Id>`, iterator chains, etc.).
|
||||
|
||||
So the same `{expr}` syntax handles single ids, splices, and zero-or-many
|
||||
options uniformly:
|
||||
`{expr}` embeds a Rust expression that returns a single node `Id`:
|
||||
|
||||
```rust
|
||||
(assignment
|
||||
left: {some_node_id} // a single Id
|
||||
right: {rhs} // a captured value (inside rule!)
|
||||
)
|
||||
|
||||
yeast::trees!(ctx,
|
||||
(assignment left: {tmp} right: {right})
|
||||
{extra_nodes} // splices a Vec<Id>
|
||||
left: {some_node_id} // insert a pre-built node
|
||||
right: {rhs} // insert a captured value (inside rule!)
|
||||
)
|
||||
```
|
||||
|
||||
@@ -291,17 +277,21 @@ expressions (with `let` bindings) work too:
|
||||
})
|
||||
```
|
||||
|
||||
Inside `rule!`, captures are Rust variables — `{name}` works for
|
||||
single, optional, and repeated captures alike:
|
||||
`{..expr}` splices a `Vec<Id>` (or any iterable of `Id`); the contents
|
||||
are likewise a Rust block, so the splice can be the result of arbitrary
|
||||
computation:
|
||||
|
||||
```rust
|
||||
rule!(
|
||||
(assignment left: @lhs right: _* @parts)
|
||||
=>
|
||||
(assignment left: {lhs} right: (block stmt: {parts}))
|
||||
yeast::trees!(ctx,
|
||||
(assignment left: {tmp} right: {right})
|
||||
{..extra_nodes} // splice a Vec<Id>
|
||||
)
|
||||
```
|
||||
|
||||
Inside `rule!`, captures are Rust variables, so `{name}` inserts a
|
||||
single capture (`Id`) and `{..name}` splices a repeated capture
|
||||
(`Vec<Id>`).
|
||||
|
||||
### Raw captures (`@@name`)
|
||||
|
||||
The default `@name` capture marker is *auto-translated*: in OneShot
|
||||
@@ -312,7 +302,7 @@ already conforms to the output schema.
|
||||
For rules that need the raw (input-schema) capture — typically to read
|
||||
its source text or to translate it explicitly with mutable context
|
||||
state between calls — use `@@name` instead. The body sees the original
|
||||
input-schema `Id`:
|
||||
input-schema `NodeRef`:
|
||||
|
||||
```rust
|
||||
yeast::rule!(
|
||||
@@ -320,7 +310,7 @@ yeast::rule!(
|
||||
=>
|
||||
{
|
||||
// raw_lhs is untranslated: read its original source text.
|
||||
let text = ctx.ast.source_text(raw_lhs);
|
||||
let text = ctx.ast.source_text(raw_lhs.into());
|
||||
// rhs is already translated by the auto-translate prefix.
|
||||
tree!((call
|
||||
method: (identifier #{text.as_str()})
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user