mirror of
https://github.com/github/codeql.git
synced 2026-05-05 05:35:13 +02:00
Merge branch 'main' into rdmarsh2/swift/keypath-write-flow
This commit is contained in:
@@ -11,12 +11,12 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.IO.FileSystem" Version="4.3.0" />
|
||||
<PackageReference Include="System.IO.FileSystem.Primitives" Version="4.3.0" />
|
||||
<PackageReference Include="xunit" Version="2.5.0" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
|
||||
<PackageReference Include="xunit" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Build" Version="17.7.2" />
|
||||
<PackageReference Include="Microsoft.Build" Version="17.3.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The `DataFlow::asDefiningArgument` predicate now takes its argument from the range starting at `1` instead of `2`. Queries that depend on the single-parameter version of `DataFlow::asDefiningArgument` should have their arguments updated accordingly.
|
||||
5
cpp/ql/lib/change-notes/2023-09-07-return-from-end.md
Normal file
5
cpp/ql/lib/change-notes/2023-09-07-return-from-end.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Treat functions that reach the end of the function as returning in the IR.
|
||||
They used to be treated as unreachable but it is allowed in C.
|
||||
@@ -254,9 +254,7 @@ class Node extends TIRDataFlowNode {
|
||||
* after the `f` has returned.
|
||||
*/
|
||||
Expr asDefiningArgument(int index) {
|
||||
// Subtract one because `DefinitionByReferenceNode` is defined to be in
|
||||
// the range `[0 ... n - 1]` for some `n` instead of `[1 ... n]`.
|
||||
this.(DefinitionByReferenceNode).getIndirectionIndex() = index - 1 and
|
||||
this.(DefinitionByReferenceNode).getIndirectionIndex() = index and
|
||||
result = this.(DefinitionByReferenceNode).getArgument()
|
||||
}
|
||||
|
||||
|
||||
@@ -405,9 +405,6 @@ predicate hasUnreachedInstruction(IRFunction func) {
|
||||
exists(Call c |
|
||||
c.getEnclosingFunction() = func.getFunction() and
|
||||
any(Options opt).exits(c.getTarget())
|
||||
) and
|
||||
not exists(TranslatedUnreachableReturnStmt return |
|
||||
return.getEnclosingFunction().getFunction() = func.getFunction()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -442,29 +442,26 @@ class TranslatedReturnVoidStmt extends TranslatedReturnStmt {
|
||||
|
||||
/**
|
||||
* The IR translation of an implicit `return` statement generated by the extractor to handle control
|
||||
* flow that reaches the end of a non-`void`-returning function body. Since such control flow
|
||||
* produces undefined behavior, we simply generate an `Unreached` instruction to prevent that flow
|
||||
* from continuing on to pollute other analysis. The assumption is that the developer is certain
|
||||
* that the implicit `return` is unreachable, even if the compiler cannot prove it.
|
||||
* flow that reaches the end of a non-`void`-returning function body. Such control flow
|
||||
* produces undefined behavior in C++ but not in C. However even in C using the return value is
|
||||
* undefined behaviour. We make it return uninitialized memory to get as much flow as possible.
|
||||
*/
|
||||
class TranslatedUnreachableReturnStmt extends TranslatedReturnStmt {
|
||||
TranslatedUnreachableReturnStmt() {
|
||||
class TranslatedNoValueReturnStmt extends TranslatedReturnStmt, TranslatedVariableInitialization {
|
||||
TranslatedNoValueReturnStmt() {
|
||||
not stmt.hasExpr() and hasReturnValue(stmt.getEnclosingFunction())
|
||||
}
|
||||
|
||||
override TranslatedElement getChild(int id) { none() }
|
||||
|
||||
override Instruction getFirstInstruction() { result = this.getInstruction(OnlyInstructionTag()) }
|
||||
|
||||
override predicate hasInstruction(Opcode opcode, InstructionTag tag, CppType resultType) {
|
||||
tag = OnlyInstructionTag() and
|
||||
opcode instanceof Opcode::Unreached and
|
||||
resultType = getVoidType()
|
||||
final override Instruction getInitializationSuccessor() {
|
||||
result = this.getEnclosingFunction().getReturnSuccessorInstruction()
|
||||
}
|
||||
|
||||
override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) { none() }
|
||||
final override Type getTargetType() { result = this.getEnclosingFunction().getReturnType() }
|
||||
|
||||
override Instruction getChildSuccessor(TranslatedElement child) { none() }
|
||||
final override TranslatedInitialization getInitialization() { none() }
|
||||
|
||||
final override IRVariable getIRVariable() {
|
||||
result = this.getEnclosingFunction().getReturnVariable()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6207,10 +6207,12 @@
|
||||
| ir.cpp:1286:25:1286:49 | ChiPartial | partial:m1286_7 |
|
||||
| ir.cpp:1286:25:1286:49 | ChiTotal | total:m1286_4 |
|
||||
| ir.cpp:1286:25:1286:49 | SideEffect | ~m1286_4 |
|
||||
| ir.cpp:1289:5:1289:22 | Address | &:r1289_9 |
|
||||
| ir.cpp:1289:5:1289:22 | Address | &:r1289_10 |
|
||||
| ir.cpp:1289:5:1289:22 | ChiPartial | partial:m1289_3 |
|
||||
| ir.cpp:1289:5:1289:22 | ChiTotal | total:m1289_2 |
|
||||
| ir.cpp:1289:5:1289:22 | Load | m1291_4 |
|
||||
| ir.cpp:1289:5:1289:22 | Load | m1289_9 |
|
||||
| ir.cpp:1289:5:1289:22 | Phi | from 2:m1291_4 |
|
||||
| ir.cpp:1289:5:1289:22 | Phi | from 3:m1293_2 |
|
||||
| ir.cpp:1289:5:1289:22 | SideEffect | m1289_3 |
|
||||
| ir.cpp:1289:29:1289:29 | Address | &:r1289_5 |
|
||||
| ir.cpp:1289:36:1289:36 | Address | &:r1289_7 |
|
||||
@@ -6221,6 +6223,7 @@
|
||||
| ir.cpp:1291:16:1291:16 | Address | &:r1291_2 |
|
||||
| ir.cpp:1291:16:1291:16 | Load | m1289_8 |
|
||||
| ir.cpp:1291:16:1291:16 | StoreValue | r1291_3 |
|
||||
| ir.cpp:1293:1:1293:1 | Address | &:r1293_1 |
|
||||
| ir.cpp:1295:6:1295:15 | ChiPartial | partial:m1295_3 |
|
||||
| ir.cpp:1295:6:1295:15 | ChiTotal | total:m1295_2 |
|
||||
| ir.cpp:1295:6:1295:15 | SideEffect | ~m1296_8 |
|
||||
@@ -8393,16 +8396,23 @@
|
||||
| ir.cpp:1747:39:1747:39 | ChiTotal | total:m1747_20 |
|
||||
| ir.cpp:1747:39:1747:39 | SideEffect | ~m1747_4 |
|
||||
| ir.cpp:1747:39:1747:39 | SideEffect | ~m1747_15 |
|
||||
| ir.cpp:1750:5:1750:34 | Address | &:r1750_5 |
|
||||
| ir.cpp:1750:5:1750:34 | ChiPartial | partial:m1750_3 |
|
||||
| ir.cpp:1750:5:1750:34 | ChiTotal | total:m1750_2 |
|
||||
| ir.cpp:1750:5:1750:34 | Load | m1755_2 |
|
||||
| ir.cpp:1750:5:1750:34 | SideEffect | ~m1754_10 |
|
||||
| ir.cpp:1751:51:1751:51 | Address | &:r1751_1 |
|
||||
| ir.cpp:1751:51:1751:51 | Address | &:r1751_1 |
|
||||
| ir.cpp:1751:51:1751:51 | Address | &:r1751_3 |
|
||||
| ir.cpp:1751:51:1751:51 | Address | &:r1751_3 |
|
||||
| ir.cpp:1751:51:1751:51 | Load | m1751_2 |
|
||||
| ir.cpp:1751:51:1751:51 | SideEffect | m1751_4 |
|
||||
| ir.cpp:1752:48:1752:48 | Address | &:r1752_1 |
|
||||
| ir.cpp:1752:48:1752:48 | Address | &:r1752_1 |
|
||||
| ir.cpp:1752:48:1752:48 | Address | &:r1752_3 |
|
||||
| ir.cpp:1752:48:1752:48 | Address | &:r1752_3 |
|
||||
| ir.cpp:1752:48:1752:48 | Load | m1752_2 |
|
||||
| ir.cpp:1752:48:1752:48 | SideEffect | m1752_4 |
|
||||
| ir.cpp:1753:40:1753:41 | Address | &:r1753_1 |
|
||||
| ir.cpp:1753:40:1753:41 | Address | &:r1753_1 |
|
||||
| ir.cpp:1753:40:1753:41 | Arg(this) | this:r1753_1 |
|
||||
@@ -8435,6 +8445,7 @@
|
||||
| ir.cpp:1754:42:1754:42 | SideEffect | ~m1752_4 |
|
||||
| ir.cpp:1754:42:1754:42 | Unary | r1754_5 |
|
||||
| ir.cpp:1754:42:1754:42 | Unary | r1754_6 |
|
||||
| ir.cpp:1755:1:1755:1 | Address | &:r1755_1 |
|
||||
| ir.cpp:1757:6:1757:22 | ChiPartial | partial:m1757_3 |
|
||||
| ir.cpp:1757:6:1757:22 | ChiTotal | total:m1757_2 |
|
||||
| ir.cpp:1757:6:1757:22 | SideEffect | m1757_3 |
|
||||
@@ -9588,22 +9599,27 @@
|
||||
| ir.cpp:2021:23:2021:40 | SideEffect | ~m2021_27 |
|
||||
| ir.cpp:2021:23:2021:40 | Unary | r2021_20 |
|
||||
| ir.cpp:2021:23:2021:40 | Unary | r2021_28 |
|
||||
| ir.cpp:2026:14:2026:22 | Address | &:r2026_7 |
|
||||
| ir.cpp:2026:14:2026:22 | ChiPartial | partial:m2026_3 |
|
||||
| ir.cpp:2026:14:2026:22 | ChiTotal | total:m2026_2 |
|
||||
| ir.cpp:2026:14:2026:22 | Load | m2031_2 |
|
||||
| ir.cpp:2026:14:2026:22 | SideEffect | ~m2028_6 |
|
||||
| ir.cpp:2026:37:2026:37 | Address | &:r2026_5 |
|
||||
| ir.cpp:2027:16:2027:16 | Address | &:r2027_1 |
|
||||
| ir.cpp:2028:3:2028:3 | Address | &:r2028_9 |
|
||||
| ir.cpp:2028:3:2028:3 | Address | &:r2028_10 |
|
||||
| ir.cpp:2028:7:2028:7 | Address | &:r2028_1 |
|
||||
| ir.cpp:2028:7:2028:7 | Left | r2028_2 |
|
||||
| ir.cpp:2028:7:2028:7 | Load | m2026_6 |
|
||||
| ir.cpp:2028:7:2028:13 | Condition | r2028_4 |
|
||||
| ir.cpp:2028:7:2030:28 | Address | &:r2028_7 |
|
||||
| ir.cpp:2028:7:2030:28 | Address | &:r2028_11 |
|
||||
| ir.cpp:2028:7:2030:28 | Address | &:r2028_13 |
|
||||
| ir.cpp:2028:7:2030:28 | Load | m2028_6 |
|
||||
| ir.cpp:2028:7:2030:28 | Phi | from 2:m2028_12 |
|
||||
| ir.cpp:2028:7:2030:28 | Phi | from 3:m2028_14 |
|
||||
| ir.cpp:2028:7:2030:28 | StoreValue | r2028_8 |
|
||||
| ir.cpp:2028:7:2030:28 | Address | &:r2028_8 |
|
||||
| ir.cpp:2028:7:2030:28 | Address | &:r2028_12 |
|
||||
| ir.cpp:2028:7:2030:28 | Address | &:r2028_14 |
|
||||
| ir.cpp:2028:7:2030:28 | Load | m2028_7 |
|
||||
| ir.cpp:2028:7:2030:28 | Phi | from 2:m2028_13 |
|
||||
| ir.cpp:2028:7:2030:28 | Phi | from 2:~m2029_6 |
|
||||
| ir.cpp:2028:7:2030:28 | Phi | from 3:m2028_15 |
|
||||
| ir.cpp:2028:7:2030:28 | Phi | from 3:~m2030_6 |
|
||||
| ir.cpp:2028:7:2030:28 | StoreValue | r2028_9 |
|
||||
| ir.cpp:2028:11:2028:13 | Right | r2028_3 |
|
||||
| ir.cpp:2029:6:2029:20 | CallTarget | func:r2029_1 |
|
||||
| ir.cpp:2029:6:2029:20 | ChiPartial | partial:m2029_5 |
|
||||
@@ -9626,6 +9642,7 @@
|
||||
| ir.cpp:2030:22:2030:22 | Arg(0) | 0:r2030_3 |
|
||||
| ir.cpp:2030:22:2030:22 | Load | m2026_6 |
|
||||
| ir.cpp:2030:26:2030:27 | Unary | r2030_7 |
|
||||
| ir.cpp:2031:1:2031:1 | Address | &:r2031_1 |
|
||||
| ir.cpp:2033:6:2033:17 | ChiPartial | partial:m2033_3 |
|
||||
| ir.cpp:2033:6:2033:17 | ChiTotal | total:m2033_2 |
|
||||
| ir.cpp:2033:6:2033:17 | SideEffect | ~m2036_6 |
|
||||
@@ -9721,8 +9738,11 @@
|
||||
| ir.cpp:2051:32:2051:32 | Address | &:r2051_7 |
|
||||
| ir.cpp:2051:32:2051:32 | Load | m2051_6 |
|
||||
| ir.cpp:2051:32:2051:32 | SideEffect | m2051_8 |
|
||||
| ir.cpp:2056:5:2056:18 | Address | &:r2056_5 |
|
||||
| ir.cpp:2056:5:2056:18 | ChiPartial | partial:m2056_3 |
|
||||
| ir.cpp:2056:5:2056:18 | ChiTotal | total:m2056_2 |
|
||||
| ir.cpp:2056:5:2056:18 | Load | m2066_2 |
|
||||
| ir.cpp:2056:5:2056:18 | SideEffect | ~m2065_6 |
|
||||
| ir.cpp:2058:12:2058:13 | Address | &:r2058_1 |
|
||||
| ir.cpp:2058:17:2058:27 | Address | &:r2058_4 |
|
||||
| ir.cpp:2058:17:2058:27 | Address | &:r2058_8 |
|
||||
@@ -9796,6 +9816,7 @@
|
||||
| ir.cpp:2065:12:2065:12 | Address | &:r2065_2 |
|
||||
| ir.cpp:2065:12:2065:12 | Arg(0) | 0:r2065_3 |
|
||||
| ir.cpp:2065:12:2065:12 | Load | m2064_15 |
|
||||
| ir.cpp:2066:1:2066:1 | Address | &:r2066_1 |
|
||||
| ir.cpp:2070:6:2070:26 | ChiPartial | partial:m2070_3 |
|
||||
| ir.cpp:2070:6:2070:26 | ChiTotal | total:m2070_2 |
|
||||
| ir.cpp:2070:6:2070:26 | SideEffect | ~m2072_5 |
|
||||
|
||||
@@ -12,13 +12,15 @@ unnecessaryPhiInstruction
|
||||
memoryOperandDefinitionIsUnmodeled
|
||||
operandAcrossFunctions
|
||||
instructionWithoutUniqueBlock
|
||||
missingCanonicalLanguageType
|
||||
multipleCanonicalLanguageTypes
|
||||
containsLoopOfForwardEdges
|
||||
missingIRType
|
||||
multipleIRTypes
|
||||
lostReachability
|
||||
backEdgeCountMismatch
|
||||
useNotDominatedByDefinition
|
||||
| ir.cpp:1486:8:1486:8 | Unary | Operand 'Unary' is not dominated by its definition in function '$@'. | ir.cpp:1486:8:1486:8 | void StructuredBindingDataMemberStruct::StructuredBindingDataMemberStruct() | void StructuredBindingDataMemberStruct::StructuredBindingDataMemberStruct() |
|
||||
| ir.cpp:1751:51:1751:51 | Address | Operand 'Address' is not dominated by its definition in function '$@'. | ir.cpp:1750:5:1750:34 | int implicit_copy_constructor_test(CopyConstructorTestNonVirtualClass const&, CopyConstructorTestVirtualClass const&) | int implicit_copy_constructor_test(CopyConstructorTestNonVirtualClass const&, CopyConstructorTestVirtualClass const&) |
|
||||
| ir.cpp:1752:48:1752:48 | Address | Operand 'Address' is not dominated by its definition in function '$@'. | ir.cpp:1750:5:1750:34 | int implicit_copy_constructor_test(CopyConstructorTestNonVirtualClass const&, CopyConstructorTestVirtualClass const&) | int implicit_copy_constructor_test(CopyConstructorTestNonVirtualClass const&, CopyConstructorTestVirtualClass const&) |
|
||||
| try_except.c:13:13:13:13 | Left | Operand 'Left' is not dominated by its definition in function '$@'. | try_except.c:6:6:6:6 | void f() | void f() |
|
||||
| try_except.c:13:13:13:13 | Left | Operand 'Left' is not dominated by its definition in function '$@'. | try_except.c:6:6:6:6 | void f() | void f() |
|
||||
| try_except.c:39:15:39:15 | Left | Operand 'Left' is not dominated by its definition in function '$@'. | try_except.c:32:6:32:6 | void h(int) | void h(int) |
|
||||
@@ -35,8 +37,4 @@ nonUniqueEnclosingIRFunction
|
||||
fieldAddressOnNonPointer
|
||||
thisArgumentIsNonPointer
|
||||
nonUniqueIRVariable
|
||||
missingCanonicalLanguageType
|
||||
multipleCanonicalLanguageTypes
|
||||
missingIRType
|
||||
multipleIRTypes
|
||||
missingCppType
|
||||
|
||||
@@ -7201,21 +7201,26 @@ ir.cpp:
|
||||
# 1290| r1290_1(glval<bool>) = VariableAddress[b] :
|
||||
# 1290| r1290_2(bool) = Load[b] : &:r1290_1, ~m?
|
||||
# 1290| v1290_3(void) = ConditionalBranch : r1290_2
|
||||
#-----| False -> Block 2
|
||||
#-----| True -> Block 1
|
||||
#-----| False -> Block 3
|
||||
#-----| True -> Block 2
|
||||
|
||||
# 1291| Block 1
|
||||
# 1291| r1291_1(glval<int>) = VariableAddress[#return] :
|
||||
# 1291| r1291_2(glval<int>) = VariableAddress[x] :
|
||||
# 1291| r1291_3(int) = Load[x] : &:r1291_2, ~m?
|
||||
# 1291| mu1291_4(int) = Store[#return] : &:r1291_1, r1291_3
|
||||
# 1289| Block 1
|
||||
# 1289| r1289_8(glval<int>) = VariableAddress[#return] :
|
||||
# 1289| v1289_9(void) = ReturnValue : &:r1289_8, ~m?
|
||||
# 1289| v1289_10(void) = AliasedUse : ~m?
|
||||
# 1289| v1289_11(void) = ExitFunction :
|
||||
|
||||
# 1293| Block 2
|
||||
# 1293| v1293_1(void) = Unreached :
|
||||
# 1291| Block 2
|
||||
# 1291| r1291_1(glval<int>) = VariableAddress[#return] :
|
||||
# 1291| r1291_2(glval<int>) = VariableAddress[x] :
|
||||
# 1291| r1291_3(int) = Load[x] : &:r1291_2, ~m?
|
||||
# 1291| mu1291_4(int) = Store[#return] : &:r1291_1, r1291_3
|
||||
#-----| Goto -> Block 1
|
||||
|
||||
# 1293| Block 3
|
||||
# 1293| r1293_1(glval<int>) = VariableAddress[#return] :
|
||||
# 1293| mu1293_2(int) = Uninitialized[#return] : &:r1293_1
|
||||
#-----| Goto -> Block 1
|
||||
|
||||
# 1295| void returnVoid(int, int)
|
||||
# 1295| Block 0
|
||||
@@ -9526,15 +9531,14 @@ ir.cpp:
|
||||
# 1754| mu1754_9(unknown) = ^CallSideEffect : ~m?
|
||||
# 1754| v1754_10(void) = ^BufferReadSideEffect[0] : &:r1754_7, ~m?
|
||||
# 1754| mu1754_11(CopyConstructorTestVirtualClass) = ^IndirectMayWriteSideEffect[-1] : &:r1754_1
|
||||
# 1755| v1755_1(void) = Unreached :
|
||||
|
||||
# 1751| Block 1
|
||||
# 1751| v1751_5(void) = ReturnIndirection[x] : &:r1751_3, ~m?
|
||||
# 1752| v1752_5(void) = ReturnIndirection[y] : &:r1752_3, ~m?
|
||||
# 1750| r1750_4(glval<int>) = VariableAddress[#return] :
|
||||
# 1750| v1750_5(void) = ReturnValue : &:r1750_4, ~m?
|
||||
# 1750| v1750_6(void) = AliasedUse : ~m?
|
||||
# 1750| v1750_7(void) = ExitFunction :
|
||||
# 1755| r1755_1(glval<int>) = VariableAddress[#return] :
|
||||
# 1755| mu1755_2(int) = Uninitialized[#return] : &:r1755_1
|
||||
# 1751| v1751_5(void) = ReturnIndirection[x] : &:r1751_3, ~m?
|
||||
# 1752| v1752_5(void) = ReturnIndirection[y] : &:r1752_3, ~m?
|
||||
# 1750| r1750_4(glval<int>) = VariableAddress[#return] :
|
||||
# 1750| v1750_5(void) = ReturnValue : &:r1750_4, ~m?
|
||||
# 1750| v1750_6(void) = AliasedUse : ~m?
|
||||
# 1750| v1750_7(void) = ExitFunction :
|
||||
|
||||
# 1757| void if_initialization(int)
|
||||
# 1757| Block 0
|
||||
@@ -10199,24 +10203,32 @@ ir.cpp:
|
||||
# 1900| r1900_3(int) = Constant[10] :
|
||||
# 1900| r1900_4(bool) = CompareLT : r1900_2, r1900_3
|
||||
# 1900| v1900_5(void) = ConditionalBranch : r1900_4
|
||||
#-----| False -> Block 2
|
||||
#-----| True -> Block 1
|
||||
#-----| False -> Block 3
|
||||
#-----| True -> Block 2
|
||||
|
||||
# 1901| Block 1
|
||||
# 1901| r1901_1(glval<int>) = VariableAddress[#return] :
|
||||
# 1901| r1901_2(glval<int>) = VariableAddress[x] :
|
||||
# 1901| r1901_3(int) = Load[x] : &:r1901_2, ~m?
|
||||
# 1901| mu1901_4(int) = Store[#return] : &:r1901_1, r1901_3
|
||||
# 1899| Block 1
|
||||
# 1899| r1899_6(glval<int>) = VariableAddress[#return] :
|
||||
# 1899| v1899_7(void) = ReturnValue : &:r1899_6, ~m?
|
||||
# 1899| v1899_8(void) = AliasedUse : ~m?
|
||||
# 1899| v1899_9(void) = ExitFunction :
|
||||
|
||||
# 1903| Block 2
|
||||
# 1901| Block 2
|
||||
# 1901| r1901_1(glval<int>) = VariableAddress[#return] :
|
||||
# 1901| r1901_2(glval<int>) = VariableAddress[x] :
|
||||
# 1901| r1901_3(int) = Load[x] : &:r1901_2, ~m?
|
||||
# 1901| mu1901_4(int) = Store[#return] : &:r1901_1, r1901_3
|
||||
#-----| Goto -> Block 1
|
||||
|
||||
# 1903| Block 3
|
||||
# 1903| r1903_1(glval<unknown>) = FunctionAddress[noreturnFunc] :
|
||||
# 1903| v1903_2(void) = Call[noreturnFunc] : func:r1903_1
|
||||
# 1903| mu1903_3(unknown) = ^CallSideEffect : ~m?
|
||||
# 1905| v1905_1(void) = Unreached :
|
||||
# 1899| v1899_10(void) = Unreached :
|
||||
|
||||
# 1905| Block 4
|
||||
# 1905| r1905_1(glval<int>) = VariableAddress[#return] :
|
||||
# 1905| mu1905_2(int) = Uninitialized[#return] : &:r1905_1
|
||||
#-----| Goto -> Block 1
|
||||
|
||||
# 1907| int noreturnTest2(int)
|
||||
# 1907| Block 0
|
||||
@@ -11069,23 +11081,22 @@ ir.cpp:
|
||||
# 2028| r2028_3(unsigned int) = Constant[100] :
|
||||
# 2028| r2028_4(bool) = CompareLT : r2028_2, r2028_3
|
||||
# 2028| v2028_5(void) = ConditionalBranch : r2028_4
|
||||
#-----| False -> Block 4
|
||||
#-----| True -> Block 3
|
||||
#-----| False -> Block 3
|
||||
#-----| True -> Block 2
|
||||
|
||||
# 2026| Block 1
|
||||
# 2026| r2026_6(glval<unsigned int>) = VariableAddress[#return] :
|
||||
# 2026| v2026_7(void) = ReturnValue : &:r2026_6, ~m?
|
||||
# 2026| v2026_8(void) = AliasedUse : ~m?
|
||||
# 2026| v2026_9(void) = ExitFunction :
|
||||
|
||||
# 2028| Block 2
|
||||
# 2028| Block 1
|
||||
# 2028| r2028_6(glval<unsigned int>) = VariableAddress[#temp2028:7] :
|
||||
# 2028| r2028_7(unsigned int) = Load[#temp2028:7] : &:r2028_6, ~m?
|
||||
# 2028| r2028_8(glval<unsigned int>) = VariableAddress[y] :
|
||||
# 2028| mu2028_9(unsigned int) = Store[y] : &:r2028_8, r2028_7
|
||||
# 2031| v2031_1(void) = Unreached :
|
||||
# 2031| r2031_1(glval<unsigned int>) = VariableAddress[#return] :
|
||||
# 2031| mu2031_2(unsigned int) = Uninitialized[#return] : &:r2031_1
|
||||
# 2026| r2026_6(glval<unsigned int>) = VariableAddress[#return] :
|
||||
# 2026| v2026_7(void) = ReturnValue : &:r2026_6, ~m?
|
||||
# 2026| v2026_8(void) = AliasedUse : ~m?
|
||||
# 2026| v2026_9(void) = ExitFunction :
|
||||
|
||||
# 2029| Block 3
|
||||
# 2029| Block 2
|
||||
# 2029| r2029_1(glval<unknown>) = FunctionAddress[CommaTestHelper] :
|
||||
# 2029| r2029_2(glval<unsigned int>) = VariableAddress[x] :
|
||||
# 2029| r2029_3(unsigned int) = Load[x] : &:r2029_2, ~m?
|
||||
@@ -11096,9 +11107,9 @@ ir.cpp:
|
||||
# 2029| r2029_8(unsigned int) = CopyValue : r2029_7
|
||||
# 2028| r2028_10(glval<unsigned int>) = VariableAddress[#temp2028:7] :
|
||||
# 2028| mu2028_11(unsigned int) = Store[#temp2028:7] : &:r2028_10, r2029_8
|
||||
#-----| Goto -> Block 2
|
||||
#-----| Goto -> Block 1
|
||||
|
||||
# 2030| Block 4
|
||||
# 2030| Block 3
|
||||
# 2030| r2030_1(glval<unknown>) = FunctionAddress[CommaTestHelper] :
|
||||
# 2030| r2030_2(glval<unsigned int>) = VariableAddress[x] :
|
||||
# 2030| r2030_3(unsigned int) = Load[x] : &:r2030_2, ~m?
|
||||
@@ -11109,7 +11120,7 @@ ir.cpp:
|
||||
# 2030| r2030_8(unsigned int) = Convert : r2030_7
|
||||
# 2028| r2028_12(glval<unsigned int>) = VariableAddress[#temp2028:7] :
|
||||
# 2028| mu2028_13(unsigned int) = Store[#temp2028:7] : &:r2028_12, r2030_8
|
||||
#-----| Goto -> Block 2
|
||||
#-----| Goto -> Block 1
|
||||
|
||||
# 2033| void NewDeleteMem()
|
||||
# 2033| Block 0
|
||||
@@ -11295,13 +11306,12 @@ ir.cpp:
|
||||
# 2065| r2065_3(Derived2 *) = Load[d] : &:r2065_2, ~m?
|
||||
# 2065| v2065_4(void) = Call[?] : func:r2065_1, 0:r2065_3
|
||||
# 2065| mu2065_5(unknown) = ^CallSideEffect : ~m?
|
||||
# 2066| v2066_1(void) = Unreached :
|
||||
|
||||
# 2056| Block 1
|
||||
# 2056| r2056_4(glval<int>) = VariableAddress[#return] :
|
||||
# 2056| v2056_5(void) = ReturnValue : &:r2056_4, ~m?
|
||||
# 2056| v2056_6(void) = AliasedUse : ~m?
|
||||
# 2056| v2056_7(void) = ExitFunction :
|
||||
# 2066| r2066_1(glval<int>) = VariableAddress[#return] :
|
||||
# 2066| mu2066_2(int) = Uninitialized[#return] : &:r2066_1
|
||||
# 2056| r2056_4(glval<int>) = VariableAddress[#return] :
|
||||
# 2056| v2056_5(void) = ReturnValue : &:r2056_4, ~m?
|
||||
# 2056| v2056_6(void) = AliasedUse : ~m?
|
||||
# 2056| v2056_7(void) = ExitFunction :
|
||||
|
||||
# 2070| void test_constant_folding()
|
||||
# 2070| Block 0
|
||||
|
||||
@@ -34,13 +34,15 @@ unnecessaryPhiInstruction
|
||||
memoryOperandDefinitionIsUnmodeled
|
||||
operandAcrossFunctions
|
||||
instructionWithoutUniqueBlock
|
||||
missingCanonicalLanguageType
|
||||
multipleCanonicalLanguageTypes
|
||||
containsLoopOfForwardEdges
|
||||
missingIRType
|
||||
multipleIRTypes
|
||||
lostReachability
|
||||
backEdgeCountMismatch
|
||||
useNotDominatedByDefinition
|
||||
| VacuousDestructorCall.cpp:2:29:2:29 | Address | Operand 'Address' is not dominated by its definition in function '$@'. | VacuousDestructorCall.cpp:2:6:2:6 | void CallDestructor<int>(int, int*) | void CallDestructor<int>(int, int*) |
|
||||
| misc.c:219:47:219:48 | Address | Operand 'Address' is not dominated by its definition in function '$@'. | misc.c:219:5:219:26 | int assign_designated_init(someStruct*) | int assign_designated_init(someStruct*) |
|
||||
| ms_assume.cpp:11:30:11:33 | Address | Operand 'Address' is not dominated by its definition in function '$@'. | ms_assume.cpp:11:12:11:12 | int f(int, char*[]) | int f(int, char*[]) |
|
||||
| ms_try_except.cpp:9:19:9:19 | Left | Operand 'Left' is not dominated by its definition in function '$@'. | ms_try_except.cpp:2:6:2:18 | void ms_try_except(int) | void ms_try_except(int) |
|
||||
| ms_try_except.cpp:9:19:9:19 | Left | Operand 'Left' is not dominated by its definition in function '$@'. | ms_try_except.cpp:2:6:2:18 | void ms_try_except(int) | void ms_try_except(int) |
|
||||
| ms_try_except.cpp:19:17:19:21 | Left | Operand 'Left' is not dominated by its definition in function '$@'. | ms_try_except.cpp:2:6:2:18 | void ms_try_except(int) | void ms_try_except(int) |
|
||||
@@ -58,8 +60,4 @@ thisArgumentIsNonPointer
|
||||
| pointer_to_member.cpp:23:5:23:54 | Call: call to expression | Call instruction 'Call: call to expression' has a `this` argument operand that is not an address, in function '$@'. | pointer_to_member.cpp:14:5:14:9 | int usePM(int PM::*) | int usePM(int PM::*) |
|
||||
| pointer_to_member.cpp:24:5:24:49 | Call: call to expression | Call instruction 'Call: call to expression' has a `this` argument operand that is not an address, in function '$@'. | pointer_to_member.cpp:14:5:14:9 | int usePM(int PM::*) | int usePM(int PM::*) |
|
||||
nonUniqueIRVariable
|
||||
missingCanonicalLanguageType
|
||||
multipleCanonicalLanguageTypes
|
||||
missingIRType
|
||||
multipleIRTypes
|
||||
missingCppType
|
||||
|
||||
2
csharp/.vscode/extensions.json
vendored
2
csharp/.vscode/extensions.json
vendored
@@ -1,9 +1,7 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"formulahendry.dotnet-test-explorer",
|
||||
"gaoshan0621.csharp-format-usings",
|
||||
"github.vscode-codeql",
|
||||
"hbenl.vscode-test-explorer",
|
||||
"ms-dotnettools.csharp",
|
||||
"ms-dotnettools.csdevkit"
|
||||
],
|
||||
|
||||
@@ -557,7 +557,7 @@ namespace Semmle.Autobuild.CSharp.Tests
|
||||
[Fact]
|
||||
public void TestLinuxBuildlessExtractionSuccess()
|
||||
{
|
||||
actions.RunProcess[@"C:\codeql\csharp/tools/linux64/Semmle.Extraction.CSharp.Standalone --references:."] = 0;
|
||||
actions.RunProcess[@"C:\codeql\csharp/tools/linux64/Semmle.Extraction.CSharp.Standalone"] = 0;
|
||||
actions.FileExists["csharp.log"] = true;
|
||||
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = "";
|
||||
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = "";
|
||||
@@ -571,7 +571,7 @@ namespace Semmle.Autobuild.CSharp.Tests
|
||||
[Fact]
|
||||
public void TestLinuxBuildlessExtractionFailed()
|
||||
{
|
||||
actions.RunProcess[@"C:\codeql\csharp/tools/linux64/Semmle.Extraction.CSharp.Standalone --references:."] = 10;
|
||||
actions.RunProcess[@"C:\codeql\csharp/tools/linux64/Semmle.Extraction.CSharp.Standalone"] = 10;
|
||||
actions.FileExists["csharp.log"] = true;
|
||||
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = "";
|
||||
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = "";
|
||||
@@ -585,7 +585,7 @@ namespace Semmle.Autobuild.CSharp.Tests
|
||||
[Fact]
|
||||
public void TestLinuxBuildlessExtractionSolution()
|
||||
{
|
||||
actions.RunProcess[@"C:\codeql\csharp/tools/linux64/Semmle.Extraction.CSharp.Standalone foo.sln --references:."] = 0;
|
||||
actions.RunProcess[@"C:\codeql\csharp/tools/linux64/Semmle.Extraction.CSharp.Standalone foo.sln"] = 0;
|
||||
actions.FileExists["csharp.log"] = true;
|
||||
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = "";
|
||||
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = "";
|
||||
@@ -873,7 +873,7 @@ namespace Semmle.Autobuild.CSharp.Tests
|
||||
[Fact]
|
||||
public void TestSkipNugetBuildless()
|
||||
{
|
||||
actions.RunProcess[@"C:\codeql\csharp/tools/linux64/Semmle.Extraction.CSharp.Standalone foo.sln --references:. --skip-nuget"] = 0;
|
||||
actions.RunProcess[@"C:\codeql\csharp/tools/linux64/Semmle.Extraction.CSharp.Standalone foo.sln --skip-nuget"] = 0;
|
||||
actions.FileExists["csharp.log"] = true;
|
||||
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_TRAP_DIR"] = "";
|
||||
actions.GetEnvironmentVariable["CODEQL_EXTRACTOR_CSHARP_SOURCE_ARCHIVE_DIR"] = "";
|
||||
|
||||
@@ -8,12 +8,12 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.IO.FileSystem" Version="4.3.0" />
|
||||
<PackageReference Include="System.IO.FileSystem.Primitives" Version="4.3.0" />
|
||||
<PackageReference Include="xunit" Version="2.5.0" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
|
||||
<PackageReference Include="xunit" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Semmle.Autobuild.CSharp\Semmle.Autobuild.CSharp.csproj" />
|
||||
|
||||
@@ -48,8 +48,11 @@ namespace Semmle.Autobuild.CSharp
|
||||
attempt = new BuildCommandRule(DotNetRule.WithDotNet).Analyse(this, false) & CheckExtractorRun(true);
|
||||
break;
|
||||
case CSharpBuildStrategy.Buildless:
|
||||
// No need to check that the extractor has been executed in buildless mode
|
||||
attempt = new StandaloneBuildRule().Analyse(this, false);
|
||||
attempt = DotNetRule.WithDotNet(this, (dotNetPath, env) =>
|
||||
{
|
||||
// No need to check that the extractor has been executed in buildless mode
|
||||
return new StandaloneBuildRule(dotNetPath).Analyse(this, false);
|
||||
});
|
||||
break;
|
||||
case CSharpBuildStrategy.MSBuild:
|
||||
attempt = new MsBuildRule().Analyse(this, false) & CheckExtractorRun(true);
|
||||
|
||||
@@ -70,7 +70,16 @@ namespace Semmle.Autobuild.CSharp
|
||||
});
|
||||
}
|
||||
|
||||
private static BuildScript WithDotNet(IAutobuilder<AutobuildOptionsShared> builder, Func<string?, IDictionary<string, string>?, BuildScript> f)
|
||||
/// <summary>
|
||||
/// Returns a script that attempts to download relevant version(s) of the
|
||||
/// .NET Core SDK, followed by running the script generated by <paramref name="f"/>.
|
||||
///
|
||||
/// The arguments to <paramref name="f"/> are the path to the directory in which the
|
||||
/// .NET Core SDK(s) were installed and any additional required environment
|
||||
/// variables needed by the installed .NET Core (<code>null</code> when no variables
|
||||
/// are needed).
|
||||
/// </summary>
|
||||
public static BuildScript WithDotNet(IAutobuilder<AutobuildOptionsShared> builder, Func<string?, IDictionary<string, string>?, BuildScript> f)
|
||||
{
|
||||
var installDir = builder.Actions.PathCombine(builder.Options.RootDirectory, ".dotnet");
|
||||
var installScript = DownloadDotNet(builder, installDir);
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<AssemblyName>Semmle.Autobuild.CSharp</AssemblyName>
|
||||
<RootNamespace>Semmle.Autobuild.CSharp</RootNamespace>
|
||||
<ApplicationIcon />
|
||||
<ApplicationIcon/>
|
||||
<OutputType>Exe</OutputType>
|
||||
<StartupObject />
|
||||
<StartupObject/>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<RuntimeIdentifiers>win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
|
||||
<Nullable>enable</Nullable>
|
||||
@@ -14,8 +14,8 @@
|
||||
<Folder Include="Properties\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Build" Version="17.7.2" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="Microsoft.Build" Version="17.3.2" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\extractor\Semmle.Util\Semmle.Util.csproj" />
|
||||
|
||||
@@ -8,6 +8,13 @@ namespace Semmle.Autobuild.CSharp
|
||||
/// </summary>
|
||||
internal class StandaloneBuildRule : IBuildRule<CSharpAutobuildOptions>
|
||||
{
|
||||
private readonly string? dotNetPath;
|
||||
|
||||
internal StandaloneBuildRule(string? dotNetPath)
|
||||
{
|
||||
this.dotNetPath = dotNetPath;
|
||||
}
|
||||
|
||||
public BuildScript Analyse(IAutobuilder<CSharpAutobuildOptions> builder, bool auto)
|
||||
{
|
||||
BuildScript GetCommand(string? solution)
|
||||
@@ -28,13 +35,17 @@ namespace Semmle.Autobuild.CSharp
|
||||
if (solution is not null)
|
||||
cmd.QuoteArgument(solution);
|
||||
|
||||
cmd.Argument("--references:.");
|
||||
|
||||
if (!builder.Options.NugetRestore)
|
||||
{
|
||||
cmd.Argument("--skip-nuget");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(this.dotNetPath))
|
||||
{
|
||||
cmd.Argument("--dotnet");
|
||||
cmd.QuoteArgument(this.dotNetPath);
|
||||
}
|
||||
|
||||
return cmd.Script;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<Folder Include="Properties\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Build" Version="17.7.2" />
|
||||
<PackageReference Include="Microsoft.Build" Version="17.3.2" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\extractor\Semmle.Util\Semmle.Util.csproj" />
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.DiaSymReader" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.DiaSymReader" Version="1.4.0" />
|
||||
<PackageReference Include="Microsoft.DiaSymReader.Native" Version="1.7.0" />
|
||||
<PackageReference Include="Microsoft.DiaSymReader.PortablePdb" Version="1.6.0"><IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
|
||||
@@ -15,14 +15,24 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
/// <summary>
|
||||
/// Locate all reference files and index them.
|
||||
/// </summary>
|
||||
/// <param name="dirs">Directories to search.</param>
|
||||
/// <param name="paths">
|
||||
/// Paths to search. Directories are searched recursively. Files are added directly to the
|
||||
/// assembly cache.
|
||||
/// </param>
|
||||
/// <param name="progressMonitor">Callback for progress.</param>
|
||||
public AssemblyCache(IEnumerable<string> dirs, ProgressMonitor progressMonitor)
|
||||
public AssemblyCache(IEnumerable<string> paths, ProgressMonitor progressMonitor)
|
||||
{
|
||||
foreach (var dir in dirs)
|
||||
foreach (var path in paths)
|
||||
{
|
||||
progressMonitor.FindingFiles(dir);
|
||||
AddReferenceDirectory(dir);
|
||||
if (File.Exists(path))
|
||||
{
|
||||
pendingDllsToIndex.Enqueue(path);
|
||||
}
|
||||
else
|
||||
{
|
||||
progressMonitor.FindingFiles(path);
|
||||
AddReferenceDirectory(path);
|
||||
}
|
||||
}
|
||||
IndexReferences();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
@@ -31,6 +31,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
private readonly FileContent fileContent;
|
||||
private readonly TemporaryDirectory packageDirectory;
|
||||
private TemporaryDirectory? razorWorkingDirectory;
|
||||
private readonly Git git;
|
||||
|
||||
|
||||
/// <summary>
|
||||
@@ -48,7 +49,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
|
||||
try
|
||||
{
|
||||
this.dotnet = new DotNet(progressMonitor);
|
||||
this.dotnet = new DotNet(options, progressMonitor);
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -68,7 +69,11 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
? new[] { options.SolutionFile }
|
||||
: allFiles.SelectFileNamesByExtension(".sln");
|
||||
|
||||
var dllDirNames = options.DllDirs.Select(Path.GetFullPath).ToList();
|
||||
// If DLL reference paths are specified on the command-line, use those to discover
|
||||
// assemblies. Otherwise (the default), query the git CLI to determine which DLL files
|
||||
// are tracked as part of the repository.
|
||||
this.git = new Git(this.progressMonitor);
|
||||
var dllDirNames = options.DllDirs.Count == 0 ? this.git.ListFiles("*.dll") : options.DllDirs.Select(Path.GetFullPath).ToList();
|
||||
|
||||
// Find DLLs in the .Net / Asp.Net Framework
|
||||
if (options.ScanNetFrameworkDlls)
|
||||
@@ -98,13 +103,9 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
progressMonitor.MissingNuGet();
|
||||
}
|
||||
|
||||
// TODO: remove the below when the required SDK is installed
|
||||
using (new FileRenamer(sourceDir.GetFiles("global.json", SearchOption.AllDirectories)))
|
||||
{
|
||||
Restore(solutions);
|
||||
Restore(allProjects);
|
||||
DownloadMissingPackages(allFiles);
|
||||
}
|
||||
Restore(solutions);
|
||||
Restore(allProjects);
|
||||
DownloadMissingPackages(allFiles);
|
||||
}
|
||||
|
||||
assemblyCache = new AssemblyCache(dllDirNames, progressMonitor);
|
||||
@@ -157,7 +158,6 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
{
|
||||
progressMonitor.LogInfo($"Found {views.Length} cshtml and razor files.");
|
||||
|
||||
// TODO: use SDK specified in global.json
|
||||
var sdk = new Sdk(dotnet).GetNewestSdk();
|
||||
if (sdk != null)
|
||||
{
|
||||
|
||||
@@ -51,6 +51,11 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
/// The number of threads to use.
|
||||
/// </summary>
|
||||
int Threads { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The path to the local ".dotnet" directory, if any.
|
||||
/// </summary>
|
||||
string? DotNetPath { get; }
|
||||
}
|
||||
|
||||
public class DependencyOptions : IDependencyOptions
|
||||
@@ -73,5 +78,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
Excludes.Any(path.Contains);
|
||||
|
||||
public int Threads { get; set; } = EnvironmentVariables.GetDefaultNumberOfThreads();
|
||||
|
||||
public string? DotNetPath { get; set; } = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using Semmle.Util;
|
||||
|
||||
namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
@@ -10,12 +11,13 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
/// </summary>
|
||||
internal class DotNet : IDotNet
|
||||
{
|
||||
private const string dotnet = "dotnet";
|
||||
private readonly ProgressMonitor progressMonitor;
|
||||
private readonly string dotnet;
|
||||
|
||||
public DotNet(ProgressMonitor progressMonitor)
|
||||
public DotNet(IDependencyOptions options, ProgressMonitor progressMonitor)
|
||||
{
|
||||
this.progressMonitor = progressMonitor;
|
||||
this.dotnet = Path.Combine(options.DotNetPath ?? string.Empty, "dotnet");
|
||||
Info();
|
||||
}
|
||||
|
||||
@@ -29,7 +31,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
}
|
||||
}
|
||||
|
||||
private static ProcessStartInfo MakeDotnetStartInfo(string args, bool redirectStandardOutput) =>
|
||||
private ProcessStartInfo MakeDotnetStartInfo(string args, bool redirectStandardOutput) =>
|
||||
new ProcessStartInfo(dotnet, args)
|
||||
{
|
||||
UseShellExecute = false,
|
||||
@@ -39,7 +41,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
private bool RunCommand(string args)
|
||||
{
|
||||
progressMonitor.RunningProcess($"{dotnet} {args}");
|
||||
using var proc = Process.Start(MakeDotnetStartInfo(args, redirectStandardOutput: false));
|
||||
using var proc = Process.Start(this.MakeDotnetStartInfo(args, redirectStandardOutput: false));
|
||||
proc?.WaitForExit();
|
||||
var exitCode = proc?.ExitCode ?? -1;
|
||||
if (exitCode != 0)
|
||||
@@ -77,7 +79,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
private IList<string> GetListed(string args, string artifact)
|
||||
{
|
||||
progressMonitor.RunningProcess($"{dotnet} {args}");
|
||||
var pi = MakeDotnetStartInfo(args, redirectStandardOutput: true);
|
||||
var pi = this.MakeDotnetStartInfo(args, redirectStandardOutput: true);
|
||||
var exitCode = pi.ReadOutput(out var artifacts);
|
||||
if (exitCode != 0)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
{
|
||||
/// <summary>
|
||||
/// Utilities for querying information from the git CLI.
|
||||
/// </summary>
|
||||
internal class Git
|
||||
{
|
||||
private readonly ProgressMonitor progressMonitor;
|
||||
private const string git = "git";
|
||||
|
||||
public Git(ProgressMonitor progressMonitor)
|
||||
{
|
||||
this.progressMonitor = progressMonitor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lists all files matching <paramref name="pattern"/> which are tracked in the
|
||||
/// current git repository.
|
||||
/// </summary>
|
||||
/// <param name="pattern">The file pattern.</param>
|
||||
/// <returns>A list of all tracked files which match <paramref name="pattern"/>.</returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public List<string> ListFiles(string pattern)
|
||||
{
|
||||
var results = new List<string>();
|
||||
var args = string.Join(' ', "ls-files", $"\"{pattern}\"");
|
||||
|
||||
progressMonitor.RunningProcess($"{git} {args}");
|
||||
var pi = new ProcessStartInfo(git, args)
|
||||
{
|
||||
UseShellExecute = false,
|
||||
RedirectStandardOutput = true
|
||||
};
|
||||
|
||||
using var p = new Process() { StartInfo = pi };
|
||||
p.OutputDataReceived += new DataReceivedEventHandler((sender, e) =>
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(e.Data))
|
||||
{
|
||||
results.Add(e.Data);
|
||||
}
|
||||
});
|
||||
p.Start();
|
||||
p.BeginOutputReadLine();
|
||||
p.WaitForExit();
|
||||
|
||||
if (p.ExitCode != 0)
|
||||
{
|
||||
progressMonitor.CommandFailed(git, args, p.ExitCode);
|
||||
throw new Exception($"{git} {args} failed");
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -50,6 +50,9 @@ namespace Semmle.Extraction.CSharp.Standalone
|
||||
case "references":
|
||||
dependencies.DllDirs.Add(value);
|
||||
return true;
|
||||
case "dotnet":
|
||||
dependencies.DotNetPath = value;
|
||||
return true;
|
||||
default:
|
||||
return base.HandleOption(key, value);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Semmle.Extraction.CSharp.Standalone</RootNamespace>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
<WarningsAsErrors />
|
||||
<WarningsAsErrors/>
|
||||
<RuntimeIdentifiers>win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
@@ -19,7 +19,7 @@
|
||||
<Folder Include="Properties\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Build" Version="17.7.2" />
|
||||
<PackageReference Include="Microsoft.Build" Version="17.3.2" />
|
||||
<PackageReference Include="Microsoft.Win32.Primitives" Version="4.3.0" />
|
||||
<PackageReference Include="System.Net.Primitives" Version="4.3.1" />
|
||||
<PackageReference Include="System.Security.Principal" Version="4.3.0" />
|
||||
|
||||
@@ -250,41 +250,44 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
/// </summary>
|
||||
public void PopulateGenerics()
|
||||
{
|
||||
if (Symbol is null || !NeedsPopulation || !Context.ExtractGenerics(this))
|
||||
return;
|
||||
|
||||
var members = new List<ISymbol>();
|
||||
|
||||
foreach (var member in Symbol.GetMembers())
|
||||
members.Add(member);
|
||||
foreach (var member in Symbol.GetTypeMembers())
|
||||
members.Add(member);
|
||||
|
||||
// Mono extractor puts all BASE interface members as members of the current interface.
|
||||
|
||||
if (Symbol.TypeKind == TypeKind.Interface)
|
||||
Context.PopulateLater(() =>
|
||||
{
|
||||
foreach (var baseInterface in Symbol.Interfaces)
|
||||
if (Symbol is null || !NeedsPopulation || !Context.ExtractGenerics(this))
|
||||
return;
|
||||
|
||||
var members = new List<ISymbol>();
|
||||
|
||||
foreach (var member in Symbol.GetMembers())
|
||||
members.Add(member);
|
||||
foreach (var member in Symbol.GetTypeMembers())
|
||||
members.Add(member);
|
||||
|
||||
// Mono extractor puts all BASE interface members as members of the current interface.
|
||||
|
||||
if (Symbol.TypeKind == TypeKind.Interface)
|
||||
{
|
||||
foreach (var member in baseInterface.GetMembers())
|
||||
members.Add(member);
|
||||
foreach (var member in baseInterface.GetTypeMembers())
|
||||
members.Add(member);
|
||||
foreach (var baseInterface in Symbol.Interfaces)
|
||||
{
|
||||
foreach (var member in baseInterface.GetMembers())
|
||||
members.Add(member);
|
||||
foreach (var member in baseInterface.GetTypeMembers())
|
||||
members.Add(member);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var member in members)
|
||||
{
|
||||
Context.CreateEntity(member);
|
||||
}
|
||||
foreach (var member in members)
|
||||
{
|
||||
Context.CreateEntity(member);
|
||||
}
|
||||
|
||||
if (Symbol.BaseType is not null)
|
||||
Create(Context, Symbol.BaseType).PopulateGenerics();
|
||||
if (Symbol.BaseType is not null)
|
||||
Create(Context, Symbol.BaseType).PopulateGenerics();
|
||||
|
||||
foreach (var i in Symbol.Interfaces)
|
||||
{
|
||||
Create(Context, i).PopulateGenerics();
|
||||
}
|
||||
foreach (var i in Symbol.Interfaces)
|
||||
{
|
||||
Create(Context, i).PopulateGenerics();
|
||||
}
|
||||
}, preserveDuplicationKey: false);
|
||||
}
|
||||
|
||||
public void ExtractRecursive(TextWriter trapFile, IEntity parent)
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<Folder Include="Properties\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.7.0" />
|
||||
<PackageReference Include="Microsoft.Build" Version="17.7.2" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.4.0" />
|
||||
<PackageReference Include="Microsoft.Build" Version="17.3.2" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -8,12 +8,12 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.IO.FileSystem" Version="4.3.0" />
|
||||
<PackageReference Include="System.IO.FileSystem.Primitives" Version="4.3.0" />
|
||||
<PackageReference Include="xunit" Version="2.5.0" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
|
||||
<PackageReference Include="xunit" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Semmle.Extraction.CSharp.Standalone\Semmle.Extraction.CSharp.Standalone.csproj" />
|
||||
|
||||
@@ -153,9 +153,9 @@ namespace Semmle.Extraction
|
||||
/// Enqueue the given action to be performed later.
|
||||
/// </summary>
|
||||
/// <param name="toRun">The action to run.</param>
|
||||
public void PopulateLater(Action a)
|
||||
public void PopulateLater(Action a, bool preserveDuplicationKey = true)
|
||||
{
|
||||
var key = GetCurrentTagStackKey();
|
||||
var key = preserveDuplicationKey ? GetCurrentTagStackKey() : null;
|
||||
if (key is not null)
|
||||
{
|
||||
// If we are currently executing with a duplication guard, then the same
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
<DefineConstants>TRACE;DEBUG;DEBUG_LABELS</DefineConstants>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Build" Version="17.7.2" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis" Version="4.7.0" />
|
||||
<PackageReference Include="GitInfo" Version="3.3.1">
|
||||
<PackageReference Include="Microsoft.Build" Version="17.3.2" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis" Version="4.4.0" />
|
||||
<PackageReference Include="GitInfo" Version="2.2.0">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="xunit" Version="2.5.0" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
|
||||
<PackageReference Include="xunit" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Semmle.Util\Semmle.Util.csproj" />
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -8,10 +8,10 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.3.0" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -10,10 +10,10 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.1" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="3.1.1" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="3.1.1" />
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
||||
<PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />
|
||||
<PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.2" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -40,8 +40,8 @@
|
||||
| CSharp7.cs:87:18:87:34 | (..., ...) | CSharp7.cs:87:18:87:34 | (String,String) | 1 | CSharp7.cs:87:30:87:33 | "X2" |
|
||||
| CSharp7.cs:88:9:88:24 | (..., ...) | CSharp7.cs:87:18:87:34 | (String,String) | 0 | CSharp7.cs:88:14:88:15 | String t2 |
|
||||
| CSharp7.cs:88:9:88:24 | (..., ...) | CSharp7.cs:87:18:87:34 | (String,String) | 1 | CSharp7.cs:88:22:88:23 | String t3 |
|
||||
| CSharp7.cs:95:18:95:38 | (..., ...) | file://:0:0:0:0 | (Int32,String) | 0 | CSharp7.cs:95:19:95:19 | 1 |
|
||||
| CSharp7.cs:95:18:95:38 | (..., ...) | file://:0:0:0:0 | (Int32,String) | 1 | CSharp7.cs:95:22:95:37 | "TupleExprNode1" |
|
||||
| CSharp7.cs:95:18:95:38 | (..., ...) | CSharp7.cs:95:18:95:38 | (Int32,String) | 0 | CSharp7.cs:95:19:95:19 | 1 |
|
||||
| CSharp7.cs:95:18:95:38 | (..., ...) | CSharp7.cs:95:18:95:38 | (Int32,String) | 1 | CSharp7.cs:95:22:95:37 | "TupleExprNode1" |
|
||||
| CSharp7.cs:96:18:96:43 | (..., ...) | CSharp7.cs:96:18:96:43 | (Int32,(String,Int32)) | 0 | CSharp7.cs:96:19:96:19 | 1 |
|
||||
| CSharp7.cs:96:18:96:43 | (..., ...) | CSharp7.cs:96:18:96:43 | (Int32,(String,Int32)) | 1 | CSharp7.cs:96:22:96:42 | (..., ...) |
|
||||
| CSharp7.cs:96:22:96:42 | (..., ...) | file://:0:0:0:0 | (String,Int32) | 0 | CSharp7.cs:96:23:96:38 | "TupleExprNode2" |
|
||||
@@ -82,11 +82,11 @@
|
||||
| CSharp7.cs:223:9:223:18 | (..., ...) | CSharp7.cs:213:5:213:17 | (Int32,Double) | 1 | CSharp7.cs:223:17:223:17 | _ |
|
||||
| CSharp7.cs:224:9:224:18 | (..., ...) | CSharp7.cs:213:5:213:17 | (Int32,Double) | 0 | CSharp7.cs:224:10:224:10 | _ |
|
||||
| CSharp7.cs:224:9:224:18 | (..., ...) | CSharp7.cs:213:5:213:17 | (Int32,Double) | 1 | CSharp7.cs:224:17:224:17 | Double y |
|
||||
| CSharp7.cs:283:40:283:61 | (..., ...) | file://:0:0:0:0 | (Int32,String) | 0 | CSharp7.cs:283:41:283:48 | access to property Key |
|
||||
| CSharp7.cs:283:40:283:61 | (..., ...) | file://:0:0:0:0 | (Int32,String) | 1 | CSharp7.cs:283:51:283:60 | access to property Value |
|
||||
| CSharp7.cs:285:18:285:34 | (..., ...) | file://:0:0:0:0 | (Int32,String) | 0 | CSharp7.cs:285:23:285:23 | Int32 a |
|
||||
| CSharp7.cs:285:18:285:34 | (..., ...) | file://:0:0:0:0 | (Int32,String) | 1 | CSharp7.cs:285:33:285:33 | String b |
|
||||
| CSharp7.cs:287:18:287:31 | (..., ...) | file://:0:0:0:0 | (Int32,String) | 0 | CSharp7.cs:287:23:287:23 | Int32 a |
|
||||
| CSharp7.cs:287:18:287:31 | (..., ...) | file://:0:0:0:0 | (Int32,String) | 1 | CSharp7.cs:287:30:287:30 | String b |
|
||||
| CSharp7.cs:289:18:289:27 | (..., ...) | file://:0:0:0:0 | (Int32,String) | 0 | CSharp7.cs:289:23:289:23 | Int32 a |
|
||||
| CSharp7.cs:289:18:289:27 | (..., ...) | file://:0:0:0:0 | (Int32,String) | 1 | CSharp7.cs:289:26:289:26 | String b |
|
||||
| CSharp7.cs:283:40:283:61 | (..., ...) | CSharp7.cs:95:18:95:38 | (Int32,String) | 0 | CSharp7.cs:283:41:283:48 | access to property Key |
|
||||
| CSharp7.cs:283:40:283:61 | (..., ...) | CSharp7.cs:95:18:95:38 | (Int32,String) | 1 | CSharp7.cs:283:51:283:60 | access to property Value |
|
||||
| CSharp7.cs:285:18:285:34 | (..., ...) | CSharp7.cs:95:18:95:38 | (Int32,String) | 0 | CSharp7.cs:285:23:285:23 | Int32 a |
|
||||
| CSharp7.cs:285:18:285:34 | (..., ...) | CSharp7.cs:95:18:95:38 | (Int32,String) | 1 | CSharp7.cs:285:33:285:33 | String b |
|
||||
| CSharp7.cs:287:18:287:31 | (..., ...) | CSharp7.cs:95:18:95:38 | (Int32,String) | 0 | CSharp7.cs:287:23:287:23 | Int32 a |
|
||||
| CSharp7.cs:287:18:287:31 | (..., ...) | CSharp7.cs:95:18:95:38 | (Int32,String) | 1 | CSharp7.cs:287:30:287:30 | String b |
|
||||
| CSharp7.cs:289:18:289:27 | (..., ...) | CSharp7.cs:95:18:95:38 | (Int32,String) | 0 | CSharp7.cs:289:23:289:23 | Int32 a |
|
||||
| CSharp7.cs:289:18:289:27 | (..., ...) | CSharp7.cs:95:18:95:38 | (Int32,String) | 1 | CSharp7.cs:289:26:289:26 | String b |
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
| (Int32,(String,Int32)) | (int, (string, int)) | ValueTuple<int, (string, int)> | 2 | 0 | CSharp7.cs:96:19:96:19 | Item1 |
|
||||
| (Int32,(String,Int32)) | (int, (string, int)) | ValueTuple<int, (string, int)> | 2 | 1 | CSharp7.cs:96:22:96:42 | Item2 |
|
||||
| (Int32,(String,Int32)) | (int, (string, int)) | ValueTuple<int, (string, int)> | 2 | 1 | CSharp7.cs:102:22:102:46 | Item2 |
|
||||
| (Int32,Double) | (int, double) | ValueTuple<int, double> | 2 | 0 | CSharp7.cs:213:6:213:8 | Item1 |
|
||||
| (Int32,Double) | (int, double) | ValueTuple<int, double> | 2 | 1 | CSharp7.cs:213:11:213:16 | Item2 |
|
||||
| (Int32,Int32) | (int, int) | ValueTuple<int, int> | 2 | 0 | CSharp7.cs:62:10:62:10 | Item1 |
|
||||
| (Int32,Int32) | (int, int) | ValueTuple<int, int> | 2 | 1 | CSharp7.cs:62:17:62:17 | Item2 |
|
||||
| (Int32,String) | (int, string) | ValueTuple<int, string> | 2 | 0 | CSharp7.cs:95:19:95:19 | Item1 |
|
||||
| (Int32,String) | (int, string) | ValueTuple<int, string> | 2 | 1 | CSharp7.cs:95:22:95:37 | Item2 |
|
||||
| (String,Int32) | (string, int) | ValueTuple<string, int> | 2 | 0 | CSharp7.cs:82:17:82:17 | Item1 |
|
||||
| (String,Int32) | (string, int) | ValueTuple<string, int> | 2 | 0 | CSharp7.cs:101:19:101:38 | Item1 |
|
||||
| (String,Int32) | (string, int) | ValueTuple<string, int> | 2 | 1 | CSharp7.cs:82:23:82:23 | Item2 |
|
||||
| (String,String) | (string, string) | ValueTuple<string, string> | 2 | 0 | CSharp7.cs:87:19:87:27 | Item1 |
|
||||
| (String,String) | (string, string) | ValueTuple<string, string> | 2 | 1 | CSharp7.cs:87:30:87:33 | Item2 |
|
||||
|
||||
@@ -482,3 +482,14 @@ func EmitGoModVersionSupportedLowerEqualGoEnv(msg string) {
|
||||
noLocation,
|
||||
)
|
||||
}
|
||||
|
||||
func EmitNewerSystemGoRequired(requiredVersion string) {
|
||||
emitDiagnostic(
|
||||
"go/autobuilder/newer-system-go-version-required",
|
||||
"The Go version installed on the system is too old to support this project",
|
||||
"At least Go version `"+requiredVersion+"` is required to build this project, but the version installed on the system is older. [Install a newer version](https://github.com/actions/setup-go#basic).",
|
||||
severityError,
|
||||
fullVisibility,
|
||||
noLocation,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -91,6 +91,10 @@ func ExtractWithFlags(buildFlags []string, patterns []string) error {
|
||||
}
|
||||
pkgs, err := packages.Load(cfg, patterns...)
|
||||
if err != nil {
|
||||
// the toolchain directive is only supported in Go 1.21 and above
|
||||
if strings.Contains(err.Error(), "unknown directive: toolchain") {
|
||||
diagnostics.EmitNewerSystemGoRequired("1.21.0")
|
||||
}
|
||||
return err
|
||||
}
|
||||
log.Println("Done running packages.Load.")
|
||||
|
||||
@@ -35,22 +35,54 @@ newtype TApplicationModeEndpoint =
|
||||
argExpr.isVararg() and
|
||||
not exists(int i | i < idx and call.getArgument(i).(Argument).isVararg())
|
||||
)
|
||||
} or
|
||||
TMethodReturnValue(Call call) { not call instanceof ConstructorCall } or
|
||||
TOverriddenParameter(Parameter p, Method overriddenMethod) {
|
||||
not p.getCallable().callsConstructor(_) and
|
||||
p.getCallable().(Method).overrides(overriddenMethod)
|
||||
}
|
||||
|
||||
/**
|
||||
* An endpoint is a node that is a candidate for modeling.
|
||||
*/
|
||||
abstract private class ApplicationModeEndpoint extends TApplicationModeEndpoint {
|
||||
abstract predicate isArgOf(Call c, int idx);
|
||||
/**
|
||||
* Gets the callable to be modeled that this endpoint represents.
|
||||
*/
|
||||
abstract Callable getCallable();
|
||||
|
||||
Call getCall() { this.isArgOf(result, _) }
|
||||
abstract Call getCall();
|
||||
|
||||
int getArgIndex() { this.isArgOf(_, result) }
|
||||
/**
|
||||
* Gets the input (if any) for this endpoint, eg.: `Argument[0]`.
|
||||
*
|
||||
* For endpoints that are source candidates, this will be `none()`.
|
||||
*/
|
||||
abstract string getMaDInput();
|
||||
|
||||
/**
|
||||
* Gets the output (if any) for this endpoint, eg.: `ReturnValue`.
|
||||
*
|
||||
* For endpoints that are sink candidates, this will be `none()`.
|
||||
*/
|
||||
abstract string getMaDOutput();
|
||||
|
||||
abstract Top asTop();
|
||||
|
||||
/**
|
||||
* Converts the endpoint to a node that can be used in a data flow graph.
|
||||
*/
|
||||
abstract DataFlow::Node asNode();
|
||||
|
||||
string getExtensibleType() {
|
||||
if not exists(this.getMaDInput()) and exists(this.getMaDOutput())
|
||||
then result = "sourceModel"
|
||||
else
|
||||
if exists(this.getMaDInput()) and not exists(this.getMaDOutput())
|
||||
then result = "sinkModel"
|
||||
else none() // if both exist, it would be a summaryModel (not yet supported)
|
||||
}
|
||||
|
||||
abstract string toString();
|
||||
}
|
||||
|
||||
@@ -63,7 +95,15 @@ class ExplicitArgument extends ApplicationModeEndpoint, TExplicitArgument {
|
||||
|
||||
ExplicitArgument() { this = TExplicitArgument(call, arg) }
|
||||
|
||||
override predicate isArgOf(Call c, int idx) { c = call and this.asTop() = c.getArgument(idx) }
|
||||
override Callable getCallable() { result = call.getCallee() }
|
||||
|
||||
override Call getCall() { result = call }
|
||||
|
||||
private int getArgIndex() { this.asTop() = call.getArgument(result) }
|
||||
|
||||
override string getMaDInput() { result = "Argument[" + this.getArgIndex() + "]" }
|
||||
|
||||
override string getMaDOutput() { none() }
|
||||
|
||||
override Top asTop() { result = arg.asExpr() }
|
||||
|
||||
@@ -78,9 +118,13 @@ class InstanceArgument extends ApplicationModeEndpoint, TInstanceArgument {
|
||||
|
||||
InstanceArgument() { this = TInstanceArgument(call, arg) }
|
||||
|
||||
override predicate isArgOf(Call c, int idx) {
|
||||
c = call and this.asTop() = c.getQualifier() and idx = -1
|
||||
}
|
||||
override Callable getCallable() { result = call.getCallee() }
|
||||
|
||||
override Call getCall() { result = call }
|
||||
|
||||
override string getMaDInput() { result = "Argument[this]" }
|
||||
|
||||
override string getMaDOutput() { none() }
|
||||
|
||||
override Top asTop() { if exists(arg.asExpr()) then result = arg.asExpr() else result = call }
|
||||
|
||||
@@ -105,15 +149,78 @@ class ImplicitVarargsArray extends ApplicationModeEndpoint, TImplicitVarargsArra
|
||||
|
||||
ImplicitVarargsArray() { this = TImplicitVarargsArray(call, vararg, idx) }
|
||||
|
||||
override predicate isArgOf(Call c, int i) { c = call and i = idx }
|
||||
override Callable getCallable() { result = call.getCallee() }
|
||||
|
||||
override Top asTop() { result = this.getCall() }
|
||||
override Call getCall() { result = call }
|
||||
|
||||
override string getMaDInput() { result = "Argument[" + idx + "]" }
|
||||
|
||||
override string getMaDOutput() { none() }
|
||||
|
||||
override Top asTop() { result = call }
|
||||
|
||||
override DataFlow::Node asNode() { result = vararg }
|
||||
|
||||
override string toString() { result = vararg.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An endpoint that represents a method call. The `ReturnValue` of a method call
|
||||
* may be a source.
|
||||
*/
|
||||
class MethodReturnValue extends ApplicationModeEndpoint, TMethodReturnValue {
|
||||
Call call;
|
||||
|
||||
MethodReturnValue() { this = TMethodReturnValue(call) }
|
||||
|
||||
override Callable getCallable() { result = call.getCallee() }
|
||||
|
||||
override Call getCall() { result = call }
|
||||
|
||||
override string getMaDInput() { none() }
|
||||
|
||||
override string getMaDOutput() { result = "ReturnValue" }
|
||||
|
||||
override Top asTop() { result = call }
|
||||
|
||||
override DataFlow::Node asNode() { result.asExpr() = call }
|
||||
|
||||
override string toString() { result = call.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An endpoint that represents a parameter of an overridden method that may be
|
||||
* a source.
|
||||
*/
|
||||
class OverriddenParameter extends ApplicationModeEndpoint, TOverriddenParameter {
|
||||
Parameter p;
|
||||
Method overriddenMethod;
|
||||
|
||||
OverriddenParameter() { this = TOverriddenParameter(p, overriddenMethod) }
|
||||
|
||||
override Callable getCallable() {
|
||||
// NB: we're returning the overridden callable here. This means that the
|
||||
// candidate model will be about the overridden method, not the overriding
|
||||
// method. This is a more general model, that also applies to other
|
||||
// subclasses of the overridden class.
|
||||
result = overriddenMethod
|
||||
}
|
||||
|
||||
override Call getCall() { none() }
|
||||
|
||||
private int getArgIndex() { p.getCallable().getParameter(result) = p }
|
||||
|
||||
override string getMaDInput() { none() }
|
||||
|
||||
override string getMaDOutput() { result = "Parameter[" + this.getArgIndex() + "]" }
|
||||
|
||||
override Top asTop() { result = p }
|
||||
|
||||
override DataFlow::Node asNode() { result.(DataFlow::ParameterNode).asParameter() = p }
|
||||
|
||||
override string toString() { result = p.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A candidates implementation.
|
||||
*
|
||||
@@ -161,6 +268,14 @@ module ApplicationCandidatesImpl implements SharedCharacteristics::CandidateSig
|
||||
isCustomSink(e, kind) and provenance = "custom-sink"
|
||||
}
|
||||
|
||||
predicate isSource(Endpoint e, string kind, string provenance) {
|
||||
exists(string package, string type, string name, string signature, string ext, string output |
|
||||
sourceSpec(e, package, type, name, signature, ext, output) and
|
||||
ExternalFlow::sourceModel(package, type, _, name, [signature, ""], ext, output, kind,
|
||||
provenance)
|
||||
)
|
||||
}
|
||||
|
||||
predicate isNeutral(Endpoint e) {
|
||||
exists(string package, string type, string name, string signature |
|
||||
sinkSpec(e, package, type, name, signature, _, _) and
|
||||
@@ -168,13 +283,24 @@ module ApplicationCandidatesImpl implements SharedCharacteristics::CandidateSig
|
||||
)
|
||||
}
|
||||
|
||||
// XXX how to extend to support sources?
|
||||
additional predicate sinkSpec(
|
||||
Endpoint e, string package, string type, string name, string signature, string ext, string input
|
||||
) {
|
||||
ApplicationModeGetCallable::getCallable(e).hasQualifiedName(package, type, name) and
|
||||
signature = ExternalFlow::paramsString(ApplicationModeGetCallable::getCallable(e)) and
|
||||
e.getCallable().hasQualifiedName(package, type, name) and
|
||||
signature = ExternalFlow::paramsString(e.getCallable()) and
|
||||
ext = "" and
|
||||
input = AutomodelJavaUtil::getArgumentForIndex(e.getArgIndex())
|
||||
input = e.getMaDInput()
|
||||
}
|
||||
|
||||
additional predicate sourceSpec(
|
||||
Endpoint e, string package, string type, string name, string signature, string ext,
|
||||
string output
|
||||
) {
|
||||
e.getCallable().hasQualifiedName(package, type, name) and
|
||||
signature = ExternalFlow::paramsString(e.getCallable()) and
|
||||
ext = "" and
|
||||
output = e.getMaDOutput()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -229,11 +355,12 @@ class ApplicationModeMetadataExtractor extends string {
|
||||
|
||||
predicate hasMetadata(
|
||||
Endpoint e, string package, string type, string subtypes, string name, string signature,
|
||||
string input, string isVarargsArray
|
||||
string input, string output, string isVarargsArray
|
||||
) {
|
||||
exists(Callable callable |
|
||||
e.getCall().getCallee() = callable and
|
||||
input = AutomodelJavaUtil::getArgumentForIndex(e.getArgIndex()) and
|
||||
e.getCallable() = callable and
|
||||
(if exists(e.getMaDInput()) then input = e.getMaDInput() else input = "") and
|
||||
(if exists(e.getMaDOutput()) then output = e.getMaDOutput() else output = "") and
|
||||
package = callable.getDeclaringType().getPackage().getName() and
|
||||
// we're using the erased types because the MaD convention is to not specify type parameters.
|
||||
// Whether something is or isn't a sink doesn't usually depend on the type parameters.
|
||||
@@ -266,8 +393,8 @@ private class UnexploitableIsCharacteristic extends CharacteristicsImpl::NotASin
|
||||
|
||||
override predicate appliesToEndpoint(Endpoint e) {
|
||||
not ApplicationCandidatesImpl::isSink(e, _, _) and
|
||||
ApplicationModeGetCallable::getCallable(e).getName().matches("is%") and
|
||||
ApplicationModeGetCallable::getCallable(e).getReturnType() instanceof BooleanType
|
||||
e.getCallable().getName().matches("is%") and
|
||||
e.getCallable().getReturnType() instanceof BooleanType
|
||||
}
|
||||
}
|
||||
|
||||
@@ -313,9 +440,13 @@ private class IsMaDTaintStepCharacteristic extends CharacteristicsImpl::NotASink
|
||||
IsMaDTaintStepCharacteristic() { this = "taint step" }
|
||||
|
||||
override predicate appliesToEndpoint(Endpoint e) {
|
||||
FlowSummaryImpl::Private::Steps::summaryThroughStepValue(e.asNode(), _, _) or
|
||||
FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(e.asNode(), _, _) or
|
||||
FlowSummaryImpl::Private::Steps::summaryGetterStep(e.asNode(), _, _, _) or
|
||||
e.getExtensibleType() = "sinkModel" and
|
||||
FlowSummaryImpl::Private::Steps::summaryThroughStepValue(e.asNode(), _, _)
|
||||
or
|
||||
FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(e.asNode(), _, _)
|
||||
or
|
||||
FlowSummaryImpl::Private::Steps::summaryGetterStep(e.asNode(), _, _, _)
|
||||
or
|
||||
FlowSummaryImpl::Private::Steps::summarySetterStep(e.asNode(), _, _, _)
|
||||
}
|
||||
}
|
||||
@@ -326,8 +457,8 @@ private class IsMaDTaintStepCharacteristic extends CharacteristicsImpl::NotASink
|
||||
* The reason is that we would expect data/taint flow into the method implementation to uncover
|
||||
* any sinks that are present there.
|
||||
*/
|
||||
private class ArgumentToLocalCall extends CharacteristicsImpl::UninterestingToModelCharacteristic {
|
||||
ArgumentToLocalCall() { this = "argument to local call" }
|
||||
private class LocalCall extends CharacteristicsImpl::UninterestingToModelCharacteristic {
|
||||
LocalCall() { this = "local call" }
|
||||
|
||||
override predicate appliesToEndpoint(Endpoint e) {
|
||||
ApplicationModeGetCallable::getCallable(e).fromSource()
|
||||
@@ -354,6 +485,7 @@ private class NonPublicMethodCharacteristic extends CharacteristicsImpl::Uninter
|
||||
NonPublicMethodCharacteristic() { this = "non-public method" }
|
||||
|
||||
override predicate appliesToEndpoint(Endpoint e) {
|
||||
e.getExtensibleType() = "sinkModel" and
|
||||
not ApplicationModeGetCallable::getCallable(e).isPublic()
|
||||
}
|
||||
}
|
||||
@@ -376,6 +508,7 @@ private class OtherArgumentToModeledMethodCharacteristic extends Characteristics
|
||||
}
|
||||
|
||||
override predicate appliesToEndpoint(Endpoint e) {
|
||||
e.getExtensibleType() = "sinkModel" and
|
||||
not ApplicationCandidatesImpl::isSink(e, _, _) and
|
||||
exists(Endpoint otherSink |
|
||||
ApplicationCandidatesImpl::isSink(otherSink, _, "manual") and
|
||||
@@ -393,7 +526,10 @@ private class OtherArgumentToModeledMethodCharacteristic extends Characteristics
|
||||
private class FunctionValueCharacteristic extends CharacteristicsImpl::LikelyNotASinkCharacteristic {
|
||||
FunctionValueCharacteristic() { this = "function value" }
|
||||
|
||||
override predicate appliesToEndpoint(Endpoint e) { e.asNode().asExpr() instanceof FunctionalExpr }
|
||||
override predicate appliesToEndpoint(Endpoint e) {
|
||||
e.getExtensibleType() = "sinkModel" and
|
||||
e.asNode().asExpr() instanceof FunctionalExpr
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -407,7 +543,10 @@ private class CannotBeTaintedCharacteristic extends CharacteristicsImpl::LikelyN
|
||||
{
|
||||
CannotBeTaintedCharacteristic() { this = "cannot be tainted" }
|
||||
|
||||
override predicate appliesToEndpoint(Endpoint e) { not this.isKnownOutNodeForStep(e) }
|
||||
override predicate appliesToEndpoint(Endpoint e) {
|
||||
e.getExtensibleType() = "sinkModel" and
|
||||
not this.isKnownOutNodeForStep(e)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the node `n` is known as the predecessor in a modeled flow step.
|
||||
|
||||
@@ -25,18 +25,20 @@ private import AutomodelJavaUtil
|
||||
bindingset[limit]
|
||||
private Endpoint getSampleForSignature(
|
||||
int limit, string package, string type, string subtypes, string name, string signature,
|
||||
string input, string isVarargs
|
||||
string input, string output, string isVarargs, string extensibleType
|
||||
) {
|
||||
exists(int n, int num_endpoints, ApplicationModeMetadataExtractor meta |
|
||||
num_endpoints =
|
||||
count(Endpoint e |
|
||||
meta.hasMetadata(e, package, type, subtypes, name, signature, input, isVarargs)
|
||||
e.getExtensibleType() = extensibleType and
|
||||
meta.hasMetadata(e, package, type, subtypes, name, signature, input, output, isVarargs)
|
||||
)
|
||||
|
|
||||
result =
|
||||
rank[n](Endpoint e, Location loc |
|
||||
loc = e.asTop().getLocation() and
|
||||
meta.hasMetadata(e, package, type, subtypes, name, signature, input, isVarargs)
|
||||
e.getExtensibleType() = extensibleType and
|
||||
meta.hasMetadata(e, package, type, subtypes, name, signature, input, output, isVarargs)
|
||||
|
|
||||
e
|
||||
order by
|
||||
@@ -55,37 +57,39 @@ private Endpoint getSampleForSignature(
|
||||
from
|
||||
Endpoint endpoint, string message, ApplicationModeMetadataExtractor meta, DollarAtString package,
|
||||
DollarAtString type, DollarAtString subtypes, DollarAtString name, DollarAtString signature,
|
||||
DollarAtString input, DollarAtString isVarargsArray, DollarAtString alreadyAiModeled
|
||||
DollarAtString input, DollarAtString output, DollarAtString isVarargsArray,
|
||||
DollarAtString alreadyAiModeled, DollarAtString extensibleType
|
||||
where
|
||||
not exists(CharacteristicsImpl::UninterestingToModelCharacteristic u |
|
||||
u.appliesToEndpoint(endpoint)
|
||||
) and
|
||||
endpoint =
|
||||
getSampleForSignature(9, package, type, subtypes, name, signature, input, isVarargsArray) and
|
||||
// If a node is already a known sink for any of our existing ATM queries and is already modeled as a MaD sink, we
|
||||
// don't include it as a candidate. Otherwise, we might include it as a candidate for query A, but the model will
|
||||
// label it as a sink for one of the sink types of query B, for which it's already a known sink. This would result in
|
||||
// overlap between our detected sinks and the pre-existing modeling. We assume that, if a sink has already been
|
||||
// modeled in a MaD model, then it doesn't belong to any additional sink types, and we don't need to reexamine it.
|
||||
getSampleForSignature(9, package, type, subtypes, name, signature, input, output,
|
||||
isVarargsArray, extensibleType) and
|
||||
// If a node is already modeled in MaD, we don't include it as a candidate. Otherwise, we might include it as a
|
||||
// candidate for query A, but the model will label it as a sink for one of the sink types of query B, for which it's
|
||||
// already a known sink. This would result in overlap between our detected sinks and the pre-existing modeling. We
|
||||
// assume that, if a sink has already been modeled in a MaD model, then it doesn't belong to any additional sink
|
||||
// types, and we don't need to reexamine it.
|
||||
(
|
||||
not CharacteristicsImpl::isSink(endpoint, _, _) and alreadyAiModeled = ""
|
||||
not CharacteristicsImpl::isModeled(endpoint, _, _, _) and alreadyAiModeled = ""
|
||||
or
|
||||
alreadyAiModeled.matches("%ai-%") and
|
||||
CharacteristicsImpl::isSink(endpoint, _, alreadyAiModeled)
|
||||
CharacteristicsImpl::isModeled(endpoint, _, _, alreadyAiModeled)
|
||||
) and
|
||||
meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input, isVarargsArray) and
|
||||
meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input, output, isVarargsArray) and
|
||||
includeAutomodelCandidate(package, type, name, signature) and
|
||||
// The message is the concatenation of all sink types for which this endpoint is known neither to be a sink nor to be
|
||||
// a non-sink, and we surface only endpoints that have at least one such sink type.
|
||||
message =
|
||||
strictconcat(AutomodelEndpointTypes::SinkType sinkType |
|
||||
not CharacteristicsImpl::isKnownSink(endpoint, sinkType, _) and
|
||||
not CharacteristicsImpl::isKnownAs(endpoint, sinkType, _) and
|
||||
CharacteristicsImpl::isSinkCandidate(endpoint, sinkType)
|
||||
|
|
||||
sinkType, ", "
|
||||
)
|
||||
select endpoint.asNode(),
|
||||
message + "\nrelated locations: $@." + "\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@.", //
|
||||
message + "\nrelated locations: $@." + "\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@.", //
|
||||
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, CallContext()), "CallContext", //
|
||||
package, "package", //
|
||||
type, "type", //
|
||||
@@ -93,5 +97,7 @@ select endpoint.asNode(),
|
||||
name, "name", // method name
|
||||
signature, "signature", //
|
||||
input, "input", //
|
||||
output, "output", //
|
||||
isVarargsArray, "isVarargsArray", //
|
||||
alreadyAiModeled, "alreadyAiModeled"
|
||||
alreadyAiModeled, "alreadyAiModeled", //
|
||||
extensibleType, "extensibleType"
|
||||
|
||||
@@ -44,7 +44,7 @@ from
|
||||
Endpoint endpoint, EndpointCharacteristic characteristic, float confidence, string message,
|
||||
ApplicationModeMetadataExtractor meta, DollarAtString package, DollarAtString type,
|
||||
DollarAtString subtypes, DollarAtString name, DollarAtString signature, DollarAtString input,
|
||||
DollarAtString isVarargsArray
|
||||
DollarAtString output, DollarAtString isVarargsArray
|
||||
where
|
||||
endpoint = getSampleForCharacteristic(characteristic, 100) and
|
||||
confidence >= SharedCharacteristics::highConfidence() and
|
||||
@@ -52,7 +52,7 @@ where
|
||||
// Exclude endpoints that have contradictory endpoint characteristics, because we only want examples we're highly
|
||||
// certain about in the prompt.
|
||||
not erroneousEndpoints(endpoint, _, _, _, _, false) and
|
||||
meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input, isVarargsArray) and
|
||||
meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input, output, isVarargsArray) and
|
||||
// It's valid for a node to satisfy the logic for both `isSink` and `isSanitizer`, but in that case it will be
|
||||
// treated by the actual query as a sanitizer, since the final logic is something like
|
||||
// `isSink(n) and not isSanitizer(n)`. We don't want to include such nodes as negative examples in the prompt, because
|
||||
@@ -65,7 +65,7 @@ where
|
||||
) and
|
||||
message = characteristic
|
||||
select endpoint.asNode(),
|
||||
message + "\nrelated locations: $@." + "\nmetadata: $@, $@, $@, $@, $@, $@, $@.", //
|
||||
message + "\nrelated locations: $@." + "\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@.", //
|
||||
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, CallContext()), "CallContext", //
|
||||
package, "package", //
|
||||
type, "type", //
|
||||
@@ -73,4 +73,5 @@ select endpoint.asNode(),
|
||||
name, "name", //
|
||||
signature, "signature", //
|
||||
input, "input", //
|
||||
output, "output", //
|
||||
isVarargsArray, "isVarargsArray" //
|
||||
|
||||
@@ -13,19 +13,20 @@ private import AutomodelEndpointTypes
|
||||
private import AutomodelJavaUtil
|
||||
|
||||
from
|
||||
Endpoint endpoint, SinkType sinkType, ApplicationModeMetadataExtractor meta,
|
||||
Endpoint endpoint, EndpointType endpointType, ApplicationModeMetadataExtractor meta,
|
||||
DollarAtString package, DollarAtString type, DollarAtString subtypes, DollarAtString name,
|
||||
DollarAtString signature, DollarAtString input, DollarAtString isVarargsArray
|
||||
DollarAtString signature, DollarAtString input, DollarAtString output,
|
||||
DollarAtString isVarargsArray
|
||||
where
|
||||
// Exclude endpoints that have contradictory endpoint characteristics, because we only want examples we're highly
|
||||
// certain about in the prompt.
|
||||
not erroneousEndpoints(endpoint, _, _, _, _, false) and
|
||||
meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input, isVarargsArray) and
|
||||
meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input, output, isVarargsArray) and
|
||||
// Extract positive examples of sinks belonging to the existing ATM query configurations.
|
||||
CharacteristicsImpl::isKnownSink(endpoint, sinkType, _) and
|
||||
CharacteristicsImpl::isKnownAs(endpoint, endpointType, _) and
|
||||
exists(CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, CallContext()))
|
||||
select endpoint.asNode(),
|
||||
sinkType + "\nrelated locations: $@." + "\nmetadata: $@, $@, $@, $@, $@, $@, $@.", //
|
||||
endpointType + "\nrelated locations: $@." + "\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@.", //
|
||||
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, CallContext()), "CallContext", //
|
||||
package, "package", //
|
||||
type, "type", //
|
||||
@@ -33,4 +34,5 @@ select endpoint.asNode(),
|
||||
name, "name", //
|
||||
signature, "signature", //
|
||||
input, "input", //
|
||||
output, "output", //
|
||||
isVarargsArray, "isVarargsArray"
|
||||
|
||||
@@ -28,12 +28,6 @@ abstract class SinkType extends EndpointType {
|
||||
SinkType() { any() }
|
||||
}
|
||||
|
||||
/** A class for source types that can be predicted by a classifier. */
|
||||
abstract class SourceType extends EndpointType {
|
||||
bindingset[this]
|
||||
SourceType() { any() }
|
||||
}
|
||||
|
||||
/** The `Negative` class for non-sinks. */
|
||||
class NegativeSinkType extends SinkType {
|
||||
NegativeSinkType() { this = "non-sink" }
|
||||
@@ -58,3 +52,14 @@ class RequestForgerySinkType extends SinkType {
|
||||
class CommandInjectionSinkType extends SinkType {
|
||||
CommandInjectionSinkType() { this = "command-injection" }
|
||||
}
|
||||
|
||||
/** A class for source types that can be predicted by a classifier. */
|
||||
abstract class SourceType extends EndpointType {
|
||||
bindingset[this]
|
||||
SourceType() { any() }
|
||||
}
|
||||
|
||||
/** A source of remote data. */
|
||||
class RemoteSourceType extends SourceType {
|
||||
RemoteSourceType() { this = "remote" }
|
||||
}
|
||||
|
||||
@@ -117,6 +117,10 @@ module FrameworkCandidatesImpl implements SharedCharacteristics::CandidateSig {
|
||||
)
|
||||
}
|
||||
|
||||
predicate isSource(Endpoint e, string kind, string provenance) {
|
||||
none() // TODO: implement
|
||||
}
|
||||
|
||||
predicate isNeutral(Endpoint e) {
|
||||
exists(string package, string type, string name, string signature |
|
||||
sinkSpec(e, package, type, name, signature, _, _) and
|
||||
|
||||
@@ -40,7 +40,7 @@ where
|
||||
// a non-sink, and we surface only endpoints that have at least one such sink type.
|
||||
message =
|
||||
strictconcat(AutomodelEndpointTypes::SinkType sinkType |
|
||||
not CharacteristicsImpl::isKnownSink(endpoint, sinkType, _) and
|
||||
not CharacteristicsImpl::isKnownAs(endpoint, sinkType, _) and
|
||||
CharacteristicsImpl::isSinkCandidate(endpoint, sinkType)
|
||||
|
|
||||
sinkType, ", "
|
||||
|
||||
@@ -22,7 +22,7 @@ where
|
||||
not erroneousEndpoints(endpoint, _, _, _, _, false) and
|
||||
meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input, parameterName) and
|
||||
// Extract positive examples of sinks belonging to the existing ATM query configurations.
|
||||
CharacteristicsImpl::isKnownSink(endpoint, sinkType, _)
|
||||
CharacteristicsImpl::isKnownAs(endpoint, sinkType, _)
|
||||
select endpoint,
|
||||
sinkType + "\nrelated locations: $@, $@." + "\nmetadata: $@, $@, $@, $@, $@, $@, $@.", //
|
||||
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, MethodDoc()), "MethodDoc", //
|
||||
|
||||
@@ -39,6 +39,9 @@ predicate isKnownKind(string kind, AutomodelEndpointTypes::EndpointType type) {
|
||||
or
|
||||
kind = "command-injection" and
|
||||
type instanceof AutomodelEndpointTypes::CommandInjectionSinkType
|
||||
or
|
||||
kind = "remote" and
|
||||
type instanceof AutomodelEndpointTypes::RemoteSourceType
|
||||
}
|
||||
|
||||
/** Gets the models-as-data description for the method argument with the index `index`. */
|
||||
|
||||
@@ -62,6 +62,11 @@ signature module CandidateSig {
|
||||
*/
|
||||
predicate isSink(Endpoint e, string kind, string provenance);
|
||||
|
||||
/**
|
||||
* Holds if `e` is a sink with the label `kind`, and provenance `provenance`.
|
||||
*/
|
||||
predicate isSource(Endpoint e, string kind, string provenance);
|
||||
|
||||
/**
|
||||
* Holds if `e` is not a sink of any kind.
|
||||
*/
|
||||
@@ -91,17 +96,23 @@ module SharedCharacteristics<CandidateSig Candidate> {
|
||||
|
||||
predicate isNeutral = Candidate::isNeutral/1;
|
||||
|
||||
predicate isModeled(Candidate::Endpoint e, string kind, string extensibleKind, string provenance) {
|
||||
Candidate::isSink(e, kind, provenance) and extensibleKind = "sinkModel"
|
||||
or
|
||||
Candidate::isSource(e, kind, provenance) and extensibleKind = "sourceModel"
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `sink` is a known sink of type `endpointType`.
|
||||
* Holds if `endpoint` is modeled as `endpointType` (endpoint type must not be negative).
|
||||
*/
|
||||
predicate isKnownSink(
|
||||
Candidate::Endpoint sink, Candidate::EndpointType endpointType,
|
||||
predicate isKnownAs(
|
||||
Candidate::Endpoint endpoint, Candidate::EndpointType endpointType,
|
||||
EndpointCharacteristic characteristic
|
||||
) {
|
||||
// If the list of characteristics includes positive indicators with maximal confidence for this class, then it's a
|
||||
// known sink for the class.
|
||||
not endpointType instanceof Candidate::NegativeEndpointType and
|
||||
characteristic.appliesToEndpoint(sink) and
|
||||
characteristic.appliesToEndpoint(endpoint) and
|
||||
characteristic.hasImplications(endpointType, true, maximalConfidence())
|
||||
}
|
||||
|
||||
@@ -209,6 +220,25 @@ module SharedCharacteristics<CandidateSig Candidate> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A high-confidence characteristic that indicates that an endpoint is a source of a specified type. These endpoints can
|
||||
* be used as positive samples for training or for a few-shot prompt.
|
||||
*/
|
||||
abstract class SourceCharacteristic extends EndpointCharacteristic {
|
||||
bindingset[this]
|
||||
SourceCharacteristic() { any() }
|
||||
|
||||
abstract Candidate::EndpointType getSourceType();
|
||||
|
||||
final override predicate hasImplications(
|
||||
Candidate::EndpointType endpointType, boolean isPositiveIndicator, float confidence
|
||||
) {
|
||||
endpointType = this.getSourceType() and
|
||||
isPositiveIndicator = true and
|
||||
confidence = maximalConfidence()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A high-confidence characteristic that indicates that an endpoint is not a sink of any type. These endpoints can be
|
||||
* used as negative samples for training or for a few-shot prompt.
|
||||
@@ -292,6 +322,25 @@ module SharedCharacteristics<CandidateSig Candidate> {
|
||||
override Candidate::EndpointType getSinkType() { result = endpointType }
|
||||
}
|
||||
|
||||
private class KnownSourceCharacteristic extends SourceCharacteristic {
|
||||
string madKind;
|
||||
Candidate::EndpointType endpointType;
|
||||
string provenance;
|
||||
|
||||
KnownSourceCharacteristic() {
|
||||
Candidate::isKnownKind(madKind, endpointType) and
|
||||
// bind "this" to a unique string differing from that of the SinkType classes
|
||||
this = madKind + "_" + provenance + "_characteristic" and
|
||||
Candidate::isSource(_, madKind, provenance)
|
||||
}
|
||||
|
||||
override predicate appliesToEndpoint(Candidate::Endpoint e) {
|
||||
Candidate::isSource(e, madKind, provenance)
|
||||
}
|
||||
|
||||
override Candidate::EndpointType getSourceType() { result = endpointType }
|
||||
}
|
||||
|
||||
/**
|
||||
* A negative characteristic that indicates that an endpoint was manually modeled as a neutral model.
|
||||
*/
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
| Test.java:16:3:16:11 | reference | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:16:3:16:24 | set(...) | CallContext | file://java.util.concurrent.atomic:1:1:1:1 | java.util.concurrent.atomic | package | file://AtomicReference:1:1:1:1 | AtomicReference | type | file://false:1:1:1:1 | false | subtypes | file://set:1:1:1:1 | set | name | file://(String):1:1:1:1 | (String) | signature | file://Argument[this]:1:1:1:1 | Argument[this] | input | file://false:1:1:1:1 | false | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled |
|
||||
| Test.java:21:3:21:10 | supplier | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:21:3:21:16 | get(...) | CallContext | file://java.util.function:1:1:1:1 | java.util.function | package | file://Supplier:1:1:1:1 | Supplier | type | file://true:1:1:1:1 | true | subtypes | file://get:1:1:1:1 | get | name | file://():1:1:1:1 | () | signature | file://Argument[this]:1:1:1:1 | Argument[this] | input | file://false:1:1:1:1 | false | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled |
|
||||
| Test.java:34:4:34:11 | openPath | command-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:33:10:35:3 | newInputStream(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://newInputStream:1:1:1:1 | newInputStream | name | file://(Path,OpenOption[]):1:1:1:1 | (Path,OpenOption[]) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | file://false:1:1:1:1 | false | isVarargsArray | file://ai-manual:1:1:1:1 | ai-manual | alreadyAiModeled |
|
||||
| Test.java:53:4:53:4 | o | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:51:3:56:3 | walk(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://walk:1:1:1:1 | walk | name | file://(Path,FileVisitOption[]):1:1:1:1 | (Path,FileVisitOption[]) | signature | file://Argument[1]:1:1:1:1 | Argument[1] | input | file://true:1:1:1:1 | true | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled |
|
||||
| PluginImpl.java:5:27:5:37 | name | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | PluginImpl.java:5:27:5:37 | name | CallContext | file://hudson:1:1:1:1 | hudson | package | file://Plugin:1:1:1:1 | Plugin | type | file://true:1:1:1:1 | true | subtypes | file://configure:1:1:1:1 | configure | name | file://(String,String):1:1:1:1 | (String,String) | signature | file://:1:1:1:1 | | input | file://Parameter[0]:1:1:1:1 | Parameter[0] | output | file://false:1:1:1:1 | false | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled | file://sourceModel:1:1:1:1 | sourceModel | extensibleType |
|
||||
| PluginImpl.java:5:40:5:51 | value | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | PluginImpl.java:5:40:5:51 | value | CallContext | file://hudson:1:1:1:1 | hudson | package | file://Plugin:1:1:1:1 | Plugin | type | file://true:1:1:1:1 | true | subtypes | file://configure:1:1:1:1 | configure | name | file://(String,String):1:1:1:1 | (String,String) | signature | file://:1:1:1:1 | | input | file://Parameter[1]:1:1:1:1 | Parameter[1] | output | file://false:1:1:1:1 | false | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled | file://sourceModel:1:1:1:1 | sourceModel | extensibleType |
|
||||
| Test.java:18:3:18:11 | reference | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:18:3:18:24 | set(...) | CallContext | file://java.util.concurrent.atomic:1:1:1:1 | java.util.concurrent.atomic | package | file://AtomicReference:1:1:1:1 | AtomicReference | type | file://false:1:1:1:1 | false | subtypes | file://set:1:1:1:1 | set | name | file://(String):1:1:1:1 | (String) | signature | file://Argument[this]:1:1:1:1 | Argument[this] | input | file://:1:1:1:1 | | output | file://false:1:1:1:1 | false | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled | file://sinkModel:1:1:1:1 | sinkModel | extensibleType |
|
||||
| Test.java:23:3:23:10 | supplier | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:23:3:23:16 | get(...) | CallContext | file://java.util.function:1:1:1:1 | java.util.function | package | file://Supplier:1:1:1:1 | Supplier | type | file://true:1:1:1:1 | true | subtypes | file://get:1:1:1:1 | get | name | file://():1:1:1:1 | () | signature | file://Argument[this]:1:1:1:1 | Argument[this] | input | file://:1:1:1:1 | | output | file://false:1:1:1:1 | false | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled | file://sinkModel:1:1:1:1 | sinkModel | extensibleType |
|
||||
| Test.java:23:3:23:16 | get(...) | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:23:3:23:16 | get(...) | CallContext | file://java.util.function:1:1:1:1 | java.util.function | package | file://Supplier:1:1:1:1 | Supplier | type | file://true:1:1:1:1 | true | subtypes | file://get:1:1:1:1 | get | name | file://():1:1:1:1 | () | signature | file://:1:1:1:1 | | input | file://ReturnValue:1:1:1:1 | ReturnValue | output | file://false:1:1:1:1 | false | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled | file://sourceModel:1:1:1:1 | sourceModel | extensibleType |
|
||||
| Test.java:27:3:31:3 | copy(...) | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:27:3:31:3 | copy(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://copy:1:1:1:1 | copy | name | file://(Path,Path,CopyOption[]):1:1:1:1 | (Path,Path,CopyOption[]) | signature | file://:1:1:1:1 | | input | file://ReturnValue:1:1:1:1 | ReturnValue | output | file://false:1:1:1:1 | false | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled | file://sourceModel:1:1:1:1 | sourceModel | extensibleType |
|
||||
| Test.java:35:10:37:3 | newInputStream(...) | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:35:10:37:3 | newInputStream(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://newInputStream:1:1:1:1 | newInputStream | name | file://(Path,OpenOption[]):1:1:1:1 | (Path,OpenOption[]) | signature | file://:1:1:1:1 | | input | file://ReturnValue:1:1:1:1 | ReturnValue | output | file://false:1:1:1:1 | false | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled | file://sourceModel:1:1:1:1 | sourceModel | extensibleType |
|
||||
| Test.java:36:4:36:11 | openPath | command-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:35:10:37:3 | newInputStream(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://newInputStream:1:1:1:1 | newInputStream | name | file://(Path,OpenOption[]):1:1:1:1 | (Path,OpenOption[]) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | file://:1:1:1:1 | | output | file://false:1:1:1:1 | false | isVarargsArray | file://ai-manual:1:1:1:1 | ai-manual | alreadyAiModeled | file://sinkModel:1:1:1:1 | sinkModel | extensibleType |
|
||||
| Test.java:42:4:42:22 | get(...) | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:42:4:42:22 | get(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Paths:1:1:1:1 | Paths | type | file://false:1:1:1:1 | false | subtypes | file://get:1:1:1:1 | get | name | file://(String,String[]):1:1:1:1 | (String,String[]) | signature | file://:1:1:1:1 | | input | file://ReturnValue:1:1:1:1 | ReturnValue | output | file://false:1:1:1:1 | false | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled | file://sourceModel:1:1:1:1 | sourceModel | extensibleType |
|
||||
| Test.java:53:3:58:3 | walk(...) | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:53:3:58:3 | walk(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://walk:1:1:1:1 | walk | name | file://(Path,FileVisitOption[]):1:1:1:1 | (Path,FileVisitOption[]) | signature | file://:1:1:1:1 | | input | file://ReturnValue:1:1:1:1 | ReturnValue | output | file://false:1:1:1:1 | false | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled | file://sourceModel:1:1:1:1 | sourceModel | extensibleType |
|
||||
| Test.java:55:4:55:4 | o | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:53:3:58:3 | walk(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://walk:1:1:1:1 | walk | name | file://(Path,FileVisitOption[]):1:1:1:1 | (Path,FileVisitOption[]) | signature | file://Argument[1]:1:1:1:1 | Argument[1] | input | file://:1:1:1:1 | | output | file://true:1:1:1:1 | true | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled | file://sinkModel:1:1:1:1 | sinkModel | extensibleType |
|
||||
| Test.java:62:3:62:3 | c | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:62:3:62:20 | getInputStream(...) | CallContext | file://java.net:1:1:1:1 | java.net | package | file://URLConnection:1:1:1:1 | URLConnection | type | file://true:1:1:1:1 | true | subtypes | file://getInputStream:1:1:1:1 | getInputStream | name | file://():1:1:1:1 | () | signature | file://Argument[this]:1:1:1:1 | Argument[this] | input | file://:1:1:1:1 | | output | file://false:1:1:1:1 | false | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled | file://sinkModel:1:1:1:1 | sinkModel | extensibleType |
|
||||
| Test.java:67:30:67:47 | writer | command-injection, path-injection, request-forgery, sql-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:67:30:67:47 | writer | CallContext | file://java.lang:1:1:1:1 | java.lang | package | file://Throwable:1:1:1:1 | Throwable | type | file://true:1:1:1:1 | true | subtypes | file://printStackTrace:1:1:1:1 | printStackTrace | name | file://(PrintWriter):1:1:1:1 | (PrintWriter) | signature | file://:1:1:1:1 | | input | file://Parameter[0]:1:1:1:1 | Parameter[0] | output | file://false:1:1:1:1 | false | isVarargsArray | file://:1:1:1:1 | | alreadyAiModeled | file://sourceModel:1:1:1:1 | sourceModel | extensibleType |
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
| Test.java:46:4:46:5 | f2 | known non-sink\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@. | Test.java:45:10:47:3 | compareTo(...) | CallContext | file://java.io:1:1:1:1 | java.io | package | file://File:1:1:1:1 | File | type | file://true:1:1:1:1 | true | subtypes | file://compareTo:1:1:1:1 | compareTo | name | file://(File):1:1:1:1 | (File) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | file://false:1:1:1:1 | false | isVarargsArray |
|
||||
| Test.java:52:4:52:4 | p | taint step\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@. | Test.java:51:3:56:3 | walk(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://walk:1:1:1:1 | walk | name | file://(Path,FileVisitOption[]):1:1:1:1 | (Path,FileVisitOption[]) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | file://false:1:1:1:1 | false | isVarargsArray |
|
||||
| Test.java:47:10:49:3 | compareTo(...) | known sanitizer\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:47:10:49:3 | compareTo(...) | CallContext | file://java.io:1:1:1:1 | java.io | package | file://File:1:1:1:1 | File | type | file://true:1:1:1:1 | true | subtypes | file://compareTo:1:1:1:1 | compareTo | name | file://(File):1:1:1:1 | (File) | signature | file://:1:1:1:1 | | input | file://ReturnValue:1:1:1:1 | ReturnValue | output | file://false:1:1:1:1 | false | isVarargsArray |
|
||||
| Test.java:48:4:48:5 | f2 | known non-sink\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:47:10:49:3 | compareTo(...) | CallContext | file://java.io:1:1:1:1 | java.io | package | file://File:1:1:1:1 | File | type | file://true:1:1:1:1 | true | subtypes | file://compareTo:1:1:1:1 | compareTo | name | file://(File):1:1:1:1 | (File) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | file://:1:1:1:1 | | output | file://false:1:1:1:1 | false | isVarargsArray |
|
||||
| Test.java:54:4:54:4 | p | taint step\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:53:3:58:3 | walk(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://walk:1:1:1:1 | walk | name | file://(Path,FileVisitOption[]):1:1:1:1 | (Path,FileVisitOption[]) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | file://:1:1:1:1 | | output | file://false:1:1:1:1 | false | isVarargsArray |
|
||||
| Test.java:66:7:66:18 | this <constr(this)> | exception\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:66:7:66:18 | super(...) | CallContext | file://java.lang:1:1:1:1 | java.lang | package | file://Exception:1:1:1:1 | Exception | type | file://true:1:1:1:1 | true | subtypes | file://Exception:1:1:1:1 | Exception | name | file://():1:1:1:1 | () | signature | file://Argument[this]:1:1:1:1 | Argument[this] | input | file://:1:1:1:1 | | output | file://false:1:1:1:1 | false | isVarargsArray |
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
| Test.java:26:4:26:9 | source | path-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@. | Test.java:25:3:29:3 | copy(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://copy:1:1:1:1 | copy | name | file://(Path,Path,CopyOption[]):1:1:1:1 | (Path,Path,CopyOption[]) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | file://false:1:1:1:1 | false | isVarargsArray |
|
||||
| Test.java:27:4:27:9 | target | path-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@. | Test.java:25:3:29:3 | copy(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://copy:1:1:1:1 | copy | name | file://(Path,Path,CopyOption[]):1:1:1:1 | (Path,Path,CopyOption[]) | signature | file://Argument[1]:1:1:1:1 | Argument[1] | input | file://false:1:1:1:1 | false | isVarargsArray |
|
||||
| Test.java:34:4:34:11 | openPath | path-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@. | Test.java:33:10:35:3 | newInputStream(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://newInputStream:1:1:1:1 | newInputStream | name | file://(Path,OpenOption[]):1:1:1:1 | (Path,OpenOption[]) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | file://false:1:1:1:1 | false | isVarargsArray |
|
||||
| Test.java:28:4:28:9 | source | path-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:27:3:31:3 | copy(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://copy:1:1:1:1 | copy | name | file://(Path,Path,CopyOption[]):1:1:1:1 | (Path,Path,CopyOption[]) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | file://:1:1:1:1 | | output | file://false:1:1:1:1 | false | isVarargsArray |
|
||||
| Test.java:29:4:29:9 | target | path-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:27:3:31:3 | copy(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://copy:1:1:1:1 | copy | name | file://(Path,Path,CopyOption[]):1:1:1:1 | (Path,Path,CopyOption[]) | signature | file://Argument[1]:1:1:1:1 | Argument[1] | input | file://:1:1:1:1 | | output | file://false:1:1:1:1 | false | isVarargsArray |
|
||||
| Test.java:36:4:36:11 | openPath | path-injection\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:35:10:37:3 | newInputStream(...) | CallContext | file://java.nio.file:1:1:1:1 | java.nio.file | package | file://Files:1:1:1:1 | Files | type | file://false:1:1:1:1 | false | subtypes | file://newInputStream:1:1:1:1 | newInputStream | name | file://(Path,OpenOption[]):1:1:1:1 | (Path,OpenOption[]) | signature | file://Argument[0]:1:1:1:1 | Argument[0] | input | file://:1:1:1:1 | | output | file://false:1:1:1:1 | false | isVarargsArray |
|
||||
| Test.java:62:3:62:20 | getInputStream(...) | remote\nrelated locations: $@.\nmetadata: $@, $@, $@, $@, $@, $@, $@, $@. | Test.java:62:3:62:20 | getInputStream(...) | CallContext | file://java.net:1:1:1:1 | java.net | package | file://URLConnection:1:1:1:1 | URLConnection | type | file://true:1:1:1:1 | true | subtypes | file://getInputStream:1:1:1:1 | getInputStream | name | file://():1:1:1:1 | () | signature | file://:1:1:1:1 | | input | file://ReturnValue:1:1:1:1 | ReturnValue | output | file://false:1:1:1:1 | false | isVarargsArray |
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import hudson.Plugin;
|
||||
|
||||
public class PluginImpl extends Plugin {
|
||||
@Override
|
||||
public void configure(String name, String value) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.github.codeql.test;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.file.CopyOption;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
@@ -9,6 +10,7 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Supplier;
|
||||
import java.io.File;
|
||||
import java.nio.file.FileVisitOption;
|
||||
import java.net.URLConnection;
|
||||
|
||||
class Test {
|
||||
public static void main(String[] args) throws Exception {
|
||||
@@ -18,11 +20,11 @@ class Test {
|
||||
}
|
||||
|
||||
public static void callSupplier(Supplier<String> supplier) {
|
||||
supplier.get(); // Argument[this] is a candidate
|
||||
supplier.get(); // Argument[this] is a sink candidate; the call is a source candidate
|
||||
}
|
||||
|
||||
public static void copyFiles(Path source, Path target, CopyOption option) throws Exception {
|
||||
Files.copy(
|
||||
Files.copy( // the call is a source candidate
|
||||
source, // positive example (known sink)
|
||||
target, // positive example (known sink)
|
||||
option // no candidate (not modeled, but source and target are modeled)
|
||||
@@ -30,29 +32,39 @@ class Test {
|
||||
}
|
||||
|
||||
public static InputStream getInputStream(Path openPath) throws Exception {
|
||||
return Files.newInputStream(
|
||||
return Files.newInputStream( // the call is a source candidate
|
||||
openPath // positive example (known sink), candidate ("only" ai-modeled, and useful as a candidate in regression testing)
|
||||
);
|
||||
}
|
||||
|
||||
public static InputStream getInputStream(String openPath) throws Exception {
|
||||
return Test.getInputStream(
|
||||
Paths.get(openPath) // no candidate (argument to local call)
|
||||
return Test.getInputStream( // the call is not a source candidate (argument to local call)
|
||||
Paths.get(openPath) // no sink candidate (argument to local call); the call is a source candidate
|
||||
);
|
||||
}
|
||||
|
||||
public static int compareFiles(File f1, File f2) {
|
||||
return f1.compareTo(
|
||||
return f1.compareTo( // compareTo call is a known sanitizer
|
||||
f2 // negative example (modeled as not a sink)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public static void FilesWalkExample(Path p, FileVisitOption o) throws Exception {
|
||||
Files.walk(
|
||||
Files.walk( // the call is a source candidate
|
||||
p, // negative example (modeled as a taint step)
|
||||
o, // the implicit varargs array is a candidate
|
||||
o // not a candidate (only the first arg corresponding to a varargs array
|
||||
// is extracted)
|
||||
);
|
||||
}
|
||||
|
||||
public static void WebSocketExample(URLConnection c) throws Exception {
|
||||
c.getInputStream(); // the call is a source example, c is a sink candidate
|
||||
}
|
||||
}
|
||||
|
||||
class OverrideTest extends Exception {
|
||||
public void printStackTrace(PrintWriter writer) { // writer is a source candidate because it overrides an existing method
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package hudson;
|
||||
|
||||
public class Plugin {
|
||||
public void configure(String name, String value) {}
|
||||
}
|
||||
@@ -4396,6 +4396,117 @@ private module StdlibPrivate {
|
||||
preservesValue = true
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// asyncio
|
||||
// ---------------------------------------------------------------------------
|
||||
/** Provides models for the `asyncio` module. */
|
||||
module AsyncIO {
|
||||
/**
|
||||
* A call to the `asyncio.create_subprocess_exec` function (also accessible via the `subprocess` module of `asyncio`)
|
||||
*
|
||||
* See https://docs.python.org/3/library/asyncio-subprocess.html#creating-subprocesses
|
||||
*/
|
||||
private class CreateSubprocessExec extends SystemCommandExecution::Range,
|
||||
FileSystemAccess::Range, API::CallNode
|
||||
{
|
||||
CreateSubprocessExec() {
|
||||
this = API::moduleImport("asyncio").getMember("create_subprocess_exec").getACall()
|
||||
or
|
||||
this =
|
||||
API::moduleImport("asyncio")
|
||||
.getMember("subprocess")
|
||||
.getMember("create_subprocess_exec")
|
||||
.getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getCommand() { result = this.getParameter(0, "program").asSink() }
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = this.getCommand() }
|
||||
|
||||
override predicate isShellInterpreted(DataFlow::Node arg) {
|
||||
none() // this is a safe API.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the `asyncio.create_subprocess_shell` function (also accessible via the `subprocess` module of `asyncio`)
|
||||
*
|
||||
* See https://docs.python.org/3/library/asyncio-subprocess.html#asyncio.create_subprocess_shell
|
||||
*/
|
||||
private class CreateSubprocessShell extends SystemCommandExecution::Range,
|
||||
FileSystemAccess::Range, API::CallNode
|
||||
{
|
||||
CreateSubprocessShell() {
|
||||
this = API::moduleImport("asyncio").getMember("create_subprocess_shell").getACall()
|
||||
or
|
||||
this =
|
||||
API::moduleImport("asyncio")
|
||||
.getMember("subprocess")
|
||||
.getMember("create_subprocess_shell")
|
||||
.getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getCommand() { result = this.getParameter(0, "cmd").asSink() }
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = this.getCommand() }
|
||||
|
||||
override predicate isShellInterpreted(DataFlow::Node arg) { arg = this.getCommand() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an asyncio event loop (an object with basetype `AbstractEventLoop`).
|
||||
*
|
||||
* See https://docs.python.org/3/library/asyncio-eventloop.html
|
||||
*/
|
||||
private API::Node getAsyncioEventLoop() {
|
||||
result = API::moduleImport("asyncio").getMember("get_running_loop").getReturn()
|
||||
or
|
||||
result = API::moduleImport("asyncio").getMember("get_event_loop").getReturn() // deprecated in Python 3.10.0 and later
|
||||
or
|
||||
result = API::moduleImport("asyncio").getMember("new_event_loop").getReturn()
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `subprocess_exec` on an event loop instance.
|
||||
*
|
||||
* See https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.subprocess_exec
|
||||
*/
|
||||
private class EventLoopSubprocessExec extends API::CallNode, SystemCommandExecution::Range,
|
||||
FileSystemAccess::Range
|
||||
{
|
||||
EventLoopSubprocessExec() {
|
||||
this = getAsyncioEventLoop().getMember("subprocess_exec").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getCommand() { result = this.getArg(1) }
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = this.getCommand() }
|
||||
|
||||
override predicate isShellInterpreted(DataFlow::Node arg) {
|
||||
none() // this is a safe API.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `subprocess_shell` on an event loop instance.
|
||||
*
|
||||
* See https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.subprocess_shell
|
||||
*/
|
||||
private class EventLoopSubprocessShell extends API::CallNode, SystemCommandExecution::Range,
|
||||
FileSystemAccess::Range
|
||||
{
|
||||
EventLoopSubprocessShell() {
|
||||
this = getAsyncioEventLoop().getMember("subprocess_shell").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getCommand() { result = this.getParameter(1, "cmd").asSink() }
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = this.getCommand() }
|
||||
|
||||
override predicate isShellInterpreted(DataFlow::Node arg) { arg = this.getCommand() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -2,4 +2,33 @@
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
<include src="CleartextStorage.qhelp" /></qhelp>
|
||||
|
||||
<overview>
|
||||
|
||||
<p>If sensitive data is written to a log entry it could be exposed to an attacker
|
||||
who gains access to the logs.</p>
|
||||
|
||||
<p>Potential attackers can obtain sensitive user data when the log output is displayed. Additionally that data may
|
||||
expose system information such as full path names, system information, and sometimes usernames and passwords.</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>
|
||||
Sensitive data should not be logged.
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>In the example the entire process environment is logged using `print`. Regular users of the production deployed application
|
||||
should not have access to this much information about the environment configuration.
|
||||
</p>
|
||||
<sample src="examples/CleartextLogging.py" />
|
||||
|
||||
<p> In the second example the data that is logged is not sensitive.</p>
|
||||
<sample src="examples/CleartextLoggingGood.py" />
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>OWASP: <a href="https://owasp.org/Top10/A09_2021-Security_Logging_and_Monitoring_Failures/">Insertion of Sensitive Information into Log File</a>.</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
# BAD: Logging cleartext sensitive data
|
||||
import os
|
||||
print(f"[INFO] Environment: {os.environ}")
|
||||
@@ -0,0 +1,3 @@
|
||||
not_sensitive_data = {'a': 1, 'b': 2}
|
||||
# GOOD: it is fine to log data that is not sensitive
|
||||
print(f"[INFO] Some object contains: {not_sensitive_data}")
|
||||
@@ -8,8 +8,8 @@ def full_ssrf():
|
||||
target = request.args["target"]
|
||||
|
||||
# BAD: user has full control of URL
|
||||
resp = request.get("https://" + target + ".example.com/data/")
|
||||
resp = requests.get("https://" + target + ".example.com/data/")
|
||||
|
||||
# GOOD: `subdomain` is controlled by the server.
|
||||
subdomain = "europe" if target == "EU" else "world"
|
||||
resp = request.get("https://" + subdomain + ".example.com/data/")
|
||||
resp = requests.get("https://" + subdomain + ".example.com/data/")
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Extended the `py/command-line-injection` query with sinks from Python's `asyncio` module.
|
||||
@@ -158,3 +158,21 @@ safe_cmd = "ls {}".format(shlex.quote(tainted))
|
||||
wrong_use = shlex.quote("ls {}".format(tainted))
|
||||
# still dangerous, for example
|
||||
cmd = "sh -c " + wrong_use
|
||||
|
||||
########################################
|
||||
# Program/shell command execution via asyncio
|
||||
|
||||
import asyncio
|
||||
from asyncio import subprocess
|
||||
|
||||
asyncio.run(asyncio.create_subprocess_exec("executable", "arg0")) # $getCommand="executable" getAPathArgument="executable"
|
||||
asyncio.run(subprocess.create_subprocess_exec("executable", "arg0")) # $getCommand="executable" getAPathArgument="executable"
|
||||
|
||||
loop = asyncio.new_event_loop()
|
||||
loop.run_until_complete(loop.subprocess_exec(asyncio.SubprocessProtocol, "executable", "arg0")) # $getCommand="executable" getAPathArgument="executable"
|
||||
|
||||
asyncio.run(asyncio.create_subprocess_shell("shell_command")) # $getCommand="shell_command" getAPathArgument="shell_command"
|
||||
asyncio.run(subprocess.create_subprocess_shell("shell_command")) # $getCommand="shell_command" getAPathArgument="shell_command"
|
||||
|
||||
loop = asyncio.get_running_loop()
|
||||
loop.run_until_complete(loop.subprocess_shell(asyncio.SubprocessProtocol, "shell_command")) # $getCommand="shell_command" getAPathArgument="shell_command"
|
||||
|
||||
BIN
ql/Cargo.lock
generated
BIN
ql/Cargo.lock
generated
Binary file not shown.
@@ -7,6 +7,6 @@ edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
lazy_static = "1.4.0"
|
||||
chrono = "0.4.29"
|
||||
chrono = "0.4.30"
|
||||
rayon = "1.7.0"
|
||||
regex = "1.9.5"
|
||||
|
||||
4
swift/ql/lib/change-notes/2023-08-11-dictionary-flow.md
Normal file
4
swift/ql/lib/change-notes/2023-08-11-dictionary-flow.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Added flow steps through `Dictionary` keys and values.
|
||||
@@ -9,6 +9,7 @@ private import codeql.swift.dataflow.FlowSummary as FlowSummary
|
||||
private import codeql.swift.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
private import codeql.swift.frameworks.StandardLibrary.PointerTypes
|
||||
private import codeql.swift.frameworks.StandardLibrary.Array
|
||||
private import codeql.swift.frameworks.StandardLibrary.Dictionary
|
||||
|
||||
/** Gets the callable in which this node occurs. */
|
||||
DataFlowCallable nodeGetEnclosingCallable(Node n) { result = n.(NodeImpl).getEnclosingCallable() }
|
||||
@@ -139,6 +140,9 @@ private module Cached {
|
||||
any(ApplyExpr apply).getQualifier(), any(TupleElementExpr te).getSubExpr(),
|
||||
any(SubscriptExpr se).getBase()
|
||||
])
|
||||
} or
|
||||
TDictionarySubscriptNode(SubscriptExpr e) {
|
||||
e.getBase().getType().getCanonicalType() instanceof CanonicalDictionaryType
|
||||
}
|
||||
|
||||
private predicate localSsaFlowStepUseUse(Ssa::Definition def, Node nodeFrom, Node nodeTo) {
|
||||
@@ -332,6 +336,28 @@ import Cached
|
||||
/** Holds if `n` should be hidden from path explanations. */
|
||||
predicate nodeIsHidden(Node n) { n instanceof FlowSummaryNode }
|
||||
|
||||
/**
|
||||
* The intermediate node for a dictionary subscript operation `dict[key]`. In a write, this is used
|
||||
* as the destination of the `storeStep`s that add `TupleContent`s and the source of the storeStep
|
||||
* that adds `CollectionContent`. In a read, this is the destination of the `readStep` that pops
|
||||
* `CollectionContent` and the source of the `readStep` that pops `TupleContent[0]`
|
||||
*/
|
||||
private class DictionarySubscriptNode extends NodeImpl, TDictionarySubscriptNode {
|
||||
SubscriptExpr expr;
|
||||
|
||||
DictionarySubscriptNode() { this = TDictionarySubscriptNode(expr) }
|
||||
|
||||
override DataFlowCallable getEnclosingCallable() {
|
||||
result.asSourceCallable() = expr.getEnclosingCallable()
|
||||
}
|
||||
|
||||
override string toStringImpl() { result = "DictionarySubscriptNode" }
|
||||
|
||||
override Location getLocationImpl() { result = expr.getLocation() }
|
||||
|
||||
SubscriptExpr getExpr() { result = expr }
|
||||
}
|
||||
|
||||
private module ParameterNodes {
|
||||
abstract class ParameterNodeImpl extends NodeImpl {
|
||||
predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { none() }
|
||||
@@ -835,6 +861,32 @@ predicate storeStep(Node node1, ContentSet c, Node node2) {
|
||||
c instanceof OptionalSomeContentSet
|
||||
)
|
||||
or
|
||||
// assignment to a dictionary value via subscript operator, with intermediate step
|
||||
// `dict[key] = value`
|
||||
exists(AssignExpr assign, SubscriptExpr subscript |
|
||||
subscript = assign.getDest() and
|
||||
(
|
||||
subscript.getArgument(0).getExpr() = node1.asExpr() and
|
||||
node2.(DictionarySubscriptNode).getExpr() = subscript and
|
||||
c.isSingleton(any(Content::TupleContent tc | tc.getIndex() = 0))
|
||||
or
|
||||
assign.getSource() = node1.asExpr() and
|
||||
node2.(DictionarySubscriptNode).getExpr() = subscript and
|
||||
c.isSingleton(any(Content::TupleContent tc | tc.getIndex() = 1))
|
||||
or
|
||||
node1.(DictionarySubscriptNode).getExpr() = subscript and
|
||||
node2.(PostUpdateNode).getPreUpdateNode().asExpr() = subscript.getBase() and
|
||||
c.isSingleton(any(Content::CollectionContent cc))
|
||||
)
|
||||
)
|
||||
or
|
||||
// creation of a dictionary `[key: value, ...]`
|
||||
exists(DictionaryExpr dict |
|
||||
node1.asExpr() = dict.getAnElement() and
|
||||
node2.asExpr() = dict and
|
||||
c.isSingleton(any(Content::CollectionContent cc))
|
||||
)
|
||||
or
|
||||
FlowSummaryImpl::Private::Steps::summaryStoreStep(node1.(FlowSummaryNode).getSummaryNode(), c,
|
||||
node2.(FlowSummaryNode).getSummaryNode())
|
||||
}
|
||||
@@ -926,6 +978,17 @@ predicate readStep(Node node1, ContentSet c, Node node2) {
|
||||
)
|
||||
)
|
||||
or
|
||||
// read of a dictionary value via subscript operator
|
||||
exists(SubscriptExpr subscript |
|
||||
subscript.getBase() = node1.asExpr() and
|
||||
node2.(DictionarySubscriptNode).getExpr() = subscript and
|
||||
c.isSingleton(any(Content::CollectionContent cc))
|
||||
or
|
||||
subscript = node2.asExpr() and
|
||||
node1.(DictionarySubscriptNode).getExpr() = subscript and
|
||||
c.isSingleton(any(Content::TupleContent tc | tc.getIndex() = 1))
|
||||
)
|
||||
or
|
||||
FlowSummaryImpl::Private::Steps::summaryReadStep(node1.(FlowSummaryNode).getSummaryNode(), c,
|
||||
node2.(FlowSummaryNode).getSummaryNode())
|
||||
}
|
||||
@@ -936,7 +999,12 @@ predicate readStep(Node node1, ContentSet c, Node node2) {
|
||||
* in `x.f = newValue`.
|
||||
*/
|
||||
predicate clearsContent(Node n, ContentSet c) {
|
||||
n = any(PostUpdateNode pun | storeStep(_, c, pun)).getPreUpdateNode()
|
||||
n = any(PostUpdateNode pun | storeStep(_, c, pun)).getPreUpdateNode() and
|
||||
(
|
||||
c.isSingleton(any(Content::FieldContent fc)) or
|
||||
c.isSingleton(any(Content::TupleContent tc)) or
|
||||
c.isSingleton(any(Content::EnumContent ec))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,7 +9,7 @@ private import codeql.swift.dataflow.ExternalFlow
|
||||
* An instance of the `Array` type.
|
||||
*/
|
||||
class ArrayType extends Type {
|
||||
ArrayType() { this.getName().matches("Array<%") or this.getName().matches("[%]") }
|
||||
ArrayType() { this.getCanonicalType().getName().matches("Array<%") }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Provides models for the Swift `Dictionary` class.
|
||||
*/
|
||||
|
||||
import swift
|
||||
private import codeql.swift.dataflow.ExternalFlow
|
||||
|
||||
/**
|
||||
* An instance of the `Dictionary` type.
|
||||
*/
|
||||
class CanonicalDictionaryType extends BoundGenericType {
|
||||
CanonicalDictionaryType() { this.getName().matches("Dictionary<%") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A model for `Dictionary` and related class members that permit data flow.
|
||||
*/
|
||||
private class DictionarySummaries extends SummaryModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
";Dictionary;true;updateValue(_:forKey:);;;Argument[0];Argument[-1].CollectionElement.TupleElement[1];value",
|
||||
";Dictionary;true;updateValue(_:forKey:);;;Argument[1];Argument[-1].CollectionElement.TupleElement[0];value",
|
||||
";Dictionary;true;updateValue(_:forKey:);;;Argument[-1].CollectionElement.TupleElement[1];ReturnValue.OptionalSome;value"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -442,17 +442,60 @@ edges
|
||||
| test.swift:766:29:766:29 | KeyPathComponent [x] | test.swift:766:13:766:29 | exit #keyPath(...) |
|
||||
| test.swift:767:15:767:15 | s2 [s, some:0, x] | test.swift:766:13:766:29 | enter #keyPath(...) [s, some:0, x] |
|
||||
| test.swift:767:15:767:15 | s2 [s, some:0, x] | test.swift:767:15:767:28 | \\...[...] |
|
||||
| test.swift:773:11:773:17 | [post] exit #keyPath(...) | test.swift:773:17:773:17 | [post] KeyPathComponent [x] |
|
||||
| test.swift:773:15:773:15 | [post] KeyPathComponent [s, x] | test.swift:773:11:773:17 | [post] enter #keyPath(...) [s, x] |
|
||||
| test.swift:773:17:773:17 | [post] KeyPathComponent [x] | test.swift:773:15:773:15 | [post] KeyPathComponent [s, x] |
|
||||
| test.swift:774:3:774:3 | [post] s2 [s, x] | test.swift:775:13:775:13 | s2 [s, x] |
|
||||
| test.swift:774:3:774:16 | \\...[...] | test.swift:773:11:773:17 | [post] exit #keyPath(...) |
|
||||
| test.swift:774:3:774:16 | \\...[...] | test.swift:774:3:774:3 | [post] s2 [s, x] |
|
||||
| test.swift:774:20:774:27 | call to source() | test.swift:774:3:774:16 | \\...[...] |
|
||||
| test.swift:775:13:775:13 | s2 [s, x] | test.swift:632:7:632:7 | self [s, x] |
|
||||
| test.swift:775:13:775:13 | s2 [s, x] | test.swift:775:13:775:16 | .s [x] |
|
||||
| test.swift:775:13:775:16 | .s [x] | test.swift:615:7:615:7 | self [x] |
|
||||
| test.swift:775:13:775:16 | .s [x] | test.swift:775:13:775:18 | .x |
|
||||
| test.swift:774:5:774:5 | [post] dict1 [Collection element, Tuple element at index 1] | test.swift:776:15:776:15 | dict1 [Collection element, Tuple element at index 1] |
|
||||
| test.swift:774:5:774:12 | DictionarySubscriptNode [Tuple element at index 1] | test.swift:774:5:774:5 | [post] dict1 [Collection element, Tuple element at index 1] |
|
||||
| test.swift:774:16:774:23 | call to source() | test.swift:774:5:774:12 | DictionarySubscriptNode [Tuple element at index 1] |
|
||||
| test.swift:776:15:776:15 | dict1 [Collection element, Tuple element at index 1] | test.swift:776:15:776:22 | DictionarySubscriptNode [Tuple element at index 1] |
|
||||
| test.swift:776:15:776:22 | DictionarySubscriptNode [Tuple element at index 1] | test.swift:776:15:776:22 | ...[...] |
|
||||
| test.swift:786:17:786:29 | [...] [Collection element, Tuple element at index 1] | test.swift:787:15:787:15 | dict3 [Collection element, Tuple element at index 1] |
|
||||
| test.swift:786:17:786:29 | [...] [Collection element, Tuple element at index 1] | test.swift:789:5:789:5 | dict3 [Collection element, Tuple element at index 1] |
|
||||
| test.swift:786:17:786:29 | [...] [Collection element, Tuple element at index 1] | test.swift:792:15:792:15 | dict3 [Collection element, Tuple element at index 1] |
|
||||
| test.swift:786:18:786:28 | (...) [Tuple element at index 1] | test.swift:786:17:786:29 | [...] [Collection element, Tuple element at index 1] |
|
||||
| test.swift:786:21:786:28 | call to source() | test.swift:786:18:786:28 | (...) [Tuple element at index 1] |
|
||||
| test.swift:787:15:787:15 | dict3 [Collection element, Tuple element at index 1] | test.swift:787:15:787:22 | DictionarySubscriptNode [Tuple element at index 1] |
|
||||
| test.swift:787:15:787:22 | DictionarySubscriptNode [Tuple element at index 1] | test.swift:787:15:787:22 | ...[...] |
|
||||
| test.swift:789:5:789:5 | [post] dict3 [Collection element, Tuple element at index 0] | test.swift:791:15:791:15 | dict3 [Collection element, Tuple element at index 0] |
|
||||
| test.swift:789:5:789:5 | [post] dict3 [Collection element, Tuple element at index 1] | test.swift:792:15:792:15 | dict3 [Collection element, Tuple element at index 1] |
|
||||
| test.swift:789:5:789:5 | dict3 [Collection element, Tuple element at index 1] | test.swift:789:5:789:19 | DictionarySubscriptNode [Tuple element at index 1] |
|
||||
| test.swift:789:5:789:19 | DictionarySubscriptNode [Tuple element at index 0] | test.swift:789:5:789:5 | [post] dict3 [Collection element, Tuple element at index 0] |
|
||||
| test.swift:789:5:789:19 | DictionarySubscriptNode [Tuple element at index 1] | test.swift:789:5:789:5 | [post] dict3 [Collection element, Tuple element at index 1] |
|
||||
| test.swift:789:11:789:18 | call to source() | test.swift:789:5:789:19 | DictionarySubscriptNode [Tuple element at index 0] |
|
||||
| test.swift:791:15:791:15 | dict3 [Collection element, Tuple element at index 0] | test.swift:791:15:791:35 | call to randomElement() [some:0, Tuple element at index 0] |
|
||||
| test.swift:791:15:791:35 | call to randomElement() [some:0, Tuple element at index 0] | test.swift:791:15:791:36 | ...! [Tuple element at index 0] |
|
||||
| test.swift:791:15:791:36 | ...! [Tuple element at index 0] | test.swift:791:15:791:38 | .0 |
|
||||
| test.swift:792:15:792:15 | dict3 [Collection element, Tuple element at index 1] | test.swift:792:15:792:35 | call to randomElement() [some:0, Tuple element at index 1] |
|
||||
| test.swift:792:15:792:35 | call to randomElement() [some:0, Tuple element at index 1] | test.swift:792:15:792:36 | ...! [Tuple element at index 1] |
|
||||
| test.swift:792:15:792:36 | ...! [Tuple element at index 1] | test.swift:792:15:792:38 | .1 |
|
||||
| test.swift:799:17:799:28 | [...] [Collection element, Tuple element at index 1] | test.swift:800:15:800:15 | dict4 [Collection element, Tuple element at index 1] |
|
||||
| test.swift:799:17:799:28 | [...] [Collection element, Tuple element at index 1] | test.swift:801:15:801:15 | dict4 [Collection element, Tuple element at index 1] |
|
||||
| test.swift:799:17:799:28 | [...] [Collection element, Tuple element at index 1] | test.swift:803:15:803:15 | dict4 [Collection element, Tuple element at index 1] |
|
||||
| test.swift:799:18:799:27 | (...) [Tuple element at index 1] | test.swift:799:17:799:28 | [...] [Collection element, Tuple element at index 1] |
|
||||
| test.swift:799:20:799:27 | call to source() | test.swift:799:18:799:27 | (...) [Tuple element at index 1] |
|
||||
| test.swift:800:15:800:15 | [post] dict4 [Collection element, Tuple element at index 0] | test.swift:802:15:802:15 | dict4 [Collection element, Tuple element at index 0] |
|
||||
| test.swift:800:15:800:15 | dict4 [Collection element, Tuple element at index 1] | test.swift:800:15:800:52 | call to updateValue(_:forKey:) [some:0] |
|
||||
| test.swift:800:15:800:52 | call to updateValue(_:forKey:) [some:0] | test.swift:800:15:800:53 | ...! |
|
||||
| test.swift:800:44:800:51 | call to source() | test.swift:800:15:800:15 | [post] dict4 [Collection element, Tuple element at index 0] |
|
||||
| test.swift:801:15:801:15 | [post] dict4 [Collection element, Tuple element at index 1] | test.swift:803:15:803:15 | dict4 [Collection element, Tuple element at index 1] |
|
||||
| test.swift:801:15:801:15 | dict4 [Collection element, Tuple element at index 1] | test.swift:801:15:801:52 | call to updateValue(_:forKey:) [some:0] |
|
||||
| test.swift:801:15:801:52 | call to updateValue(_:forKey:) [some:0] | test.swift:801:15:801:53 | ...! |
|
||||
| test.swift:801:33:801:40 | call to source() | test.swift:801:15:801:15 | [post] dict4 [Collection element, Tuple element at index 1] |
|
||||
| test.swift:802:15:802:15 | dict4 [Collection element, Tuple element at index 0] | test.swift:802:15:802:35 | call to randomElement() [some:0, Tuple element at index 0] |
|
||||
| test.swift:802:15:802:35 | call to randomElement() [some:0, Tuple element at index 0] | test.swift:802:15:802:36 | ...! [Tuple element at index 0] |
|
||||
| test.swift:802:15:802:36 | ...! [Tuple element at index 0] | test.swift:802:15:802:38 | .0 |
|
||||
| test.swift:803:15:803:15 | dict4 [Collection element, Tuple element at index 1] | test.swift:803:15:803:35 | call to randomElement() [some:0, Tuple element at index 1] |
|
||||
| test.swift:803:15:803:35 | call to randomElement() [some:0, Tuple element at index 1] | test.swift:803:15:803:36 | ...! [Tuple element at index 1] |
|
||||
| test.swift:803:15:803:36 | ...! [Tuple element at index 1] | test.swift:803:15:803:38 | .1 |
|
||||
| test.swift:811:11:811:17 | [post] exit #keyPath(...) | test.swift:811:17:811:17 | [post] KeyPathComponent [x] |
|
||||
| test.swift:811:15:811:15 | [post] KeyPathComponent [s, x] | test.swift:811:11:811:17 | [post] enter #keyPath(...) [s, x] |
|
||||
| test.swift:811:17:811:17 | [post] KeyPathComponent [x] | test.swift:811:15:811:15 | [post] KeyPathComponent [s, x] |
|
||||
| test.swift:812:3:812:3 | [post] s2 [s, x] | test.swift:813:13:813:13 | s2 [s, x] |
|
||||
| test.swift:812:3:812:16 | \\...[...] | test.swift:811:11:811:17 | [post] exit #keyPath(...) |
|
||||
| test.swift:812:3:812:16 | \\...[...] | test.swift:812:3:812:3 | [post] s2 [s, x] |
|
||||
| test.swift:812:20:812:27 | call to source() | test.swift:812:3:812:16 | \\...[...] |
|
||||
| test.swift:813:13:813:13 | s2 [s, x] | test.swift:632:7:632:7 | self [s, x] |
|
||||
| test.swift:813:13:813:13 | s2 [s, x] | test.swift:813:13:813:16 | .s [x] |
|
||||
| test.swift:813:13:813:16 | .s [x] | test.swift:615:7:615:7 | self [x] |
|
||||
| test.swift:813:13:813:16 | .s [x] | test.swift:813:13:813:18 | .x |
|
||||
nodes
|
||||
| file://:0:0:0:0 | .a [x] | semmle.label | .a [x] |
|
||||
| file://:0:0:0:0 | .s [x] | semmle.label | .s [x] |
|
||||
@@ -936,16 +979,63 @@ nodes
|
||||
| test.swift:766:29:766:29 | KeyPathComponent [x] | semmle.label | KeyPathComponent [x] |
|
||||
| test.swift:767:15:767:15 | s2 [s, some:0, x] | semmle.label | s2 [s, some:0, x] |
|
||||
| test.swift:767:15:767:28 | \\...[...] | semmle.label | \\...[...] |
|
||||
| test.swift:773:11:773:17 | [post] enter #keyPath(...) [s, x] | semmle.label | [post] enter #keyPath(...) [s, x] |
|
||||
| test.swift:773:11:773:17 | [post] exit #keyPath(...) | semmle.label | [post] exit #keyPath(...) |
|
||||
| test.swift:773:15:773:15 | [post] KeyPathComponent [s, x] | semmle.label | [post] KeyPathComponent [s, x] |
|
||||
| test.swift:773:17:773:17 | [post] KeyPathComponent [x] | semmle.label | [post] KeyPathComponent [x] |
|
||||
| test.swift:774:3:774:3 | [post] s2 [s, x] | semmle.label | [post] s2 [s, x] |
|
||||
| test.swift:774:3:774:16 | \\...[...] | semmle.label | \\...[...] |
|
||||
| test.swift:774:20:774:27 | call to source() | semmle.label | call to source() |
|
||||
| test.swift:775:13:775:13 | s2 [s, x] | semmle.label | s2 [s, x] |
|
||||
| test.swift:775:13:775:16 | .s [x] | semmle.label | .s [x] |
|
||||
| test.swift:775:13:775:18 | .x | semmle.label | .x |
|
||||
| test.swift:774:5:774:5 | [post] dict1 [Collection element, Tuple element at index 1] | semmle.label | [post] dict1 [Collection element, Tuple element at index 1] |
|
||||
| test.swift:774:5:774:12 | DictionarySubscriptNode [Tuple element at index 1] | semmle.label | DictionarySubscriptNode [Tuple element at index 1] |
|
||||
| test.swift:774:16:774:23 | call to source() | semmle.label | call to source() |
|
||||
| test.swift:776:15:776:15 | dict1 [Collection element, Tuple element at index 1] | semmle.label | dict1 [Collection element, Tuple element at index 1] |
|
||||
| test.swift:776:15:776:22 | ...[...] | semmle.label | ...[...] |
|
||||
| test.swift:776:15:776:22 | DictionarySubscriptNode [Tuple element at index 1] | semmle.label | DictionarySubscriptNode [Tuple element at index 1] |
|
||||
| test.swift:786:17:786:29 | [...] [Collection element, Tuple element at index 1] | semmle.label | [...] [Collection element, Tuple element at index 1] |
|
||||
| test.swift:786:18:786:28 | (...) [Tuple element at index 1] | semmle.label | (...) [Tuple element at index 1] |
|
||||
| test.swift:786:21:786:28 | call to source() | semmle.label | call to source() |
|
||||
| test.swift:787:15:787:15 | dict3 [Collection element, Tuple element at index 1] | semmle.label | dict3 [Collection element, Tuple element at index 1] |
|
||||
| test.swift:787:15:787:22 | ...[...] | semmle.label | ...[...] |
|
||||
| test.swift:787:15:787:22 | DictionarySubscriptNode [Tuple element at index 1] | semmle.label | DictionarySubscriptNode [Tuple element at index 1] |
|
||||
| test.swift:789:5:789:5 | [post] dict3 [Collection element, Tuple element at index 0] | semmle.label | [post] dict3 [Collection element, Tuple element at index 0] |
|
||||
| test.swift:789:5:789:5 | [post] dict3 [Collection element, Tuple element at index 1] | semmle.label | [post] dict3 [Collection element, Tuple element at index 1] |
|
||||
| test.swift:789:5:789:5 | dict3 [Collection element, Tuple element at index 1] | semmle.label | dict3 [Collection element, Tuple element at index 1] |
|
||||
| test.swift:789:5:789:19 | DictionarySubscriptNode [Tuple element at index 0] | semmle.label | DictionarySubscriptNode [Tuple element at index 0] |
|
||||
| test.swift:789:5:789:19 | DictionarySubscriptNode [Tuple element at index 1] | semmle.label | DictionarySubscriptNode [Tuple element at index 1] |
|
||||
| test.swift:789:11:789:18 | call to source() | semmle.label | call to source() |
|
||||
| test.swift:791:15:791:15 | dict3 [Collection element, Tuple element at index 0] | semmle.label | dict3 [Collection element, Tuple element at index 0] |
|
||||
| test.swift:791:15:791:35 | call to randomElement() [some:0, Tuple element at index 0] | semmle.label | call to randomElement() [some:0, Tuple element at index 0] |
|
||||
| test.swift:791:15:791:36 | ...! [Tuple element at index 0] | semmle.label | ...! [Tuple element at index 0] |
|
||||
| test.swift:791:15:791:38 | .0 | semmle.label | .0 |
|
||||
| test.swift:792:15:792:15 | dict3 [Collection element, Tuple element at index 1] | semmle.label | dict3 [Collection element, Tuple element at index 1] |
|
||||
| test.swift:792:15:792:35 | call to randomElement() [some:0, Tuple element at index 1] | semmle.label | call to randomElement() [some:0, Tuple element at index 1] |
|
||||
| test.swift:792:15:792:36 | ...! [Tuple element at index 1] | semmle.label | ...! [Tuple element at index 1] |
|
||||
| test.swift:792:15:792:38 | .1 | semmle.label | .1 |
|
||||
| test.swift:799:17:799:28 | [...] [Collection element, Tuple element at index 1] | semmle.label | [...] [Collection element, Tuple element at index 1] |
|
||||
| test.swift:799:18:799:27 | (...) [Tuple element at index 1] | semmle.label | (...) [Tuple element at index 1] |
|
||||
| test.swift:799:20:799:27 | call to source() | semmle.label | call to source() |
|
||||
| test.swift:800:15:800:15 | [post] dict4 [Collection element, Tuple element at index 0] | semmle.label | [post] dict4 [Collection element, Tuple element at index 0] |
|
||||
| test.swift:800:15:800:15 | dict4 [Collection element, Tuple element at index 1] | semmle.label | dict4 [Collection element, Tuple element at index 1] |
|
||||
| test.swift:800:15:800:52 | call to updateValue(_:forKey:) [some:0] | semmle.label | call to updateValue(_:forKey:) [some:0] |
|
||||
| test.swift:800:15:800:53 | ...! | semmle.label | ...! |
|
||||
| test.swift:800:44:800:51 | call to source() | semmle.label | call to source() |
|
||||
| test.swift:801:15:801:15 | [post] dict4 [Collection element, Tuple element at index 1] | semmle.label | [post] dict4 [Collection element, Tuple element at index 1] |
|
||||
| test.swift:801:15:801:15 | dict4 [Collection element, Tuple element at index 1] | semmle.label | dict4 [Collection element, Tuple element at index 1] |
|
||||
| test.swift:801:15:801:52 | call to updateValue(_:forKey:) [some:0] | semmle.label | call to updateValue(_:forKey:) [some:0] |
|
||||
| test.swift:801:15:801:53 | ...! | semmle.label | ...! |
|
||||
| test.swift:801:33:801:40 | call to source() | semmle.label | call to source() |
|
||||
| test.swift:802:15:802:15 | dict4 [Collection element, Tuple element at index 0] | semmle.label | dict4 [Collection element, Tuple element at index 0] |
|
||||
| test.swift:802:15:802:35 | call to randomElement() [some:0, Tuple element at index 0] | semmle.label | call to randomElement() [some:0, Tuple element at index 0] |
|
||||
| test.swift:802:15:802:36 | ...! [Tuple element at index 0] | semmle.label | ...! [Tuple element at index 0] |
|
||||
| test.swift:802:15:802:38 | .0 | semmle.label | .0 |
|
||||
| test.swift:803:15:803:15 | dict4 [Collection element, Tuple element at index 1] | semmle.label | dict4 [Collection element, Tuple element at index 1] |
|
||||
| test.swift:803:15:803:35 | call to randomElement() [some:0, Tuple element at index 1] | semmle.label | call to randomElement() [some:0, Tuple element at index 1] |
|
||||
| test.swift:803:15:803:36 | ...! [Tuple element at index 1] | semmle.label | ...! [Tuple element at index 1] |
|
||||
| test.swift:803:15:803:38 | .1 | semmle.label | .1 |
|
||||
| test.swift:811:11:811:17 | [post] enter #keyPath(...) [s, x] | semmle.label | [post] enter #keyPath(...) [s, x] |
|
||||
| test.swift:811:11:811:17 | [post] exit #keyPath(...) | semmle.label | [post] exit #keyPath(...) |
|
||||
| test.swift:811:15:811:15 | [post] KeyPathComponent [s, x] | semmle.label | [post] KeyPathComponent [s, x] |
|
||||
| test.swift:811:17:811:17 | [post] KeyPathComponent [x] | semmle.label | [post] KeyPathComponent [x] |
|
||||
| test.swift:812:3:812:3 | [post] s2 [s, x] | semmle.label | [post] s2 [s, x] |
|
||||
| test.swift:812:3:812:16 | \\...[...] | semmle.label | \\...[...] |
|
||||
| test.swift:812:20:812:27 | call to source() | semmle.label | call to source() |
|
||||
| test.swift:813:13:813:13 | s2 [s, x] | semmle.label | s2 [s, x] |
|
||||
| test.swift:813:13:813:16 | .s [x] | semmle.label | .s [x] |
|
||||
| test.swift:813:13:813:18 | .x | semmle.label | .x |
|
||||
subpaths
|
||||
| test.swift:75:22:75:22 | x | test.swift:65:16:65:28 | arg1 | test.swift:65:1:70:1 | arg2[return] | test.swift:75:32:75:32 | [post] y |
|
||||
| test.swift:114:19:114:19 | arg | test.swift:109:9:109:14 | arg | test.swift:110:12:110:12 | arg | test.swift:114:12:114:22 | call to ... |
|
||||
@@ -1000,10 +1090,10 @@ subpaths
|
||||
| test.swift:764:18:764:25 | call to source() | test.swift:617:8:617:11 | x | test.swift:617:3:619:3 | self[return] [x] | test.swift:764:13:764:26 | call to S.init(x:) [x] |
|
||||
| test.swift:765:29:765:29 | s [some:0, x] | test.swift:655:8:655:12 | s [some:0, x] | test.swift:655:3:657:3 | self[return] [s, some:0, x] | test.swift:765:14:765:30 | call to S2_Optional.init(s:) [s, some:0, x] |
|
||||
| test.swift:767:15:767:15 | s2 [s, some:0, x] | test.swift:766:13:766:29 | enter #keyPath(...) [s, some:0, x] | test.swift:766:13:766:29 | exit #keyPath(...) | test.swift:767:15:767:28 | \\...[...] |
|
||||
| test.swift:774:3:774:16 | \\...[...] | test.swift:773:11:773:17 | [post] exit #keyPath(...) | test.swift:773:11:773:17 | [post] enter #keyPath(...) [s, x] | test.swift:774:3:774:3 | [post] s2 [s, x] |
|
||||
| test.swift:774:3:774:16 | \\...[...] | test.swift:773:11:773:17 | [post] exit #keyPath(...) | test.swift:773:15:773:15 | [post] KeyPathComponent [s, x] | test.swift:774:3:774:3 | [post] s2 [s, x] |
|
||||
| test.swift:775:13:775:13 | s2 [s, x] | test.swift:632:7:632:7 | self [s, x] | file://:0:0:0:0 | .s [x] | test.swift:775:13:775:16 | .s [x] |
|
||||
| test.swift:775:13:775:16 | .s [x] | test.swift:615:7:615:7 | self [x] | file://:0:0:0:0 | .x | test.swift:775:13:775:18 | .x |
|
||||
| test.swift:812:3:812:16 | \\...[...] | test.swift:811:11:811:17 | [post] exit #keyPath(...) | test.swift:811:11:811:17 | [post] enter #keyPath(...) [s, x] | test.swift:812:3:812:3 | [post] s2 [s, x] |
|
||||
| test.swift:812:3:812:16 | \\...[...] | test.swift:811:11:811:17 | [post] exit #keyPath(...) | test.swift:811:15:811:15 | [post] KeyPathComponent [s, x] | test.swift:812:3:812:3 | [post] s2 [s, x] |
|
||||
| test.swift:813:13:813:13 | s2 [s, x] | test.swift:632:7:632:7 | self [s, x] | file://:0:0:0:0 | .s [x] | test.swift:813:13:813:16 | .s [x] |
|
||||
| test.swift:813:13:813:16 | .s [x] | test.swift:615:7:615:7 | self [x] | file://:0:0:0:0 | .x | test.swift:813:13:813:18 | .x |
|
||||
#select
|
||||
| test.swift:7:15:7:15 | t1 | test.swift:6:19:6:26 | call to source() | test.swift:7:15:7:15 | t1 | result |
|
||||
| test.swift:9:15:9:15 | t1 | test.swift:6:19:6:26 | call to source() | test.swift:9:15:9:15 | t1 | result |
|
||||
@@ -1105,4 +1195,13 @@ subpaths
|
||||
| test.swift:756:15:756:21 | ...! | test.swift:746:14:746:21 | call to source() | test.swift:756:15:756:21 | ...! | result |
|
||||
| test.swift:757:15:757:19 | .v3 | test.swift:747:14:747:21 | call to source() | test.swift:757:15:757:19 | .v3 | result |
|
||||
| test.swift:767:15:767:28 | \\...[...] | test.swift:764:18:764:25 | call to source() | test.swift:767:15:767:28 | \\...[...] | result |
|
||||
| test.swift:775:13:775:18 | .x | test.swift:774:20:774:27 | call to source() | test.swift:775:13:775:18 | .x | result |
|
||||
| test.swift:776:15:776:22 | ...[...] | test.swift:774:16:774:23 | call to source() | test.swift:776:15:776:22 | ...[...] | result |
|
||||
| test.swift:787:15:787:22 | ...[...] | test.swift:786:21:786:28 | call to source() | test.swift:787:15:787:22 | ...[...] | result |
|
||||
| test.swift:791:15:791:38 | .0 | test.swift:789:11:789:18 | call to source() | test.swift:791:15:791:38 | .0 | result |
|
||||
| test.swift:792:15:792:38 | .1 | test.swift:786:21:786:28 | call to source() | test.swift:792:15:792:38 | .1 | result |
|
||||
| test.swift:800:15:800:53 | ...! | test.swift:799:20:799:27 | call to source() | test.swift:800:15:800:53 | ...! | result |
|
||||
| test.swift:801:15:801:53 | ...! | test.swift:799:20:799:27 | call to source() | test.swift:801:15:801:53 | ...! | result |
|
||||
| test.swift:802:15:802:38 | .0 | test.swift:800:44:800:51 | call to source() | test.swift:802:15:802:38 | .0 | result |
|
||||
| test.swift:803:15:803:38 | .1 | test.swift:799:20:799:27 | call to source() | test.swift:803:15:803:38 | .1 | result |
|
||||
| test.swift:803:15:803:38 | .1 | test.swift:801:33:801:40 | call to source() | test.swift:803:15:803:38 | .1 | result |
|
||||
| test.swift:813:13:813:18 | .x | test.swift:812:20:812:27 | call to source() | test.swift:813:13:813:18 | .x | result |
|
||||
|
||||
@@ -937,16 +937,75 @@
|
||||
| test.swift:766:13:766:29 | #keyPath(...) | test.swift:766:9:766:9 | f |
|
||||
| test.swift:766:13:766:29 | enter #keyPath(...) | test.swift:766:26:766:26 | KeyPathComponent |
|
||||
| test.swift:766:26:766:26 | [post] KeyPathComponent | test.swift:766:13:766:29 | [post] enter #keyPath(...) |
|
||||
| test.swift:771:7:771:7 | SSA def(s2) | test.swift:772:13:772:13 | s2 |
|
||||
| test.swift:771:7:771:7 | s2 | test.swift:771:7:771:7 | SSA def(s2) |
|
||||
| test.swift:771:12:771:25 | call to S2.init(s:) | test.swift:771:7:771:7 | s2 |
|
||||
| test.swift:772:13:772:13 | [post] s2 | test.swift:774:3:774:3 | s2 |
|
||||
| test.swift:772:13:772:13 | s2 | test.swift:774:3:774:3 | s2 |
|
||||
| test.swift:773:7:773:7 | SSA def(f) | test.swift:774:15:774:15 | f |
|
||||
| test.swift:773:7:773:7 | f | test.swift:773:7:773:7 | SSA def(f) |
|
||||
| test.swift:773:11:773:17 | #keyPath(...) | test.swift:773:7:773:7 | f |
|
||||
| test.swift:773:11:773:17 | enter #keyPath(...) | test.swift:773:15:773:15 | KeyPathComponent |
|
||||
| test.swift:773:15:773:15 | [post] KeyPathComponent | test.swift:773:11:773:17 | [post] enter #keyPath(...) |
|
||||
| test.swift:774:3:774:3 | [post] s2 | test.swift:775:13:775:13 | s2 |
|
||||
| test.swift:774:3:774:3 | s2 | test.swift:775:13:775:13 | s2 |
|
||||
| test.swift:774:20:774:27 | call to source() | test.swift:774:3:774:16 | \\...[...] |
|
||||
| test.swift:771:9:771:9 | SSA def(dict1) | test.swift:772:15:772:15 | dict1 |
|
||||
| test.swift:771:9:771:9 | dict1 | test.swift:771:9:771:9 | SSA def(dict1) |
|
||||
| test.swift:771:17:771:31 | [...] | test.swift:771:9:771:9 | dict1 |
|
||||
| test.swift:772:15:772:15 | &... | test.swift:774:5:774:5 | dict1 |
|
||||
| test.swift:772:15:772:15 | [post] dict1 | test.swift:772:15:772:15 | &... |
|
||||
| test.swift:772:15:772:15 | dict1 | test.swift:772:15:772:15 | &... |
|
||||
| test.swift:774:5:774:5 | &... | test.swift:776:15:776:15 | dict1 |
|
||||
| test.swift:774:5:774:5 | [post] dict1 | test.swift:774:5:774:5 | &... |
|
||||
| test.swift:774:5:774:5 | dict1 | test.swift:774:5:774:5 | &... |
|
||||
| test.swift:776:15:776:15 | [post] dict1 | test.swift:776:15:776:15 | &... |
|
||||
| test.swift:776:15:776:15 | dict1 | test.swift:776:15:776:15 | &... |
|
||||
| test.swift:778:9:778:9 | SSA def(dict2) | test.swift:779:15:779:15 | dict2 |
|
||||
| test.swift:778:9:778:9 | dict2 | test.swift:778:9:778:9 | SSA def(dict2) |
|
||||
| test.swift:778:17:778:29 | [...] | test.swift:778:9:778:9 | dict2 |
|
||||
| test.swift:779:15:779:15 | &... | test.swift:781:25:781:25 | dict2 |
|
||||
| test.swift:779:15:779:15 | [post] dict2 | test.swift:779:15:779:15 | &... |
|
||||
| test.swift:779:15:779:15 | dict2 | test.swift:779:15:779:15 | &... |
|
||||
| test.swift:781:10:781:10 | SSA def(key) | test.swift:782:19:782:19 | key |
|
||||
| test.swift:781:10:781:10 | key | test.swift:781:10:781:10 | SSA def(key) |
|
||||
| test.swift:781:15:781:15 | SSA def(value) | test.swift:783:19:783:19 | value |
|
||||
| test.swift:781:15:781:15 | value | test.swift:781:15:781:15 | SSA def(value) |
|
||||
| test.swift:786:9:786:9 | SSA def(dict3) | test.swift:787:15:787:15 | dict3 |
|
||||
| test.swift:786:9:786:9 | dict3 | test.swift:786:9:786:9 | SSA def(dict3) |
|
||||
| test.swift:786:17:786:29 | [...] | test.swift:786:9:786:9 | dict3 |
|
||||
| test.swift:787:15:787:15 | &... | test.swift:789:5:789:5 | dict3 |
|
||||
| test.swift:787:15:787:15 | [post] dict3 | test.swift:787:15:787:15 | &... |
|
||||
| test.swift:787:15:787:15 | dict3 | test.swift:787:15:787:15 | &... |
|
||||
| test.swift:789:5:789:5 | &... | test.swift:791:15:791:15 | dict3 |
|
||||
| test.swift:789:5:789:5 | [post] dict3 | test.swift:789:5:789:5 | &... |
|
||||
| test.swift:789:5:789:5 | dict3 | test.swift:789:5:789:5 | &... |
|
||||
| test.swift:791:15:791:15 | [post] dict3 | test.swift:792:15:792:15 | dict3 |
|
||||
| test.swift:791:15:791:15 | dict3 | test.swift:792:15:792:15 | dict3 |
|
||||
| test.swift:791:15:791:35 | call to randomElement() | test.swift:791:15:791:36 | ...! |
|
||||
| test.swift:792:15:792:15 | [post] dict3 | test.swift:794:25:794:25 | dict3 |
|
||||
| test.swift:792:15:792:15 | dict3 | test.swift:794:25:794:25 | dict3 |
|
||||
| test.swift:792:15:792:35 | call to randomElement() | test.swift:792:15:792:36 | ...! |
|
||||
| test.swift:794:10:794:10 | SSA def(key) | test.swift:795:19:795:19 | key |
|
||||
| test.swift:794:10:794:10 | key | test.swift:794:10:794:10 | SSA def(key) |
|
||||
| test.swift:794:15:794:15 | SSA def(value) | test.swift:796:19:796:19 | value |
|
||||
| test.swift:794:15:794:15 | value | test.swift:794:15:794:15 | SSA def(value) |
|
||||
| test.swift:799:9:799:9 | SSA def(dict4) | test.swift:800:15:800:15 | dict4 |
|
||||
| test.swift:799:9:799:9 | dict4 | test.swift:799:9:799:9 | SSA def(dict4) |
|
||||
| test.swift:799:17:799:28 | [...] | test.swift:799:9:799:9 | dict4 |
|
||||
| test.swift:800:15:800:15 | &... | test.swift:801:15:801:15 | dict4 |
|
||||
| test.swift:800:15:800:15 | [post] dict4 | test.swift:800:15:800:15 | &... |
|
||||
| test.swift:800:15:800:15 | dict4 | test.swift:800:15:800:15 | &... |
|
||||
| test.swift:800:15:800:52 | call to updateValue(_:forKey:) | test.swift:800:15:800:53 | ...! |
|
||||
| test.swift:801:15:801:15 | &... | test.swift:802:15:802:15 | dict4 |
|
||||
| test.swift:801:15:801:15 | [post] dict4 | test.swift:801:15:801:15 | &... |
|
||||
| test.swift:801:15:801:15 | dict4 | test.swift:801:15:801:15 | &... |
|
||||
| test.swift:801:15:801:52 | call to updateValue(_:forKey:) | test.swift:801:15:801:53 | ...! |
|
||||
| test.swift:802:15:802:15 | [post] dict4 | test.swift:803:15:803:15 | dict4 |
|
||||
| test.swift:802:15:802:15 | dict4 | test.swift:803:15:803:15 | dict4 |
|
||||
| test.swift:802:15:802:35 | call to randomElement() | test.swift:802:15:802:36 | ...! |
|
||||
| test.swift:803:15:803:15 | [post] dict4 | test.swift:804:15:804:15 | dict4 |
|
||||
| test.swift:803:15:803:15 | dict4 | test.swift:804:15:804:15 | dict4 |
|
||||
| test.swift:803:15:803:35 | call to randomElement() | test.swift:803:15:803:36 | ...! |
|
||||
| test.swift:804:15:804:15 | [post] dict4 | test.swift:805:15:805:15 | dict4 |
|
||||
| test.swift:804:15:804:15 | dict4 | test.swift:805:15:805:15 | dict4 |
|
||||
| test.swift:809:7:809:7 | SSA def(s2) | test.swift:810:13:810:13 | s2 |
|
||||
| test.swift:809:7:809:7 | s2 | test.swift:809:7:809:7 | SSA def(s2) |
|
||||
| test.swift:809:12:809:25 | call to S2.init(s:) | test.swift:809:7:809:7 | s2 |
|
||||
| test.swift:810:13:810:13 | [post] s2 | test.swift:812:3:812:3 | s2 |
|
||||
| test.swift:810:13:810:13 | s2 | test.swift:812:3:812:3 | s2 |
|
||||
| test.swift:811:7:811:7 | SSA def(f) | test.swift:812:15:812:15 | f |
|
||||
| test.swift:811:7:811:7 | f | test.swift:811:7:811:7 | SSA def(f) |
|
||||
| test.swift:811:11:811:17 | #keyPath(...) | test.swift:811:7:811:7 | f |
|
||||
| test.swift:811:11:811:17 | enter #keyPath(...) | test.swift:811:15:811:15 | KeyPathComponent |
|
||||
| test.swift:811:15:811:15 | [post] KeyPathComponent | test.swift:811:11:811:17 | [post] enter #keyPath(...) |
|
||||
| test.swift:812:3:812:3 | [post] s2 | test.swift:813:13:813:13 | s2 |
|
||||
| test.swift:812:3:812:3 | s2 | test.swift:813:13:813:13 | s2 |
|
||||
| test.swift:812:20:812:27 | call to source() | test.swift:812:3:812:16 | \\...[...] |
|
||||
|
||||
@@ -767,10 +767,48 @@ func testOptionalKeyPathForce() {
|
||||
sink(arg: s2[keyPath: f]) // $ flow=764
|
||||
}
|
||||
|
||||
func testDictionary() {
|
||||
var dict1 = [1:2, 3:4, 5:6]
|
||||
sink(arg: dict1[1])
|
||||
|
||||
dict1[1] = source()
|
||||
|
||||
sink(arg: dict1[1]) // $ flow=774
|
||||
|
||||
var dict2 = [source(): 1]
|
||||
sink(arg: dict2[1])
|
||||
|
||||
for (key, value) in dict2 {
|
||||
sink(arg: key) // $ MISSING: flow=778
|
||||
sink(arg: value)
|
||||
}
|
||||
|
||||
var dict3 = [1: source()]
|
||||
sink(arg: dict3[1]) // $ flow=786
|
||||
|
||||
dict3[source()] = 2
|
||||
|
||||
sink(arg: dict3.randomElement()!.0) // $ flow=789
|
||||
sink(arg: dict3.randomElement()!.1) // $ flow=786
|
||||
|
||||
for (key, value) in dict3 {
|
||||
sink(arg: key) // $ MISSING: flow=789
|
||||
sink(arg: value) // $ MISSING: flow=786
|
||||
}
|
||||
|
||||
var dict4 = [1:source()]
|
||||
sink(arg: dict4.updateValue(1, forKey: source())!) // $ flow=799
|
||||
sink(arg: dict4.updateValue(source(), forKey: 2)!) // $ SPURIOUS: flow=799
|
||||
sink(arg: dict4.randomElement()!.0) // $ flow=800
|
||||
sink(arg: dict4.randomElement()!.1) // $ flow=799 flow=801
|
||||
sink(arg: dict4.keys.randomElement()) // $ MISSING: flow=800
|
||||
sink(arg: dict4.values.randomElement()) // $ MISSING: flow=799 flow=801
|
||||
}
|
||||
|
||||
func testNestedKeyPathWrite() {
|
||||
var s2 = S2(s: S(x: 1))
|
||||
sink(arg: s2.s.x)
|
||||
var f = \S2.s.x
|
||||
s2[keyPath: f] = source()
|
||||
sink(arg: s2.s.x) // $ flow=774
|
||||
sink(arg: s2.s.x) // $ flow=812
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ func taintCollections(array: inout Array<Int>, contiguousArray: inout Contiguous
|
||||
|
||||
dictionary[0] = source2()
|
||||
sink(arg: dictionary)
|
||||
sink(arg: dictionary[0]!) // $ MISSING: tainted=162
|
||||
sink(arg: dictionary[0]!) // $ tainted=162
|
||||
dictionary.withContiguousStorageIfAvailable({
|
||||
buffer in
|
||||
sink(arg: buffer)
|
||||
|
||||
Reference in New Issue
Block a user