mirror of
https://github.com/github/codeql.git
synced 2026-07-07 04:25:30 +02:00
Compare commits
53 Commits
copilot/ad
...
tausbn/swi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
676c552fb0 | ||
|
|
c3a0b65c0c | ||
|
|
268e9eadac | ||
|
|
ab1bc853fc | ||
|
|
f4d8358454 | ||
|
|
0a02b16c43 | ||
|
|
4aef485d3c | ||
|
|
5e50fc8471 | ||
|
|
e4a7b4ff51 | ||
|
|
66ddf3b4c6 | ||
|
|
1af9609eed | ||
|
|
4f4cdf434b | ||
|
|
79eeaa2028 | ||
|
|
1f4ae86a84 | ||
|
|
797f58b5d5 | ||
|
|
2308981665 | ||
|
|
32181cd7e8 | ||
|
|
d8b89d2581 | ||
|
|
f4d6f582c8 | ||
|
|
b12c67f231 | ||
|
|
41f2e7b6f6 | ||
|
|
b5ec9c25c0 | ||
|
|
9e37ae02fd | ||
|
|
c81d31f2e3 | ||
|
|
f251a572e1 | ||
|
|
43cfa2f8bd | ||
|
|
ca4f751f9b | ||
|
|
b7b731bab7 | ||
|
|
bfc37e547f | ||
|
|
299c8cd914 | ||
|
|
7861e9e596 | ||
|
|
95e030f4e3 | ||
|
|
8155ff7a4f | ||
|
|
caef09bf8e | ||
|
|
84a3435c12 | ||
|
|
3403cffe51 | ||
|
|
4aa53b6be9 | ||
|
|
351d4954d7 | ||
|
|
4bda03fe8d | ||
|
|
0c72a2b982 | ||
|
|
933338f627 | ||
|
|
662f522032 | ||
|
|
a4e3761dea | ||
|
|
8129107ebf | ||
|
|
09c7329488 | ||
|
|
f9e1305da3 | ||
|
|
be56df7ad1 | ||
|
|
9865b66308 | ||
|
|
e8fee23093 | ||
|
|
bf50e377c5 | ||
|
|
076b01cbfc | ||
|
|
bb2ec1240a | ||
|
|
03c3ef9528 |
4
Cargo.lock
generated
4
Cargo.lock
generated
@@ -2850,6 +2850,10 @@ version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "swift-syntax-rs"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.106"
|
||||
|
||||
@@ -9,6 +9,7 @@ members = [
|
||||
"ruby/extractor",
|
||||
"unified/extractor",
|
||||
"unified/extractor/tree-sitter-swift",
|
||||
"unified/swift-syntax-rs",
|
||||
"rust/extractor",
|
||||
"rust/extractor/macros",
|
||||
"rust/ast-generator",
|
||||
|
||||
35
MODULE.bazel
35
MODULE.bazel
@@ -24,13 +24,15 @@ bazel_dep(name = "rules_python", version = "1.9.0")
|
||||
bazel_dep(name = "rules_shell", version = "0.7.1")
|
||||
bazel_dep(name = "bazel_skylib", version = "1.9.0")
|
||||
bazel_dep(name = "abseil-cpp", version = "20260107.1", repo_name = "absl")
|
||||
bazel_dep(name = "nlohmann_json", version = "3.11.3", repo_name = "json")
|
||||
bazel_dep(name = "nlohmann_json", version = "3.12.0.bcr.1", repo_name = "json")
|
||||
bazel_dep(name = "fmt", version = "12.1.0-codeql.1")
|
||||
bazel_dep(name = "rules_kotlin", version = "2.2.2-codeql.1")
|
||||
bazel_dep(name = "gazelle", version = "0.50.0")
|
||||
bazel_dep(name = "rules_dotnet", version = "0.21.5-codeql.1")
|
||||
bazel_dep(name = "googletest", version = "1.17.0.bcr.2")
|
||||
bazel_dep(name = "rules_rust", version = "0.69.0")
|
||||
bazel_dep(name = "rules_swift", version = "4.0.0-rc4")
|
||||
bazel_dep(name = "swift-syntax", version = "602.0.0.bcr.2")
|
||||
bazel_dep(name = "zstd", version = "1.5.7.bcr.1")
|
||||
|
||||
bazel_dep(name = "buildifier_prebuilt", version = "6.4.0", dev_dependency = True)
|
||||
@@ -219,6 +221,37 @@ use_repo(
|
||||
"swift-resource-dir-macos",
|
||||
)
|
||||
|
||||
# Hermetic Swift toolchain (from swift.org) for building the `swift-syntax`
|
||||
# based Rust wrapper in `unified/swift-syntax-rs`. This is the official
|
||||
# `rules_swift` standalone-toolchain extension and is independent of the
|
||||
# patched prebuilt toolchain wired up via `swift_deps` above.
|
||||
#
|
||||
# Bazel cannot auto-select between Linux distributions, so the toolchain is
|
||||
# registered explicitly for the distribution our CI runs on (ubuntu24.04,
|
||||
# x86_64). Add further platforms here if CI grows to cover them.
|
||||
#
|
||||
# We register the `exec` toolchain (normal host compilation, targeting
|
||||
# linux/x86_64) rather than the `embedded` one, which targets `os:none`
|
||||
# (Embedded Swift) and would not match a normal host build.
|
||||
#
|
||||
# The Swift version is read from `unified/swift-syntax-rs/.swift-version`, which
|
||||
# is the single source of truth shared with the local `cargo` build (via
|
||||
# swiftly / any Swift install) and `swift/Package.swift`.
|
||||
swift = use_extension("@rules_swift//swift:extensions.bzl", "swift")
|
||||
swift.toolchain(
|
||||
name = "swift_toolchain",
|
||||
swift_version_file = "//unified/swift-syntax-rs:.swift-version",
|
||||
)
|
||||
use_repo(
|
||||
swift,
|
||||
"swift_toolchain",
|
||||
"swift_toolchain_ubuntu24.04",
|
||||
)
|
||||
|
||||
register_toolchains(
|
||||
"@swift_toolchain//:swift_toolchain_exec_ubuntu24.04",
|
||||
)
|
||||
|
||||
node = use_extension("@rules_nodejs//nodejs:extensions.bzl", "node")
|
||||
node.toolchain(
|
||||
name = "nodejs",
|
||||
|
||||
@@ -6,6 +6,7 @@ private import cpp as Cpp
|
||||
private import codeql.dataflow.internal.FlowSummaryImpl
|
||||
private import codeql.dataflow.internal.AccessPathSyntax as AccessPath
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowNodes
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil
|
||||
private import semmle.code.cpp.ir.dataflow.internal.DataFlowImplSpecific as DataFlowImplSpecific
|
||||
private import semmle.code.cpp.dataflow.ExternalFlow
|
||||
@@ -20,8 +21,22 @@ module Input implements InputSig<Location, DataFlowImplSpecific::CppDataFlow> {
|
||||
|
||||
class SinkBase = Void;
|
||||
|
||||
class FlowSummaryCallBase = CallInstruction;
|
||||
|
||||
predicate callableFromSource(SummarizedCallableBase c) { exists(c.getBlock()) }
|
||||
|
||||
FlowSummaryCallBase getASourceCall(SummarizedCallableBase sc) {
|
||||
result.getStaticCallTarget() = sc
|
||||
}
|
||||
|
||||
DataFlowCallable getSummarizedCallableAsDataFlowCallable(SummarizedCallableBase c) {
|
||||
result.asSummarizedCallable() = c
|
||||
}
|
||||
|
||||
DataFlowCallable getSourceCallEnclosingCallable(FlowSummaryCallBase call) {
|
||||
result.asSourceCallable() = call.getEnclosingFunction()
|
||||
}
|
||||
|
||||
ArgumentPosition callbackSelfParameterPosition() { result = TDirectPosition(-1) }
|
||||
|
||||
ReturnKind getStandardReturnValueKind() { result = getReturnValueKind("") }
|
||||
@@ -30,6 +45,10 @@ module Input implements InputSig<Location, DataFlowImplSpecific::CppDataFlow> {
|
||||
arg = repeatStars(result.(NormalReturnKind).getIndirectionIndex())
|
||||
}
|
||||
|
||||
ParameterPosition getFlowSummaryParameterPosition(ReturnKind rk) {
|
||||
result = TFlowSummaryPosition(rk)
|
||||
}
|
||||
|
||||
string encodeParameterPosition(ParameterPosition pos) { result = pos.toString() }
|
||||
|
||||
string encodeArgumentPosition(ArgumentPosition pos) { result = pos.toString() }
|
||||
@@ -114,10 +133,22 @@ module Input implements InputSig<Location, DataFlowImplSpecific::CppDataFlow> {
|
||||
private import Make<Location, DataFlowImplSpecific::CppDataFlow, Input> as Impl
|
||||
|
||||
private module StepsInput implements Impl::Private::StepsInputSig {
|
||||
Impl::Private::SummaryNode getSummaryNode(Node n) {
|
||||
result = n.(FlowSummaryNode).getSummaryNode()
|
||||
}
|
||||
|
||||
DataFlowCall getACall(Public::SummarizedCallable sc) {
|
||||
result.getStaticCallTarget().getUnderlyingCallable() = sc
|
||||
}
|
||||
|
||||
Node getSourceOutNode(Input::FlowSummaryCallBase call, ReturnKind rk) {
|
||||
exists(IndirectReturnOutNode out | result = out |
|
||||
out.getCallInstruction() = call and
|
||||
pragma[only_bind_out](rk.(NormalReturnKind).getIndirectionIndex()) =
|
||||
pragma[only_bind_out](out.getIndirectionIndex())
|
||||
)
|
||||
}
|
||||
|
||||
DataFlowCallable getSourceNodeEnclosingCallable(Input::SourceBase source) { none() }
|
||||
|
||||
Node getSourceNode(Input::SourceBase source, Impl::Private::SummaryComponentStack s) { none() }
|
||||
|
||||
@@ -1534,12 +1534,8 @@ class FlowSummaryNode extends Node, TFlowSummaryNode {
|
||||
result = this.getSummaryNode().getSummarizedCallable()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the enclosing callable. For a `FlowSummaryNode` this is always the
|
||||
* summarized function this node is part of.
|
||||
*/
|
||||
override DataFlowCallable getEnclosingCallable() {
|
||||
result.asSummarizedCallable() = this.getSummarizedCallable()
|
||||
result = FlowSummaryImpl::Private::getEnclosingCallable(this.getSummaryNode())
|
||||
}
|
||||
|
||||
override Location getLocationImpl() { result = this.getSummarizedCallable().getLocation() }
|
||||
|
||||
@@ -561,6 +561,21 @@ class SummaryArgumentNode extends ArgumentNode, FlowSummaryNode {
|
||||
}
|
||||
}
|
||||
|
||||
/** An argument node that re-enters return output as input to a flow summary. */
|
||||
private class FlowSummaryArgumentNode extends ArgumentNode, FlowSummaryNode {
|
||||
private CallInstruction callInstruction;
|
||||
private ReturnKind rk;
|
||||
|
||||
FlowSummaryArgumentNode() {
|
||||
this.getSummaryNode() = FlowSummaryImpl::Private::summaryArgumentNode(callInstruction, rk)
|
||||
}
|
||||
|
||||
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
|
||||
call.asCallInstruction() = callInstruction and
|
||||
pos = TFlowSummaryPosition(rk)
|
||||
}
|
||||
}
|
||||
|
||||
/** A parameter position represented by an integer. */
|
||||
class ParameterPosition = Position;
|
||||
|
||||
@@ -616,6 +631,18 @@ class IndirectionPosition extends Position, TIndirectionPosition {
|
||||
final override int getIndirectionIndex() { result = indirectionIndex }
|
||||
}
|
||||
|
||||
class FlowSummaryPosition extends Position, TFlowSummaryPosition {
|
||||
ReturnKind rk;
|
||||
|
||||
FlowSummaryPosition() { this = TFlowSummaryPosition(rk) }
|
||||
|
||||
override string toString() { result = "write to: " + rk.toString() }
|
||||
|
||||
override int getArgumentIndex() { none() }
|
||||
|
||||
final override int getIndirectionIndex() { result = rk.getIndirectionIndex() }
|
||||
}
|
||||
|
||||
newtype TPosition =
|
||||
TDirectPosition(int argumentIndex) {
|
||||
exists(any(CallInstruction c).getArgument(argumentIndex))
|
||||
@@ -634,7 +661,8 @@ newtype TPosition =
|
||||
p = f.getParameter(argumentIndex) and
|
||||
indirectionIndex = [1 .. Ssa::getMaxIndirectionsForType(p.getUnspecifiedType()) - 1]
|
||||
)
|
||||
}
|
||||
} or
|
||||
TFlowSummaryPosition(ReturnKind rk) { FlowSummaryImpl::Private::relevantFlowSummaryPosition(rk) }
|
||||
|
||||
private newtype TReturnKind =
|
||||
TNormalReturnKind(int indirectionIndex) {
|
||||
|
||||
@@ -158,7 +158,7 @@ private module Cached {
|
||||
model = ""
|
||||
or
|
||||
// models-as-data summarized flow
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom.(FlowSummaryNode).getSummaryNode(),
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom,
|
||||
nodeTo.(FlowSummaryNode).getSummaryNode(), true, model)
|
||||
}
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ private module Cached {
|
||||
model = ""
|
||||
or
|
||||
// models-as-data summarized flow
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom.(FlowSummaryNode).getSummaryNode(),
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom,
|
||||
nodeTo.(FlowSummaryNode).getSummaryNode(), false, model)
|
||||
or
|
||||
// object->field conflation for content that is a `TaintInheritingContent`.
|
||||
|
||||
@@ -53,14 +53,17 @@ models
|
||||
| 52 | Summary: ; ; false; ymlStepGenerated; ; ; Argument[0]; ReturnValue; taint; df-generated |
|
||||
| 53 | Summary: ; ; false; ymlStepManual; ; ; Argument[0]; ReturnValue; taint; manual |
|
||||
| 54 | Summary: ; ; false; ymlStepManual_with_body; ; ; Argument[0]; ReturnValue; taint; manual |
|
||||
| 55 | Summary: ; TemplateClass1; true; templateFunction2<U,V>; (U,V); ; Argument[1]; ReturnValue; value; manual |
|
||||
| 56 | Summary: ; TemplateClass1<T>; false; templateFunction<U>; (T,U); ; Argument[0]; ReturnValue; value; manual |
|
||||
| 57 | Summary: ; TemplateClass2<T,U>; true; function; (U,T); ; Argument[1]; ReturnValue; value; manual |
|
||||
| 58 | Summary: Azure::Core::IO; BodyStream; true; Read; ; ; Argument[-1]; Argument[*0]; taint; manual |
|
||||
| 59 | Summary: Azure::Core::IO; BodyStream; true; ReadToCount; ; ; Argument[-1]; Argument[*0]; taint; manual |
|
||||
| 60 | Summary: Azure::Core::IO; BodyStream; true; ReadToEnd; ; ; Argument[-1]; ReturnValue.Element; taint; manual |
|
||||
| 61 | Summary: Azure; Nullable; true; Value; ; ; Argument[-1]; ReturnValue[*]; taint; manual |
|
||||
| 62 | Summary: boost::asio; ; false; buffer; ; ; Argument[*0]; ReturnValue; taint; manual |
|
||||
| 55 | Summary: ; MyString; true; operator[]; ; ; Argument[-1]; ReturnValue[*]; taint; manual |
|
||||
| 56 | Summary: ; MyString; true; operator[]; ; ; ReturnValue[*]; Argument[-1]; taint; manual |
|
||||
| 57 | Summary: ; ReverseFlow; true; get_ptr; ; ; ReturnValue[*]; Argument[-1].Field[ReverseFlow::value]; value; manual |
|
||||
| 58 | Summary: ; TemplateClass1; true; templateFunction2<U,V>; (U,V); ; Argument[1]; ReturnValue; value; manual |
|
||||
| 59 | Summary: ; TemplateClass1<T>; false; templateFunction<U>; (T,U); ; Argument[0]; ReturnValue; value; manual |
|
||||
| 60 | Summary: ; TemplateClass2<T,U>; true; function; (U,T); ; Argument[1]; ReturnValue; value; manual |
|
||||
| 61 | Summary: Azure::Core::IO; BodyStream; true; Read; ; ; Argument[-1]; Argument[*0]; taint; manual |
|
||||
| 62 | Summary: Azure::Core::IO; BodyStream; true; ReadToCount; ; ; Argument[-1]; Argument[*0]; taint; manual |
|
||||
| 63 | Summary: Azure::Core::IO; BodyStream; true; ReadToEnd; ; ; Argument[-1]; ReturnValue.Element; taint; manual |
|
||||
| 64 | Summary: Azure; Nullable; true; Value; ; ; Argument[-1]; ReturnValue[*]; taint; manual |
|
||||
| 65 | Summary: boost::asio; ; false; buffer; ; ; Argument[*0]; ReturnValue; taint; manual |
|
||||
edges
|
||||
| asio_streams.cpp:87:34:87:44 | read_until output argument | asio_streams.cpp:91:7:91:17 | recv_buffer | provenance | Src:MaD:32 |
|
||||
| asio_streams.cpp:87:34:87:44 | read_until output argument | asio_streams.cpp:93:29:93:39 | *recv_buffer | provenance | Src:MaD:32 Sink:MaD:2 |
|
||||
@@ -69,16 +72,16 @@ edges
|
||||
| asio_streams.cpp:100:44:100:62 | call to buffer | asio_streams.cpp:100:44:100:62 | call to buffer | provenance | |
|
||||
| asio_streams.cpp:100:44:100:62 | call to buffer | asio_streams.cpp:101:7:101:17 | send_buffer | provenance | |
|
||||
| asio_streams.cpp:100:44:100:62 | call to buffer | asio_streams.cpp:103:29:103:39 | *send_buffer | provenance | Sink:MaD:2 |
|
||||
| asio_streams.cpp:100:64:100:71 | *send_str | asio_streams.cpp:100:44:100:62 | call to buffer | provenance | MaD:62 |
|
||||
| asio_streams.cpp:100:64:100:71 | *send_str | asio_streams.cpp:100:44:100:62 | call to buffer | provenance | MaD:65 |
|
||||
| azure.cpp:253:48:253:60 | *call to GetBodyStream | azure.cpp:253:48:253:60 | *call to GetBodyStream | provenance | Src:MaD:29 |
|
||||
| azure.cpp:253:48:253:60 | *call to GetBodyStream | azure.cpp:257:5:257:8 | *resp | provenance | |
|
||||
| azure.cpp:253:48:253:60 | *call to GetBodyStream | azure.cpp:262:5:262:8 | *resp | provenance | |
|
||||
| azure.cpp:253:48:253:60 | *call to GetBodyStream | azure.cpp:266:38:266:41 | *resp | provenance | |
|
||||
| azure.cpp:257:5:257:8 | *resp | azure.cpp:257:16:257:21 | Read output argument | provenance | MaD:58 |
|
||||
| azure.cpp:257:5:257:8 | *resp | azure.cpp:257:16:257:21 | Read output argument | provenance | MaD:61 |
|
||||
| azure.cpp:257:16:257:21 | Read output argument | azure.cpp:258:10:258:16 | * ... | provenance | |
|
||||
| azure.cpp:262:5:262:8 | *resp | azure.cpp:262:23:262:28 | ReadToCount output argument | provenance | MaD:59 |
|
||||
| azure.cpp:262:5:262:8 | *resp | azure.cpp:262:23:262:28 | ReadToCount output argument | provenance | MaD:62 |
|
||||
| azure.cpp:262:23:262:28 | ReadToCount output argument | azure.cpp:263:10:263:16 | * ... | provenance | |
|
||||
| azure.cpp:266:38:266:41 | *resp | azure.cpp:266:44:266:52 | call to ReadToEnd [element] | provenance | MaD:60 |
|
||||
| azure.cpp:266:38:266:41 | *resp | azure.cpp:266:44:266:52 | call to ReadToEnd [element] | provenance | MaD:63 |
|
||||
| azure.cpp:266:44:266:52 | call to ReadToEnd [element] | azure.cpp:266:44:266:52 | call to ReadToEnd [element] | provenance | |
|
||||
| azure.cpp:266:44:266:52 | call to ReadToEnd [element] | azure.cpp:267:10:267:12 | vec [element] | provenance | |
|
||||
| azure.cpp:267:10:267:12 | vec [element] | azure.cpp:267:10:267:12 | vec | provenance | |
|
||||
@@ -94,10 +97,10 @@ edges
|
||||
| azure.cpp:278:10:278:13 | body | azure.cpp:278:10:278:13 | body | provenance | |
|
||||
| azure.cpp:281:68:281:84 | *call to ExtractBodyStream | azure.cpp:281:68:281:84 | *call to ExtractBodyStream | provenance | Src:MaD:26 |
|
||||
| azure.cpp:281:68:281:84 | *call to ExtractBodyStream | azure.cpp:282:21:282:23 | *call to get | provenance | |
|
||||
| azure.cpp:282:21:282:23 | *call to get | azure.cpp:282:28:282:36 | call to ReadToEnd [element] | provenance | MaD:60 |
|
||||
| azure.cpp:282:21:282:23 | *call to get | azure.cpp:282:28:282:36 | call to ReadToEnd [element] | provenance | MaD:63 |
|
||||
| azure.cpp:282:28:282:36 | call to ReadToEnd [element] | azure.cpp:282:10:282:38 | call to ReadToEnd | provenance | |
|
||||
| azure.cpp:282:28:282:36 | call to ReadToEnd [element] | azure.cpp:282:28:282:36 | call to ReadToEnd [element] | provenance | |
|
||||
| azure.cpp:289:24:289:56 | call to GetHeader | azure.cpp:289:63:289:65 | call to Value | provenance | MaD:61 |
|
||||
| azure.cpp:289:24:289:56 | call to GetHeader | azure.cpp:289:63:289:65 | call to Value | provenance | MaD:64 |
|
||||
| azure.cpp:289:32:289:40 | call to GetHeader | azure.cpp:289:24:289:56 | call to GetHeader | provenance | |
|
||||
| azure.cpp:289:32:289:40 | call to GetHeader | azure.cpp:289:32:289:40 | call to GetHeader | provenance | Src:MaD:30 |
|
||||
| azure.cpp:289:63:289:65 | call to Value | azure.cpp:289:63:289:65 | call to Value | provenance | |
|
||||
@@ -159,27 +162,27 @@ edges
|
||||
| test.cpp:133:10:133:18 | call to ymlSource | test.cpp:134:45:134:45 | x | provenance | |
|
||||
| test.cpp:134:13:134:43 | call to templateFunction | test.cpp:134:13:134:43 | call to templateFunction | provenance | |
|
||||
| test.cpp:134:13:134:43 | call to templateFunction | test.cpp:135:10:135:10 | y | provenance | Sink:MaD:1 |
|
||||
| test.cpp:134:45:134:45 | x | test.cpp:134:13:134:43 | call to templateFunction | provenance | MaD:56 |
|
||||
| test.cpp:134:45:134:45 | x | test.cpp:134:13:134:43 | call to templateFunction | provenance | MaD:59 |
|
||||
| test.cpp:146:10:146:18 | call to ymlSource | test.cpp:146:10:146:18 | call to ymlSource | provenance | Src:MaD:25 |
|
||||
| test.cpp:146:10:146:18 | call to ymlSource | test.cpp:148:26:148:26 | x | provenance | |
|
||||
| test.cpp:148:10:148:27 | call to function | test.cpp:148:10:148:27 | call to function | provenance | |
|
||||
| test.cpp:148:10:148:27 | call to function | test.cpp:149:10:149:10 | z | provenance | Sink:MaD:1 |
|
||||
| test.cpp:148:26:148:26 | x | test.cpp:148:10:148:27 | call to function | provenance | MaD:57 |
|
||||
| test.cpp:148:26:148:26 | x | test.cpp:148:10:148:27 | call to function | provenance | MaD:60 |
|
||||
| test.cpp:155:10:155:18 | call to ymlSource | test.cpp:155:10:155:18 | call to ymlSource | provenance | Src:MaD:25 |
|
||||
| test.cpp:155:10:155:18 | call to ymlSource | test.cpp:157:26:157:26 | x | provenance | |
|
||||
| test.cpp:157:13:157:20 | call to function | test.cpp:157:13:157:20 | call to function | provenance | |
|
||||
| test.cpp:157:13:157:20 | call to function | test.cpp:158:10:158:10 | z | provenance | Sink:MaD:1 |
|
||||
| test.cpp:157:26:157:26 | x | test.cpp:157:13:157:20 | call to function | provenance | MaD:57 |
|
||||
| test.cpp:157:26:157:26 | x | test.cpp:157:13:157:20 | call to function | provenance | MaD:60 |
|
||||
| test.cpp:164:34:164:34 | x | test.cpp:165:69:165:69 | x | provenance | |
|
||||
| test.cpp:165:12:165:64 | call to templateFunction2 | test.cpp:164:7:164:7 | *templateFunction3 | provenance | |
|
||||
| test.cpp:165:12:165:64 | call to templateFunction2 | test.cpp:165:12:165:64 | call to templateFunction2 | provenance | |
|
||||
| test.cpp:165:69:165:69 | x | test.cpp:165:12:165:64 | call to templateFunction2 | provenance | MaD:55 |
|
||||
| test.cpp:165:69:165:69 | x | test.cpp:165:12:165:64 | call to templateFunction2 | provenance | MaD:58 |
|
||||
| test.cpp:170:10:170:18 | call to ymlSource | test.cpp:170:10:170:18 | call to ymlSource | provenance | Src:MaD:25 |
|
||||
| test.cpp:170:10:170:18 | call to ymlSource | test.cpp:172:51:172:51 | x | provenance | |
|
||||
| test.cpp:172:13:172:44 | call to templateFunction3 | test.cpp:172:13:172:44 | call to templateFunction3 | provenance | |
|
||||
| test.cpp:172:13:172:44 | call to templateFunction3 | test.cpp:173:10:173:10 | y | provenance | Sink:MaD:1 |
|
||||
| test.cpp:172:51:172:51 | x | test.cpp:164:34:164:34 | x | provenance | |
|
||||
| test.cpp:172:51:172:51 | x | test.cpp:172:13:172:44 | call to templateFunction3 | provenance | MaD:55 |
|
||||
| test.cpp:172:51:172:51 | x | test.cpp:172:13:172:44 | call to templateFunction3 | provenance | MaD:58 |
|
||||
| test.cpp:186:2:186:2 | *s [post update] [myField] | test.cpp:187:33:187:34 | *& ... [myField] | provenance | |
|
||||
| test.cpp:186:2:186:24 | ... = ... | test.cpp:186:2:186:2 | *s [post update] [myField] | provenance | |
|
||||
| test.cpp:186:14:186:22 | call to ymlSource | test.cpp:186:2:186:24 | ... = ... | provenance | Src:MaD:25 |
|
||||
@@ -192,6 +195,18 @@ edges
|
||||
| test.cpp:200:10:200:33 | call to read_field_from_struct_2 | test.cpp:200:10:200:33 | call to read_field_from_struct_2 | provenance | |
|
||||
| test.cpp:200:10:200:33 | call to read_field_from_struct_2 | test.cpp:201:10:201:10 | x | provenance | Sink:MaD:1 |
|
||||
| test.cpp:200:35:200:36 | *& ... [myField] | test.cpp:200:10:200:33 | call to read_field_from_struct_2 | provenance | MaD:51 |
|
||||
| test.cpp:216:3:216:4 | get_ptr output argument [value] | test.cpp:217:11:217:12 | *rf [value] | provenance | |
|
||||
| test.cpp:216:3:216:28 | ... = ... | test.cpp:216:3:216:4 | get_ptr output argument [value] | provenance | MaD:57 |
|
||||
| test.cpp:216:18:216:26 | call to ymlSource | test.cpp:216:3:216:28 | ... = ... | provenance | Src:MaD:25 |
|
||||
| test.cpp:217:11:217:12 | *rf [value] | test.cpp:217:14:217:18 | value | provenance | |
|
||||
| test.cpp:217:14:217:18 | value | test.cpp:217:14:217:18 | value | provenance | |
|
||||
| test.cpp:217:14:217:18 | value | test.cpp:218:11:218:11 | x | provenance | Sink:MaD:1 |
|
||||
| test.cpp:222:3:222:3 | operator[] output argument | test.cpp:223:12:223:12 | *s | provenance | |
|
||||
| test.cpp:222:3:222:20 | ... = ... | test.cpp:222:3:222:3 | operator[] output argument | provenance | MaD:56 |
|
||||
| test.cpp:222:10:222:20 | call to ymlSource | test.cpp:222:3:222:20 | ... = ... | provenance | Src:MaD:25 |
|
||||
| test.cpp:223:12:223:12 | *s | test.cpp:223:13:223:15 | call to operator[] | provenance | MaD:55 |
|
||||
| test.cpp:223:13:223:15 | call to operator[] | test.cpp:223:13:223:15 | call to operator[] | provenance | |
|
||||
| test.cpp:223:13:223:15 | call to operator[] | test.cpp:224:11:224:11 | c | provenance | Sink:MaD:1 |
|
||||
| windows.cpp:22:15:22:29 | *call to GetCommandLineA | windows.cpp:22:15:22:29 | *call to GetCommandLineA | provenance | Src:MaD:3 |
|
||||
| windows.cpp:22:15:22:29 | *call to GetCommandLineA | windows.cpp:24:8:24:11 | * ... | provenance | |
|
||||
| windows.cpp:22:15:22:29 | *call to GetCommandLineA | windows.cpp:27:36:27:38 | *cmd | provenance | |
|
||||
@@ -470,6 +485,20 @@ nodes
|
||||
| test.cpp:200:10:200:33 | call to read_field_from_struct_2 | semmle.label | call to read_field_from_struct_2 |
|
||||
| test.cpp:200:35:200:36 | *& ... [myField] | semmle.label | *& ... [myField] |
|
||||
| test.cpp:201:10:201:10 | x | semmle.label | x |
|
||||
| test.cpp:216:3:216:4 | get_ptr output argument [value] | semmle.label | get_ptr output argument [value] |
|
||||
| test.cpp:216:3:216:28 | ... = ... | semmle.label | ... = ... |
|
||||
| test.cpp:216:18:216:26 | call to ymlSource | semmle.label | call to ymlSource |
|
||||
| test.cpp:217:11:217:12 | *rf [value] | semmle.label | *rf [value] |
|
||||
| test.cpp:217:14:217:18 | value | semmle.label | value |
|
||||
| test.cpp:217:14:217:18 | value | semmle.label | value |
|
||||
| test.cpp:218:11:218:11 | x | semmle.label | x |
|
||||
| test.cpp:222:3:222:3 | operator[] output argument | semmle.label | operator[] output argument |
|
||||
| test.cpp:222:3:222:20 | ... = ... | semmle.label | ... = ... |
|
||||
| test.cpp:222:10:222:20 | call to ymlSource | semmle.label | call to ymlSource |
|
||||
| test.cpp:223:12:223:12 | *s | semmle.label | *s |
|
||||
| test.cpp:223:13:223:15 | call to operator[] | semmle.label | call to operator[] |
|
||||
| test.cpp:223:13:223:15 | call to operator[] | semmle.label | call to operator[] |
|
||||
| test.cpp:224:11:224:11 | c | semmle.label | c |
|
||||
| windows.cpp:22:15:22:29 | *call to GetCommandLineA | semmle.label | *call to GetCommandLineA |
|
||||
| windows.cpp:22:15:22:29 | *call to GetCommandLineA | semmle.label | *call to GetCommandLineA |
|
||||
| windows.cpp:24:8:24:11 | * ... | semmle.label | * ... |
|
||||
|
||||
@@ -23,4 +23,7 @@ extensions:
|
||||
- ["", "TemplateClass1", True, "templateFunction2<U,V>", "(U,V)", "", "Argument[1]", "ReturnValue", "value", "manual"]
|
||||
- ["", "TemplateClass2<T,U>", True, "function", "(U,T)", "", "Argument[1]", "ReturnValue", "value", "manual"]
|
||||
- ["", "", False, "read_field_from_struct", "", "", "Argument[*0].Field[MyNamespace::MyStructInNamespace::myField]", "ReturnValue", "value", "manual"]
|
||||
- ["", "", False, "read_field_from_struct_2", "", "", "Argument[*0].Field[MyGlobalStruct::myField]", "ReturnValue", "value", "manual"]
|
||||
- ["", "", False, "read_field_from_struct_2", "", "", "Argument[*0].Field[MyGlobalStruct::myField]", "ReturnValue", "value", "manual"]
|
||||
- ["", "ReverseFlow", True, "get_ptr", "", "", "ReturnValue[*]", "Argument[-1].Field[ReverseFlow::value]", "value", "manual"]
|
||||
- ["", "MyString", True, "operator[]", "", "", "ReturnValue[*]", "Argument[-1]", "taint", "manual"]
|
||||
- ["", "MyString", True, "operator[]", "", "", "Argument[-1]", "ReturnValue[*]", "taint", "manual"]
|
||||
|
||||
@@ -21,3 +21,5 @@
|
||||
| test.cpp:173:10:173:10 | y | test-sink |
|
||||
| test.cpp:188:10:188:10 | x | test-sink |
|
||||
| test.cpp:201:10:201:10 | x | test-sink |
|
||||
| test.cpp:218:11:218:11 | x | test-sink |
|
||||
| test.cpp:224:11:224:11 | c | test-sink |
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
| test.cpp:170:10:170:18 | call to ymlSource | local |
|
||||
| test.cpp:186:14:186:22 | call to ymlSource | local |
|
||||
| test.cpp:199:14:199:22 | call to ymlSource | local |
|
||||
| test.cpp:216:18:216:26 | call to ymlSource | local |
|
||||
| test.cpp:222:10:222:20 | call to ymlSource | local |
|
||||
| windows.cpp:22:15:22:29 | *call to GetCommandLineA | local |
|
||||
| windows.cpp:34:17:34:38 | *call to GetEnvironmentStringsA | local |
|
||||
| windows.cpp:39:36:39:38 | GetEnvironmentVariableA output argument | local |
|
||||
|
||||
@@ -199,4 +199,28 @@ void test_fully_qualified_field_test_2() {
|
||||
s.myField = ymlSource();
|
||||
int x = read_field_from_struct_2(&s);
|
||||
ymlSink(x); // $ ir
|
||||
}
|
||||
|
||||
struct ReverseFlow {
|
||||
int value;
|
||||
int& get_ptr();
|
||||
};
|
||||
|
||||
struct MyString {
|
||||
char& operator[](unsigned);
|
||||
};
|
||||
|
||||
void test_reverse_flow(unsigned i, unsigned j) {
|
||||
{
|
||||
ReverseFlow rf;
|
||||
rf.get_ptr() = ymlSource();
|
||||
int x = rf.value;
|
||||
ymlSink(x); // $ ir
|
||||
}
|
||||
{
|
||||
MyString s;
|
||||
s[i] = ymlSource();
|
||||
char c = s[j];
|
||||
ymlSink(c); // $ ir
|
||||
}
|
||||
}
|
||||
@@ -714,7 +714,7 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo, string model) {
|
||||
) and
|
||||
model = ""
|
||||
or
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom.(FlowSummaryNode).getSummaryNode(),
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom,
|
||||
nodeTo.(FlowSummaryNode).getSummaryNode(), true, model)
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,8 @@ module Input implements InputSig<Location, DataFlowImplSpecific::CsharpDataFlow>
|
||||
|
||||
class SinkBase = Void;
|
||||
|
||||
class FlowSummaryCallBase = Void;
|
||||
|
||||
predicate neutralElement(SummarizedCallableBase c, string kind, string provenance, boolean isExact) {
|
||||
interpretNeutral(c, kind, provenance, isExact)
|
||||
}
|
||||
@@ -201,6 +203,10 @@ private module TypesInput implements Impl::Private::TypesInputSig {
|
||||
}
|
||||
|
||||
private module StepsInput implements Impl::Private::StepsInputSig {
|
||||
Impl::Private::SummaryNode getSummaryNode(Node n) {
|
||||
result = n.(FlowSummaryNode).getSummaryNode()
|
||||
}
|
||||
|
||||
DataFlowCall getACall(Public::SummarizedCallable sc) {
|
||||
sc = viableCallable(result).asSummarizedCallable()
|
||||
}
|
||||
|
||||
@@ -171,7 +171,7 @@ private module Cached {
|
||||
) and
|
||||
model = ""
|
||||
or
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom.(FlowSummaryNode).getSummaryNode(),
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom,
|
||||
nodeTo.(FlowSummaryNode).getSummaryNode(), false, model)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,13 +186,6 @@ private Expr aspWrittenValue(AspInlineMember m) {
|
||||
m.getMember().(Callable).canReturn(result)
|
||||
}
|
||||
|
||||
private string makeUrl(Location l) {
|
||||
exists(string path, int sl, int sc, int el, int ec |
|
||||
l.hasLocationInfo(path, sl, sc, el, ec) and
|
||||
result = "file://" + path + ":" + sl + ":" + sc + ":" + el + ":" + ec
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A sink for writes to properties that are accessed in ASP pages.
|
||||
*
|
||||
@@ -208,10 +201,7 @@ private class AspxCodeSink extends Sink {
|
||||
|
||||
AspxCodeSink() { this.getExpr() = aspWrittenValue(inline) }
|
||||
|
||||
override string explanation() {
|
||||
result =
|
||||
"member is [[\"accessed inline\"|\"" + makeUrl(inline.getLocation()) + "\"]] in an ASPX page"
|
||||
}
|
||||
override string explanation() { result = "member is accessed inline in an ASPX page" }
|
||||
}
|
||||
|
||||
/** A sink for the output stream associated with a `HttpListenerResponse`. */
|
||||
|
||||
@@ -69,6 +69,26 @@ The CodeQL library for Java and Kotlin analysis exposes the following extensible
|
||||
|
||||
The extensible predicates are populated using the models defined in data extension files.
|
||||
|
||||
Specifying types in Java and Kotlin models
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
**Nested and inner classes** are denoted by joining the enclosing type and the nested type with a dollar sign (``$``), for example ``Outer$Inner``. This applies both to the type column and to nested types in a signature. For example, the ``Level`` enum nested inside the ``Logger`` interface, nested inside the ``System`` class, is written as ``System$Logger$Level``:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
- ["java.lang", "System$Logger", True, "log", "(System$Logger$Level,String)", "", "Argument[1]", "log-injection", "manual"]
|
||||
|
||||
**Generics** are erased, so type parameters are removed:
|
||||
|
||||
- In the type column, leave out any type parameters, so ``List<E>`` becomes ``List``.
|
||||
- In the signature, replace each type parameter with its upper bound, or ``Object`` if it has none. So ``T`` from ``<T>`` becomes ``Object``, and ``T`` from ``<T extends Number>`` becomes ``Number``.
|
||||
|
||||
For example, ``forEach`` on ``Iterable<T>`` takes a ``Consumer<? super T>`` argument, so the type is ``Iterable`` and the signature is ``(Consumer)``:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
- ["java.lang", "Iterable", True, "forEach", "(Consumer)", "", "Argument[this].Element", "Argument[0].Parameter[0]", "value", "manual"]
|
||||
|
||||
Examples of custom model definitions
|
||||
------------------------------------
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ go_library(
|
||||
"//go/extractor/dbscheme",
|
||||
"//go/extractor/diagnostics",
|
||||
"//go/extractor/srcarchive",
|
||||
"//go/extractor/subst",
|
||||
"//go/extractor/toolchain",
|
||||
"//go/extractor/trap",
|
||||
"//go/extractor/util",
|
||||
|
||||
@@ -25,7 +25,6 @@ import (
|
||||
"github.com/github/codeql-go/extractor/dbscheme"
|
||||
"github.com/github/codeql-go/extractor/diagnostics"
|
||||
"github.com/github/codeql-go/extractor/srcarchive"
|
||||
"github.com/github/codeql-go/extractor/subst"
|
||||
"github.com/github/codeql-go/extractor/toolchain"
|
||||
"github.com/github/codeql-go/extractor/trap"
|
||||
"github.com/github/codeql-go/extractor/util"
|
||||
@@ -765,9 +764,9 @@ func normalizedPath(ast *ast.File, fset *token.FileSet) string {
|
||||
file := fset.File(ast.Package).Name()
|
||||
path, err := filepath.EvalSymlinks(file)
|
||||
if err != nil {
|
||||
path = file
|
||||
return file
|
||||
}
|
||||
return subst.ResolvePath(path)
|
||||
return path
|
||||
}
|
||||
|
||||
// extractFile extracts AST information for the given file
|
||||
|
||||
12
go/extractor/subst/BUILD.bazel
generated
12
go/extractor/subst/BUILD.bazel
generated
@@ -1,12 +0,0 @@
|
||||
load("@rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "subst",
|
||||
srcs = [
|
||||
"subst.go",
|
||||
"subst_other.go",
|
||||
"subst_windows.go",
|
||||
],
|
||||
importpath = "github.com/github/codeql-go/extractor/subst",
|
||||
visibility = ["//go:__subpackages__"],
|
||||
)
|
||||
@@ -1,30 +0,0 @@
|
||||
package subst
|
||||
|
||||
// ResolvePath resolves subst'd drive letters in a full path.
|
||||
// If the path starts with a subst'd drive letter, replaces it with the backing path.
|
||||
// Otherwise returns the path unchanged.
|
||||
func ResolvePath(path string) string {
|
||||
return resolvePath(path, ResolveDrive)
|
||||
}
|
||||
|
||||
func resolvePath(path string, resolveDrive func(string) string) string {
|
||||
if len(path) < 3 {
|
||||
return path
|
||||
}
|
||||
if path[1] != ':' {
|
||||
return path
|
||||
}
|
||||
if path[2] != '\\' && path[2] != '/' {
|
||||
return path
|
||||
}
|
||||
c := path[0]
|
||||
if !((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) {
|
||||
return path
|
||||
}
|
||||
|
||||
resolved := resolveDrive(path[:3])
|
||||
if resolved == "" {
|
||||
return path
|
||||
}
|
||||
return resolved + path[2:]
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
//go:build !windows
|
||||
|
||||
package subst
|
||||
|
||||
// ResolveDrive is a no-op on non-Windows platforms.
|
||||
func ResolveDrive(driveRoot string) string { return "" }
|
||||
@@ -1,67 +0,0 @@
|
||||
//go:build windows
|
||||
|
||||
package subst
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
dll *syscall.DLL
|
||||
procResolve *syscall.Proc
|
||||
procFree *syscall.Proc
|
||||
available bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
dist := os.Getenv("CODEQL_DIST")
|
||||
if dist == "" {
|
||||
return
|
||||
}
|
||||
dllPath := filepath.Join(dist, "tools", "win64", "canonicalize.dll")
|
||||
d, err := syscall.LoadDLL(dllPath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
p, err := d.FindProc("resolve_subst_u8")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
f, _ := d.FindProc("resolve_subst_free_u8")
|
||||
dll = d
|
||||
procResolve = p
|
||||
procFree = f
|
||||
available = true
|
||||
}
|
||||
|
||||
// ResolveDrive resolves a subst'd drive root (e.g. "X:\") to its backing path.
|
||||
// Returns "" if the drive is not subst'd or on error.
|
||||
func ResolveDrive(driveRoot string) string {
|
||||
if !available {
|
||||
return ""
|
||||
}
|
||||
driveBytes := append([]byte(driveRoot), 0)
|
||||
ret, _, _ := procResolve.Call(uintptr(unsafe.Pointer(&driveBytes[0])))
|
||||
if ret == 0 {
|
||||
return ""
|
||||
}
|
||||
result := goString((*byte)(unsafe.Pointer(ret)))
|
||||
if procFree != nil {
|
||||
procFree.Call(ret)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func goString(p *byte) string {
|
||||
if p == nil {
|
||||
return ""
|
||||
}
|
||||
var n int
|
||||
for ptr := unsafe.Pointer(p); *(*byte)(ptr) != 0; n++ {
|
||||
ptr = unsafe.Add(ptr, 1)
|
||||
}
|
||||
return string(unsafe.Slice(p, n))
|
||||
}
|
||||
@@ -141,7 +141,7 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo, string model) {
|
||||
any(FunctionModel m).flowStep(nodeFrom, nodeTo) and
|
||||
model = "FunctionModel"
|
||||
or
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom.(FlowSummaryNode).getSummaryNode(),
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom,
|
||||
nodeTo.(FlowSummaryNode).getSummaryNode(), true, model)
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,8 @@ module Input implements InputSig<Location, DataFlowImplSpecific::GoDataFlow> {
|
||||
|
||||
class SinkBase = Void;
|
||||
|
||||
class FlowSummaryCallBase = Void;
|
||||
|
||||
predicate callableFromSource(SummarizedCallableBase c) { exists(c.getFuncDef()) }
|
||||
|
||||
predicate neutralElement(
|
||||
@@ -113,6 +115,10 @@ module Input implements InputSig<Location, DataFlowImplSpecific::GoDataFlow> {
|
||||
private import Make<Location, DataFlowImplSpecific::GoDataFlow, Input> as Impl
|
||||
|
||||
private module StepsInput implements Impl::Private::StepsInputSig {
|
||||
Impl::Private::SummaryNode getSummaryNode(Node n) {
|
||||
result = n.(FlowSummaryNode).getSummaryNode()
|
||||
}
|
||||
|
||||
DataFlowCall getACall(Public::SummarizedCallable sc) {
|
||||
exists(DataFlow::CallNode call |
|
||||
call.asExpr() = result and
|
||||
|
||||
@@ -109,8 +109,8 @@ private predicate localAdditionalForwardTaintStep(
|
||||
or
|
||||
any(AdditionalTaintStep a).step(pred, succ) and model = "AdditionalTaintStep"
|
||||
or
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(pred.(DataFlowPrivate::FlowSummaryNode)
|
||||
.getSummaryNode(), succ.(DataFlowPrivate::FlowSummaryNode).getSummaryNode(), false, model)
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(pred,
|
||||
succ.(DataFlowPrivate::FlowSummaryNode).getSummaryNode(), false, model)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -188,6 +188,8 @@ org.apache.hadoop.hive.ql.metadata,1,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,
|
||||
org.apache.hc.client5.http.async.methods,84,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,84,,,,,,,,,,,,,,,,,,
|
||||
org.apache.hc.client5.http.classic.methods,37,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,37,,,,,,,,,,,,,,,,,,
|
||||
org.apache.hc.client5.http.fluent,19,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,19,,,,,,,,,,,,,,,,,,
|
||||
org.apache.hc.client5.http.protocol,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1
|
||||
org.apache.hc.client5.http.utils,,,7,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,7,
|
||||
org.apache.hc.core5.benchmark,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,
|
||||
org.apache.hc.core5.function,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
|
||||
org.apache.hc.core5.http,73,2,45,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,72,,,,,,,,,,,,,,,,2,45,
|
||||
|
||||
|
@@ -40,6 +40,6 @@ Java framework & library support
|
||||
`Spring <https://spring.io/>`_,``org.springframework.*``,46,494,143,26,,28,14,,35
|
||||
`Thymeleaf <https://www.thymeleaf.org/>`_,``org.thymeleaf``,,2,2,,,,,,
|
||||
`jOOQ <https://www.jooq.org/>`_,``org.jooq``,,,1,,,1,,,
|
||||
Others,"``actions.osgi``, ``antlr``, ``ch.ethz.ssh2``, ``cn.hutool.core.codec``, ``com.alibaba.com.caucho.hessian.io``, ``com.alibaba.druid.sql``, ``com.alibaba.fastjson2``, ``com.amazonaws.auth``, ``com.auth0.jwt.algorithms``, ``com.azure.identity``, ``com.caucho.burlap.io``, ``com.caucho.hessian.io``, ``com.cedarsoftware.util.io``, ``com.esotericsoftware.kryo.io``, ``com.esotericsoftware.kryo5.io``, ``com.esotericsoftware.yamlbeans``, ``com.hubspot.jinjava``, ``com.jcraft.jsch``, ``com.microsoft.sqlserver.jdbc``, ``com.mitchellbosecke.pebble``, ``com.opensymphony.xwork2``, ``com.sshtools.j2ssh.authentication``, ``com.sun.crypto.provider``, ``com.sun.jndi.ldap``, ``com.sun.net.httpserver``, ``com.sun.net.ssl``, ``com.sun.rowset``, ``com.sun.security.auth.module``, ``com.sun.security.ntlm``, ``com.sun.security.sasl.digest``, ``com.thoughtworks.xstream``, ``com.trilead.ssh2``, ``com.unboundid.ldap.sdk``, ``com.zaxxer.hikari``, ``flexjson``, ``hudson``, ``io.jsonwebtoken``, ``io.undertow.server.handlers.resource``, ``javafx.scene.web``, ``jenkins``, ``jodd.json``, ``liquibase.database.jvm``, ``liquibase.statement.core``, ``net.lingala.zip4j``, ``net.schmizz.sshj``, ``net.sf.json``, ``net.sf.saxon.s9api``, ``ognl``, ``org.acegisecurity``, ``org.antlr.runtime``, ``org.apache.avro``, ``org.apache.commons.codec``, ``org.apache.commons.compress.archivers.tar``, ``org.apache.commons.exec``, ``org.apache.commons.fileupload``, ``org.apache.commons.httpclient.util``, ``org.apache.commons.jelly``, ``org.apache.commons.jexl2``, ``org.apache.commons.jexl3``, ``org.apache.commons.lang``, ``org.apache.commons.logging``, ``org.apache.commons.net``, ``org.apache.commons.ognl``, ``org.apache.cxf.catalog``, ``org.apache.cxf.common.classloader``, ``org.apache.cxf.common.jaxb``, ``org.apache.cxf.common.logging``, ``org.apache.cxf.configuration.jsse``, ``org.apache.cxf.helpers``, ``org.apache.cxf.resource``, ``org.apache.cxf.staxutils``, ``org.apache.cxf.tools.corba.utils``, ``org.apache.cxf.tools.util``, ``org.apache.cxf.transform``, ``org.apache.directory.ldap.client.api``, ``org.apache.hadoop.fs``, ``org.apache.hadoop.hive.metastore``, ``org.apache.hadoop.hive.ql.exec``, ``org.apache.hadoop.hive.ql.metadata``, ``org.apache.hc.client5.http.async.methods``, ``org.apache.hc.client5.http.classic.methods``, ``org.apache.hc.client5.http.fluent``, ``org.apache.hive.hcatalog.templeton``, ``org.apache.ibatis.jdbc``, ``org.apache.ibatis.mapping``, ``org.apache.log4j``, ``org.apache.shiro.authc``, ``org.apache.shiro.codec``, ``org.apache.shiro.jndi``, ``org.apache.shiro.mgt``, ``org.apache.sshd.client.session``, ``org.apache.tools.ant``, ``org.apache.tools.zip``, ``org.codehaus.cargo.container.installer``, ``org.dom4j``, ``org.exolab.castor.xml``, ``org.fusesource.leveldbjni``, ``org.geogebra.web.full.main``, ``org.gradle.api.file``, ``org.ho.yaml``, ``org.influxdb``, ``org.jabsorb``, ``org.jboss.vfs``, ``org.jdbi.v3.core``, ``org.jenkins.ui.icon``, ``org.jenkins.ui.symbol``, ``org.keycloak.models.map.storage``, ``org.kohsuke.stapler``, ``org.lastaflute.web``, ``org.mvel2``, ``org.openjdk.jmh.runner.options``, ``org.owasp.esapi``, ``org.pac4j.jwt.config.encryption``, ``org.pac4j.jwt.config.signature``, ``org.scijava.log``, ``org.xml.sax``, ``org.xmlpull.v1``, ``play.libs.ws``, ``play.mvc``, ``ratpack.core.form``, ``ratpack.core.handling``, ``ratpack.core.http``, ``ratpack.exec``, ``ratpack.form``, ``ratpack.func``, ``ratpack.handling``, ``ratpack.http``, ``ratpack.util``, ``software.amazon.awssdk.transfer.s3.model``, ``sun.jvmstat.perfdata.monitor.protocol.local``, ``sun.jvmstat.perfdata.monitor.protocol.rmi``, ``sun.misc``, ``sun.net.ftp``, ``sun.net.www.protocol.http``, ``sun.security.acl``, ``sun.security.jgss.krb5``, ``sun.security.krb5``, ``sun.security.pkcs``, ``sun.security.pkcs11``, ``sun.security.provider``, ``sun.security.ssl``, ``sun.security.x509``, ``sun.tools.jconsole``",127,6034,775,148,6,14,18,,186
|
||||
Totals,,382,26403,2707,421,16,137,33,1,415
|
||||
Others,"``actions.osgi``, ``antlr``, ``ch.ethz.ssh2``, ``cn.hutool.core.codec``, ``com.alibaba.com.caucho.hessian.io``, ``com.alibaba.druid.sql``, ``com.alibaba.fastjson2``, ``com.amazonaws.auth``, ``com.auth0.jwt.algorithms``, ``com.azure.identity``, ``com.caucho.burlap.io``, ``com.caucho.hessian.io``, ``com.cedarsoftware.util.io``, ``com.esotericsoftware.kryo.io``, ``com.esotericsoftware.kryo5.io``, ``com.esotericsoftware.yamlbeans``, ``com.hubspot.jinjava``, ``com.jcraft.jsch``, ``com.microsoft.sqlserver.jdbc``, ``com.mitchellbosecke.pebble``, ``com.opensymphony.xwork2``, ``com.sshtools.j2ssh.authentication``, ``com.sun.crypto.provider``, ``com.sun.jndi.ldap``, ``com.sun.net.httpserver``, ``com.sun.net.ssl``, ``com.sun.rowset``, ``com.sun.security.auth.module``, ``com.sun.security.ntlm``, ``com.sun.security.sasl.digest``, ``com.thoughtworks.xstream``, ``com.trilead.ssh2``, ``com.unboundid.ldap.sdk``, ``com.zaxxer.hikari``, ``flexjson``, ``hudson``, ``io.jsonwebtoken``, ``io.undertow.server.handlers.resource``, ``javafx.scene.web``, ``jenkins``, ``jodd.json``, ``liquibase.database.jvm``, ``liquibase.statement.core``, ``net.lingala.zip4j``, ``net.schmizz.sshj``, ``net.sf.json``, ``net.sf.saxon.s9api``, ``ognl``, ``org.acegisecurity``, ``org.antlr.runtime``, ``org.apache.avro``, ``org.apache.commons.codec``, ``org.apache.commons.compress.archivers.tar``, ``org.apache.commons.exec``, ``org.apache.commons.fileupload``, ``org.apache.commons.httpclient.util``, ``org.apache.commons.jelly``, ``org.apache.commons.jexl2``, ``org.apache.commons.jexl3``, ``org.apache.commons.lang``, ``org.apache.commons.logging``, ``org.apache.commons.net``, ``org.apache.commons.ognl``, ``org.apache.cxf.catalog``, ``org.apache.cxf.common.classloader``, ``org.apache.cxf.common.jaxb``, ``org.apache.cxf.common.logging``, ``org.apache.cxf.configuration.jsse``, ``org.apache.cxf.helpers``, ``org.apache.cxf.resource``, ``org.apache.cxf.staxutils``, ``org.apache.cxf.tools.corba.utils``, ``org.apache.cxf.tools.util``, ``org.apache.cxf.transform``, ``org.apache.directory.ldap.client.api``, ``org.apache.hadoop.fs``, ``org.apache.hadoop.hive.metastore``, ``org.apache.hadoop.hive.ql.exec``, ``org.apache.hadoop.hive.ql.metadata``, ``org.apache.hc.client5.http.async.methods``, ``org.apache.hc.client5.http.classic.methods``, ``org.apache.hc.client5.http.fluent``, ``org.apache.hc.client5.http.protocol``, ``org.apache.hc.client5.http.utils``, ``org.apache.hive.hcatalog.templeton``, ``org.apache.ibatis.jdbc``, ``org.apache.ibatis.mapping``, ``org.apache.log4j``, ``org.apache.shiro.authc``, ``org.apache.shiro.codec``, ``org.apache.shiro.jndi``, ``org.apache.shiro.mgt``, ``org.apache.sshd.client.session``, ``org.apache.tools.ant``, ``org.apache.tools.zip``, ``org.codehaus.cargo.container.installer``, ``org.dom4j``, ``org.exolab.castor.xml``, ``org.fusesource.leveldbjni``, ``org.geogebra.web.full.main``, ``org.gradle.api.file``, ``org.ho.yaml``, ``org.influxdb``, ``org.jabsorb``, ``org.jboss.vfs``, ``org.jdbi.v3.core``, ``org.jenkins.ui.icon``, ``org.jenkins.ui.symbol``, ``org.keycloak.models.map.storage``, ``org.kohsuke.stapler``, ``org.lastaflute.web``, ``org.mvel2``, ``org.openjdk.jmh.runner.options``, ``org.owasp.esapi``, ``org.pac4j.jwt.config.encryption``, ``org.pac4j.jwt.config.signature``, ``org.scijava.log``, ``org.xml.sax``, ``org.xmlpull.v1``, ``play.libs.ws``, ``play.mvc``, ``ratpack.core.form``, ``ratpack.core.handling``, ``ratpack.core.http``, ``ratpack.exec``, ``ratpack.form``, ``ratpack.func``, ``ratpack.handling``, ``ratpack.http``, ``ratpack.util``, ``software.amazon.awssdk.transfer.s3.model``, ``sun.jvmstat.perfdata.monitor.protocol.local``, ``sun.jvmstat.perfdata.monitor.protocol.rmi``, ``sun.misc``, ``sun.net.ftp``, ``sun.net.www.protocol.http``, ``sun.security.acl``, ``sun.security.jgss.krb5``, ``sun.security.krb5``, ``sun.security.pkcs``, ``sun.security.pkcs11``, ``sun.security.provider``, ``sun.security.ssl``, ``sun.security.x509``, ``sun.tools.jconsole``",127,6042,775,148,6,14,18,,186
|
||||
Totals,,382,26411,2707,421,16,137,33,1,415
|
||||
|
||||
|
||||
@@ -1242,13 +1242,12 @@ public class FileUtil
|
||||
public static File tryMakeCanonical (File f)
|
||||
{
|
||||
try {
|
||||
f = f.getCanonicalFile();
|
||||
return f.getCanonicalFile();
|
||||
}
|
||||
catch (IOException ignored) {
|
||||
Exceptions.ignore(ignored, "Can't log error: Could be too verbose.");
|
||||
f = new File(simplifyPath(f));
|
||||
return new File(simplifyPath(f));
|
||||
}
|
||||
return SubstResolver.resolve(f);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
package com.semmle.util.files;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
/**
|
||||
* Resolves Windows {@code subst}ed drive letters to their underlying paths. On non-Windows
|
||||
* platforms, or when the native library failed to load, {@link #resolve(File)} is a no-op that
|
||||
* returns its argument unchanged.
|
||||
*/
|
||||
public class SubstResolver {
|
||||
private static final boolean available;
|
||||
|
||||
static {
|
||||
boolean loaded = false;
|
||||
if (File.separatorChar == '\\') {
|
||||
String dist = System.getenv("CODEQL_DIST");
|
||||
if (dist != null && !dist.isEmpty()) {
|
||||
try {
|
||||
Path library = Paths.get(dist).resolve("tools").resolve("win64")
|
||||
.resolve("canonicalize.dll").toAbsolutePath();
|
||||
System.load(library.toString());
|
||||
loaded = true;
|
||||
} catch (RuntimeException | UnsatisfiedLinkError ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
available = loaded;
|
||||
}
|
||||
|
||||
private SubstResolver() {}
|
||||
|
||||
/**
|
||||
* Given a drive root like {@code "X:\\"}, returns the path that drive is
|
||||
* {@code subst}ed to, or {@code null} if the letter isn't a subst mapping.
|
||||
*/
|
||||
private static native String nativeResolveSubst(String driveRoot);
|
||||
|
||||
/**
|
||||
* If {@code f} is an absolute path starting with a {@code subst}ed drive letter, return an
|
||||
* equivalent path with the drive letter replaced by its target. Otherwise return {@code f}
|
||||
* unchanged.
|
||||
*/
|
||||
public static File resolve(File f) {
|
||||
if (!available) {
|
||||
return f;
|
||||
}
|
||||
String path = f.getPath();
|
||||
if (path.length() < 3 || path.charAt(1) != ':') {
|
||||
return f;
|
||||
}
|
||||
char sep = path.charAt(2);
|
||||
if (sep != '\\' && sep != '/') {
|
||||
return f;
|
||||
}
|
||||
if (!isDriveLetter(path.charAt(0))) {
|
||||
return f;
|
||||
}
|
||||
|
||||
String resolved = nativeResolveSubst(path.substring(0, 3));
|
||||
if (resolved == null) {
|
||||
return f;
|
||||
}
|
||||
|
||||
return new File(resolved + path.substring(2));
|
||||
}
|
||||
|
||||
private static boolean isDriveLetter(char c) {
|
||||
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
|
||||
}
|
||||
|
||||
public static boolean isAvailable() {
|
||||
return available;
|
||||
}
|
||||
}
|
||||
@@ -247,8 +247,8 @@ private predicate simpleLocalFlowStep0(Node node1, Node node2, string model) {
|
||||
or
|
||||
cloneStep(node1, node2) and model = "CloneStep"
|
||||
or
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(node1.(FlowSummaryNode).getSummaryNode(),
|
||||
node2.(FlowSummaryNode).getSummaryNode(), true, model)
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(node1, node2.(FlowSummaryNode).getSummaryNode(),
|
||||
true, model)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -41,6 +41,8 @@ module Input implements InputSig<Location, DataFlowImplSpecific::JavaDataFlow> {
|
||||
|
||||
class SinkBase = Void;
|
||||
|
||||
class FlowSummaryCallBase = Void;
|
||||
|
||||
predicate neutralElement(
|
||||
Input::SummarizedCallableBase c, string kind, string provenance, boolean isExact
|
||||
) {
|
||||
@@ -144,6 +146,10 @@ private module TypesInput implements Impl::Private::TypesInputSig {
|
||||
}
|
||||
|
||||
private module StepsInput implements Impl::Private::StepsInputSig {
|
||||
Impl::Private::SummaryNode getSummaryNode(Node n) {
|
||||
result = n.(FlowSummaryNode).getSummaryNode()
|
||||
}
|
||||
|
||||
DataFlowCall getACall(Public::SummarizedCallable sc) {
|
||||
sc = viableCallable(result).asSummarizedCallable()
|
||||
}
|
||||
|
||||
@@ -145,8 +145,8 @@ private module Cached {
|
||||
)
|
||||
)
|
||||
or
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(src.(DataFlowPrivate::FlowSummaryNode)
|
||||
.getSummaryNode(), sink.(DataFlowPrivate::FlowSummaryNode).getSummaryNode(), false, model)
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(src,
|
||||
sink.(DataFlowPrivate::FlowSummaryNode).getSummaryNode(), false, model)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,8 +13,7 @@ predicate taintFlowUpdate(DataFlow::ParameterNode p1, DataFlow::ParameterNode p2
|
||||
}
|
||||
|
||||
predicate summaryStep(FlowSummaryNode src, FlowSummaryNode sink) {
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(src.getSummaryNode(), sink.getSummaryNode(),
|
||||
false, _) or
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(src, sink.getSummaryNode(), false, _) or
|
||||
FlowSummaryImpl::Private::Steps::summaryReadStep(src.getSummaryNode(), _, sink.getSummaryNode()) or
|
||||
FlowSummaryImpl::Private::Steps::summaryStoreStep(src.getSummaryNode(), _, sink.getSummaryNode())
|
||||
}
|
||||
|
||||
@@ -1212,8 +1212,8 @@ private predicate valuePreservingStep(Node node1, Node node2) {
|
||||
or
|
||||
node2 = FlowSteps::getThrowTarget(node1)
|
||||
or
|
||||
FlowSummaryPrivate::Steps::summaryLocalStep(node1.(FlowSummaryNode).getSummaryNode(),
|
||||
node2.(FlowSummaryNode).getSummaryNode(), true, _) // TODO: preserve 'model'
|
||||
FlowSummaryPrivate::Steps::summaryLocalStep(node1, node2.(FlowSummaryNode).getSummaryNode(), true,
|
||||
_) // TODO: preserve 'model'
|
||||
}
|
||||
|
||||
predicate knownSourceModel(Node sink, string model) { none() }
|
||||
|
||||
@@ -142,6 +142,10 @@ string encodeArgumentPosition(ArgumentPosition pos) {
|
||||
ReturnKind getStandardReturnValueKind() { result = MkNormalReturnKind() and Stage::ref() }
|
||||
|
||||
private module FlowSummaryStepInput implements Private::StepsInputSig {
|
||||
Private::SummaryNode getSummaryNode(DataFlow::Node n) {
|
||||
result = n.(FlowSummaryNode).getSummaryNode()
|
||||
}
|
||||
|
||||
overlay[global]
|
||||
DataFlowCall getACall(SummarizedCallable sc) {
|
||||
exists(LibraryCallable callable | callable = sc |
|
||||
|
||||
@@ -12,8 +12,8 @@ cached
|
||||
predicate defaultAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
TaintTracking::AdditionalTaintStep::step(node1, node2)
|
||||
or
|
||||
FlowSummaryPrivate::Steps::summaryLocalStep(node1.(FlowSummaryNode).getSummaryNode(),
|
||||
node2.(FlowSummaryNode).getSummaryNode(), false, _) // TODO: preserve 'model' parameter
|
||||
FlowSummaryPrivate::Steps::summaryLocalStep(node1, node2.(FlowSummaryNode).getSummaryNode(),
|
||||
false, _) // TODO: preserve 'model' parameter
|
||||
or
|
||||
// Convert steps out of array elements to plain taint steps
|
||||
FlowSummaryPrivate::Steps::summaryReadStep(node1.(FlowSummaryNode).getSummaryNode(),
|
||||
|
||||
@@ -3,6 +3,7 @@ private import DataFlowImplSpecific
|
||||
private import codeql.dataflow.DataFlow as SharedDataFlow
|
||||
private import codeql.dataflow.TaintTracking as SharedTaintTracking
|
||||
private import codeql.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
|
||||
private import codeql.util.Void
|
||||
|
||||
module JSDataFlow implements SharedDataFlow::InputSig<Location> {
|
||||
import Private
|
||||
@@ -28,6 +29,8 @@ module JSFlowSummary implements FlowSummaryImpl::InputSig<Location, JSDataFlow>
|
||||
private import semmle.javascript.dataflow.internal.FlowSummaryPrivate as FlowSummaryPrivate
|
||||
import FlowSummaryPrivate
|
||||
|
||||
class FlowSummaryCallBase = Void;
|
||||
|
||||
overlay[local]
|
||||
predicate callableFromSource(SummarizedCallableBase c) { none() }
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ No user-facing changes.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Added new full SSRF sanitization barrier from the new AntiSSRF library.
|
||||
* Added new full SSRF sanitization barrier from the new AntiSSRF library.
|
||||
* When a guard such as `isSafe(x)` is defined, we now also automatically handle `isSafe(x) == true` and `isSafe(x) != false`.
|
||||
|
||||
## 6.1.1
|
||||
@@ -169,7 +169,7 @@ No user-facing changes.
|
||||
### Minor Analysis Improvements
|
||||
|
||||
- The modelling of Psycopg2 now supports the use of `psycopg2.pool` connection pools for handling database connections.
|
||||
* Removed `lxml` as an XML bomb sink. The underlying libxml2 library now includes [entity reference loop detection](https://github.com/lxml/lxml/blob/f33ac2c2f5f9c4c4c1fc47f363be96db308f2fa6/doc/FAQ.txt#L1077) that prevents XML bomb attacks.
|
||||
* Removed `lxml` as an XML bomb sink. The underlying libxml2 library now includes [entity reference loop detection](https://github.com/lxml/lxml/blob/f33ac2c2f5f9c4c4c1fc47f363be96db308f2fa6/doc/FAQ.txt#L1077) that prevents XML bomb attacks.
|
||||
|
||||
## 4.0.13
|
||||
|
||||
@@ -262,7 +262,7 @@ No user-facing changes.
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The sensitive data library has been improved so that `snake_case` style variable names are recognized more reliably. This may result in more sensitive data being identified, and more results from queries that use the sensitive data library.
|
||||
- Additional taint steps through methods of `lxml.etree.Element` and `lxml.etree.ElementTree` objects from the `lxml` PyPI package have been modeled.
|
||||
- Additional taint steps through methods of `lxml.etree.Element` and `lxml.etree.ElementTree` objects from the `lxml` PyPI package have been modeled.
|
||||
|
||||
## 3.1.0
|
||||
|
||||
@@ -316,7 +316,7 @@ No user-facing changes.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The common sanitizer guard `StringConstCompareBarrier` has been renamed to `ConstCompareBarrier` and expanded to cover comparisons with other constant values such as `None`. This may result in fewer false positive results for several queries.
|
||||
* The common sanitizer guard `StringConstCompareBarrier` has been renamed to `ConstCompareBarrier` and expanded to cover comparisons with other constant values such as `None`. This may result in fewer false positive results for several queries.
|
||||
|
||||
## 2.0.0
|
||||
|
||||
@@ -545,7 +545,7 @@ No user-facing changes.
|
||||
|
||||
### New Features
|
||||
|
||||
* The `DataFlow::StateConfigSig` signature module has gained default implementations for `isBarrier/2` and `isAdditionalFlowStep/4`.
|
||||
* The `DataFlow::StateConfigSig` signature module has gained default implementations for `isBarrier/2` and `isAdditionalFlowStep/4`.
|
||||
Hence it is no longer needed to provide `none()` implementations of these predicates if they are not needed.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
@@ -572,7 +572,7 @@ No user-facing changes.
|
||||
* Deleted many deprecated predicates and classes with uppercase `API`, `HTTP`, `XSS`, `SQL`, etc. in their names. Use the PascalCased versions instead.
|
||||
* Deleted the deprecated `getName()` predicate from the `Container` class, use `getAbsolutePath()` instead.
|
||||
* Deleted many deprecated module names that started with a lowercase letter, use the versions that start with an uppercase letter instead.
|
||||
* Deleted many deprecated predicates in `PointsTo.qll`.
|
||||
* Deleted many deprecated predicates in `PointsTo.qll`.
|
||||
* Deleted many deprecated files from the `semmle.python.security` package.
|
||||
* Deleted the deprecated `BottleRoutePointToExtension` class from `Extensions.qll`.
|
||||
* Type tracking is now aware of flow summaries. This leads to a richer API graph, and may lead to more results in some queries.
|
||||
@@ -729,7 +729,7 @@ No user-facing changes.
|
||||
### Deprecated APIs
|
||||
|
||||
* Some unused predicates in `SsaDefinitions.qll`, `TObject.qll`, `protocols.qll`, and the `pointsto/` folder have been deprecated.
|
||||
* Some classes/modules with upper-case acronyms in their name have been renamed to follow our style-guide.
|
||||
* Some classes/modules with upper-case acronyms in their name have been renamed to follow our style-guide.
|
||||
The old name still exists as a deprecated alias.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
@@ -748,9 +748,9 @@ No user-facing changes.
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
* Many classes/predicates/modules with upper-case acronyms in their name have been renamed to follow our style-guide.
|
||||
* Many classes/predicates/modules with upper-case acronyms in their name have been renamed to follow our style-guide.
|
||||
The old name still exists as a deprecated alias.
|
||||
* The utility files previously in the `semmle.python.security.performance` package have been moved to the `semmle.python.security.regexp` package.
|
||||
* The utility files previously in the `semmle.python.security.performance` package have been moved to the `semmle.python.security.regexp` package.
|
||||
The previous files still exist as deprecated aliases.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
@@ -843,9 +843,9 @@ No user-facing changes.
|
||||
|
||||
### Deprecated APIs
|
||||
|
||||
* Many classes/predicates/modules that had upper-case acronyms have been renamed to follow our style-guide.
|
||||
* Many classes/predicates/modules that had upper-case acronyms have been renamed to follow our style-guide.
|
||||
The old name still exists as a deprecated alias.
|
||||
* Some modules that started with a lowercase letter have been renamed to follow our style-guide.
|
||||
* Some modules that started with a lowercase letter have been renamed to follow our style-guide.
|
||||
The old name still exists as a deprecated alias.
|
||||
|
||||
### New Features
|
||||
|
||||
@@ -529,7 +529,7 @@ predicate simpleLocalFlowStepForTypetracking(Node nodeFrom, Node nodeTo) {
|
||||
}
|
||||
|
||||
private predicate summaryLocalStep(Node nodeFrom, Node nodeTo, string model) {
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom.(FlowSummaryNode).getSummaryNode(),
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom,
|
||||
nodeTo.(FlowSummaryNode).getSummaryNode(), true, model)
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@ module Input implements InputSig<Location, DataFlowImplSpecific::PythonDataFlow>
|
||||
|
||||
class SinkBase = Void;
|
||||
|
||||
class FlowSummaryCallBase = Void;
|
||||
|
||||
predicate callableFromSource(SummarizedCallableBase c) { none() }
|
||||
|
||||
ArgumentPosition callbackSelfParameterPosition() { result.isLambdaSelf() }
|
||||
@@ -111,6 +113,10 @@ module Input implements InputSig<Location, DataFlowImplSpecific::PythonDataFlow>
|
||||
private import Make<Location, DataFlowImplSpecific::PythonDataFlow, Input> as Impl
|
||||
|
||||
private module StepsInput implements Impl::Private::StepsInputSig {
|
||||
Impl::Private::SummaryNode getSummaryNode(Node n) {
|
||||
result = n.(FlowSummaryNode).getSummaryNode()
|
||||
}
|
||||
|
||||
overlay[global]
|
||||
DataFlowCall getACall(Public::SummarizedCallable sc) {
|
||||
result =
|
||||
|
||||
@@ -80,10 +80,8 @@ private module Cached {
|
||||
) and
|
||||
model = ""
|
||||
or
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom
|
||||
.(DataFlowPrivate::FlowSummaryNode)
|
||||
.getSummaryNode(), nodeTo.(DataFlowPrivate::FlowSummaryNode).getSummaryNode(), false,
|
||||
model)
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom,
|
||||
nodeTo.(DataFlowPrivate::FlowSummaryNode).getSummaryNode(), false, model)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -170,7 +170,13 @@ module TypeTrackingInput implements Shared::TypeTrackingInput<Location> {
|
||||
|
||||
/** Holds if there is a level step from `nodeFrom` to `nodeTo`, which may depend on the call graph. */
|
||||
predicate levelStepCall(Node nodeFrom, LocalSourceNode nodeTo) {
|
||||
instanceFieldStep(nodeFrom, nodeTo)
|
||||
// HOTFIX: `instanceFieldStep` is temporarily disabled (via `and none()`).
|
||||
// It uses `classInstanceTracker(cls)` -- itself a type-tracker run --
|
||||
// from inside `levelStepCall`, creating a structural mutual recursion
|
||||
// that causes catastrophic query slowdowns on some OOP-heavy Python
|
||||
// codebases (e.g. mypy and dask). The `and none()` should be removed
|
||||
// once that recursion is redesigned.
|
||||
instanceFieldStep(nodeFrom, nodeTo) and none()
|
||||
or
|
||||
inheritedFieldStep(nodeFrom, nodeTo)
|
||||
}
|
||||
|
||||
@@ -157,7 +157,7 @@ class MyClass2(object):
|
||||
print(self.foo) # $ tracked MISSING: tracked=foo
|
||||
|
||||
instance = MyClass2()
|
||||
print(instance.foo) # $ tracked MISSING: tracked=foo
|
||||
print(instance.foo) # $ MISSING: tracked=foo tracked
|
||||
instance.print_foo() # $ MISSING: tracked=foo
|
||||
|
||||
|
||||
@@ -195,7 +195,7 @@ class Sub1(Base1):
|
||||
|
||||
sub1 = Sub1()
|
||||
sub1.read_foo()
|
||||
print(sub1.foo) # $ tracked MISSING: tracked=foo
|
||||
print(sub1.foo) # $ MISSING: tracked=foo tracked
|
||||
|
||||
|
||||
# attribute written in a subclass method, read in an inherited base class method
|
||||
@@ -210,7 +210,7 @@ class Sub2(Base2):
|
||||
|
||||
sub2 = Sub2()
|
||||
sub2.read_bar()
|
||||
print(sub2.bar) # $ tracked MISSING: tracked=bar
|
||||
print(sub2.bar) # $ MISSING: tracked=bar tracked
|
||||
|
||||
|
||||
# attribute written in a base class method, read on an instance of the subclass
|
||||
@@ -223,4 +223,4 @@ class Sub3(Base3):
|
||||
pass
|
||||
|
||||
sub3 = Sub3()
|
||||
print(sub3.baz) # $ tracked MISSING: tracked=baz
|
||||
print(sub3.baz) # $ MISSING: tracked=baz tracked
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#select
|
||||
| app.py:23:20:23:24 | ControlFlowNode for query | app.py:20:18:20:21 | ControlFlowNode for name | app.py:23:20:23:24 | ControlFlowNode for query | This SQL query depends on a $@. | app.py:20:18:20:21 | ControlFlowNode for name | user-provided value |
|
||||
| app.py:30:20:30:24 | ControlFlowNode for query | app.py:27:19:27:22 | ControlFlowNode for name | app.py:30:20:30:24 | ControlFlowNode for query | This SQL query depends on a $@. | app.py:27:19:27:22 | ControlFlowNode for name | user-provided value |
|
||||
| app.py:37:20:37:24 | ControlFlowNode for query | app.py:34:19:34:22 | ControlFlowNode for name | app.py:37:20:37:24 | ControlFlowNode for query | This SQL query depends on a $@. | app.py:34:19:34:22 | ControlFlowNode for name | user-provided value |
|
||||
| app.py:44:20:44:24 | ControlFlowNode for query | app.py:41:19:41:22 | ControlFlowNode for name | app.py:44:20:44:24 | ControlFlowNode for query | This SQL query depends on a $@. | app.py:41:19:41:22 | ControlFlowNode for name | user-provided value |
|
||||
| app.py:51:20:51:24 | ControlFlowNode for query | app.py:48:19:48:22 | ControlFlowNode for name | app.py:51:20:51:24 | ControlFlowNode for query | This SQL query depends on a $@. | app.py:48:19:48:22 | ControlFlowNode for name | user-provided value |
|
||||
| sql_injection.py:21:24:21:77 | ControlFlowNode for BinaryExpr | sql_injection.py:14:15:14:22 | ControlFlowNode for username | sql_injection.py:21:24:21:77 | ControlFlowNode for BinaryExpr | This SQL query depends on a $@. | sql_injection.py:14:15:14:22 | ControlFlowNode for username | user-provided value |
|
||||
@@ -25,8 +24,6 @@ edges
|
||||
| app.py:21:5:21:9 | ControlFlowNode for query | app.py:23:20:23:24 | ControlFlowNode for query | provenance | |
|
||||
| app.py:27:19:27:22 | ControlFlowNode for name | app.py:28:5:28:9 | ControlFlowNode for query | provenance | |
|
||||
| app.py:28:5:28:9 | ControlFlowNode for query | app.py:30:20:30:24 | ControlFlowNode for query | provenance | |
|
||||
| app.py:34:19:34:22 | ControlFlowNode for name | app.py:35:5:35:9 | ControlFlowNode for query | provenance | |
|
||||
| app.py:35:5:35:9 | ControlFlowNode for query | app.py:37:20:37:24 | ControlFlowNode for query | provenance | |
|
||||
| app.py:41:19:41:22 | ControlFlowNode for name | app.py:42:5:42:9 | ControlFlowNode for query | provenance | |
|
||||
| app.py:42:5:42:9 | ControlFlowNode for query | app.py:44:20:44:24 | ControlFlowNode for query | provenance | |
|
||||
| app.py:48:19:48:22 | ControlFlowNode for name | app.py:49:5:49:9 | ControlFlowNode for query | provenance | |
|
||||
@@ -54,9 +51,6 @@ nodes
|
||||
| app.py:27:19:27:22 | ControlFlowNode for name | semmle.label | ControlFlowNode for name |
|
||||
| app.py:28:5:28:9 | ControlFlowNode for query | semmle.label | ControlFlowNode for query |
|
||||
| app.py:30:20:30:24 | ControlFlowNode for query | semmle.label | ControlFlowNode for query |
|
||||
| app.py:34:19:34:22 | ControlFlowNode for name | semmle.label | ControlFlowNode for name |
|
||||
| app.py:35:5:35:9 | ControlFlowNode for query | semmle.label | ControlFlowNode for query |
|
||||
| app.py:37:20:37:24 | ControlFlowNode for query | semmle.label | ControlFlowNode for query |
|
||||
| app.py:41:19:41:22 | ControlFlowNode for name | semmle.label | ControlFlowNode for name |
|
||||
| app.py:42:5:42:9 | ControlFlowNode for query | semmle.label | ControlFlowNode for query |
|
||||
| app.py:44:20:44:24 | ControlFlowNode for query | semmle.label | ControlFlowNode for query |
|
||||
|
||||
@@ -31,10 +31,10 @@ async def unsafe2(name: str): # $ Source
|
||||
cursor.close()
|
||||
|
||||
@app.get("/unsafe3/")
|
||||
async def unsafe3(name: str): # $ Source
|
||||
async def unsafe3(name: str): # $ MISSING: Source
|
||||
query = "select * from users where name=" + name
|
||||
cursor = hdb_con3.cursor()
|
||||
cursor.execute(query) # $ Alert
|
||||
cursor.execute(query) # $ MISSING: Alert
|
||||
cursor.close()
|
||||
|
||||
@app.get("/unsafe4/")
|
||||
|
||||
@@ -198,8 +198,7 @@ module LocalFlow {
|
||||
FlowSummaryNode nodeFrom, FlowSummaryNode nodeTo, FlowSummaryImpl::Public::SummarizedCallable c,
|
||||
string model
|
||||
) {
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom.getSummaryNode(),
|
||||
nodeTo.getSummaryNode(), true, model) and
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom, nodeTo.getSummaryNode(), true, model) and
|
||||
c = nodeFrom.getSummarizedCallable()
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@ module Input implements InputSig<Location, DataFlowImplSpecific::RubyDataFlow> {
|
||||
|
||||
class SinkBase = Void;
|
||||
|
||||
class FlowSummaryCallBase = Void;
|
||||
|
||||
predicate callableFromSource(SummarizedCallableBase c) { none() }
|
||||
|
||||
ArgumentPosition callbackSelfParameterPosition() { result.isLambdaSelf() }
|
||||
@@ -157,6 +159,10 @@ module Input implements InputSig<Location, DataFlowImplSpecific::RubyDataFlow> {
|
||||
private import Make<Location, DataFlowImplSpecific::RubyDataFlow, Input> as Impl
|
||||
|
||||
private module StepsInput implements Impl::Private::StepsInputSig {
|
||||
Impl::Private::SummaryNode getSummaryNode(Node n) {
|
||||
result = n.(FlowSummaryNode).getSummaryNode()
|
||||
}
|
||||
|
||||
DataFlowCall getACall(Public::SummarizedCallable sc) {
|
||||
result.asCall().getAstNode() = sc.(LibraryCallable).getACall()
|
||||
or
|
||||
|
||||
@@ -109,7 +109,7 @@ private module Cached {
|
||||
) and
|
||||
model = ""
|
||||
or
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom.(FlowSummaryNode).getSummaryNode(),
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom,
|
||||
nodeTo.(FlowSummaryNode).getSummaryNode(), false, model)
|
||||
or
|
||||
any(FlowSteps::AdditionalTaintStep s).step(nodeFrom, nodeTo) and model = "AdditionalTaintStep"
|
||||
|
||||
@@ -180,7 +180,7 @@ Expr getPostUpdateReverseStep(Expr e, boolean preservesValue) {
|
||||
module LocalFlow {
|
||||
predicate flowSummaryLocalStep(Node nodeFrom, Node nodeTo, string model) {
|
||||
exists(FlowSummaryImpl::Public::SummarizedCallable c |
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom.(FlowSummaryNode).getSummaryNode(),
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom,
|
||||
nodeTo.(FlowSummaryNode).getSummaryNode(), true, model) and
|
||||
c = nodeFrom.(FlowSummaryNode).getSummarizedCallable()
|
||||
)
|
||||
|
||||
@@ -11,6 +11,7 @@ private import codeql.rust.dataflow.FlowSummary
|
||||
private import codeql.rust.dataflow.Ssa
|
||||
private import codeql.rust.dataflow.internal.ModelsAsData
|
||||
private import Content
|
||||
private import Node
|
||||
|
||||
predicate encodeContentTupleField(TupleFieldContent c, string arg) {
|
||||
exists(Addressable a, int pos, string prefix |
|
||||
@@ -28,9 +29,12 @@ predicate encodeContentStructField(StructFieldContent c, string arg) {
|
||||
|
||||
module Input implements InputSig<Location, RustDataFlow> {
|
||||
private import codeql.rust.frameworks.stdlib.Stdlib
|
||||
private import codeql.util.Void
|
||||
|
||||
class SummarizedCallableBase = Function;
|
||||
|
||||
class FlowSummaryCallBase = Void;
|
||||
|
||||
predicate callableFromSource(SummarizedCallableBase c) { c.fromSource() }
|
||||
|
||||
abstract private class SourceSinkBase extends AstNode {
|
||||
@@ -144,6 +148,10 @@ module Input implements InputSig<Location, RustDataFlow> {
|
||||
private import Make<Location, RustDataFlow, Input> as Impl
|
||||
|
||||
module StepsInput implements Impl::Private::StepsInputSig {
|
||||
Impl::Private::SummaryNode getSummaryNode(RustDataFlow::Node n) {
|
||||
result = n.(FlowSummaryNode).getSummaryNode()
|
||||
}
|
||||
|
||||
DataFlowCall getACall(Public::SummarizedCallable sc) { result.asCall().getStaticTarget() = sc }
|
||||
|
||||
/** Gets the argument of `source` described by `sc`, if any. */
|
||||
|
||||
@@ -83,7 +83,7 @@ module RustTaintTrackingGen<DataFlowImpl::RustDataFlowInputSig I> implements
|
||||
pred.(Node::PostUpdateNode).getPreUpdateNode().asExpr(), _, succ, _)
|
||||
)
|
||||
or
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(pred.(Node::FlowSummaryNode).getSummaryNode(),
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(pred,
|
||||
succ.(Node::FlowSummaryNode).getSummaryNode(), false, model)
|
||||
}
|
||||
|
||||
|
||||
@@ -62,24 +62,34 @@ module HardcodedCryptographicValue {
|
||||
abstract class Barrier extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A literal, considered as a flow source.
|
||||
* Holds if `e` is a literal or a combination of literals that is constant.
|
||||
*/
|
||||
private class LiteralSource extends Source {
|
||||
LiteralSource() { this.asExpr() instanceof LiteralExpr }
|
||||
private predicate isConstant(Expr e) {
|
||||
e instanceof LiteralExpr // e.g. `0`
|
||||
or
|
||||
forex(Expr elem | elem = e.(ArrayListExpr).getExpr(_) | isConstant(elem)) // e.g. `[0, 0, 0, 0]`
|
||||
or
|
||||
isConstant(e.(ArrayRepeatExpr).getRepeatOperand()) // e.g. `[0; 10]`
|
||||
or
|
||||
// e.g. `const MY_CONST: u64 = ...`
|
||||
// the constant initializer / body is the preferred source location for flow paths, when available.
|
||||
e = any(Const c).getBody()
|
||||
or
|
||||
// e.g. `u64::MAX`
|
||||
// when the constant initializer is not available as a source location (case above), use the access instead.
|
||||
e instanceof ConstAccess and
|
||||
not exists(e.(ConstAccess).getConst().getBody())
|
||||
or
|
||||
// e.g. `1 << 4`
|
||||
isConstant(e.(BinaryExpr).getLhs()) and
|
||||
isConstant(e.(BinaryExpr).getRhs())
|
||||
}
|
||||
|
||||
/**
|
||||
* An array initialized from a list of literals, considered as a single flow source. For example:
|
||||
* ```
|
||||
* [0, 0, 0, 0]
|
||||
* [0; 10]
|
||||
* ```
|
||||
* A constant, considered as a flow source.
|
||||
*/
|
||||
private class ArrayListSource extends Source {
|
||||
ArrayListSource() {
|
||||
this.asExpr().(ArrayListExpr).getExpr(_) instanceof LiteralExpr or
|
||||
this.asExpr().(ArrayRepeatExpr).getRepeatOperand() instanceof LiteralExpr
|
||||
}
|
||||
private class ConstantSource extends Source {
|
||||
ConstantSource() { isConstant(this.asExpr()) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -155,4 +165,24 @@ module HardcodedCryptographicValue {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An arithmetic or bitwise operation that acts as a barrier.
|
||||
*
|
||||
* This prevents false positives where a hard-coded value is combined with
|
||||
* non-constant data through operations like `+`, `^`, or `+=` (including string concatenation).
|
||||
*/
|
||||
private class ArithmeticOperationBarrier extends Barrier {
|
||||
ArithmeticOperationBarrier() {
|
||||
// binary operations (e.g. `a + b`, `a ^ b`)
|
||||
this.asExpr() = any(BinaryArithmeticOperation a).getAnOperand()
|
||||
or
|
||||
this.asExpr() = any(BinaryBitwiseOperation a).getAnOperand()
|
||||
or
|
||||
// compound assignments (e.g. `a += b`, `a ^= b`)
|
||||
this.asExpr() = any(AssignArithmeticOperation a).getAnOperand()
|
||||
or
|
||||
this.asExpr() = any(AssignBitwiseOperation a).getAnOperand()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The `rust/hard-coded-cryptographic-value` query now treats arithmetic and bitwise operations, including string append operations, as barriers. This addresses false positive results where hard-coded constants are combined with non-constant data, such as incrementing a nonce or appending variable data to a constant prefix.
|
||||
@@ -1,5 +1,5 @@
|
||||
multipleResolvedTargets
|
||||
| main.rs:2223:9:2223:31 | ... .my_add(...) |
|
||||
| main.rs:2225:9:2225:29 | ... .my_add(...) |
|
||||
| main.rs:2740:13:2740:17 | x.f() |
|
||||
| main.rs:2240:9:2240:31 | ... .my_add(...) |
|
||||
| main.rs:2242:9:2242:29 | ... .my_add(...) |
|
||||
| main.rs:2757:13:2757:17 | x.f() |
|
||||
| regressions.rs:179:17:179:27 | ... + ... |
|
||||
|
||||
@@ -264,6 +264,10 @@ mod method_non_parametric_trait_impl {
|
||||
}
|
||||
}
|
||||
|
||||
trait MyTrait2<B> {
|
||||
fn m3(self) -> B;
|
||||
}
|
||||
|
||||
trait MyProduct<A, B> {
|
||||
// MyProduct::fst
|
||||
fn fst(self) -> A;
|
||||
@@ -275,6 +279,10 @@ mod method_non_parametric_trait_impl {
|
||||
x.m1() // $ target=m1
|
||||
}
|
||||
|
||||
fn call_trait_m1_trait2_m3<T1, T2: MyTrait<T1>, T3: MyTrait2<T2>>(x: T3) -> T1 {
|
||||
x.m3().m1() // $ target=m1 target=m3
|
||||
}
|
||||
|
||||
impl MyTrait<S1> for MyThing<S1> {
|
||||
// MyThing<S1>::m1
|
||||
fn m1(self) -> S1 {
|
||||
@@ -289,6 +297,13 @@ mod method_non_parametric_trait_impl {
|
||||
}
|
||||
}
|
||||
|
||||
impl MyTrait2<MyThing<S1>> for MyThing<S2> {
|
||||
// MyThing<S2>::m3
|
||||
fn m3(self) -> MyThing<S1> {
|
||||
MyThing { a: S1 }
|
||||
}
|
||||
}
|
||||
|
||||
// Implementation where the type parameter `TD` only occurs in the
|
||||
// implemented trait and not the implementing type.
|
||||
impl<TD> MyTrait<TD> for MyThing<S3>
|
||||
@@ -453,6 +468,8 @@ mod method_non_parametric_trait_impl {
|
||||
let thing = MyThing { a: S1 };
|
||||
let i = thing.convert_to(); // $ type=i:S1 target=T::convert_to
|
||||
let j = convert_to(thing); // $ target=convert_to $ MISSING: type=j:S1 -- the blanket implementation `impl<T: MyTrait<S1>> ConvertTo<S1> for T` is currently not included in the constraint analysis
|
||||
|
||||
let x = call_trait_m1_trait2_m3(MyThing { a: S2 }); // $ target=call_trait_m1_trait2_m3 type=x:S1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,45 +10,50 @@
|
||||
| test_cookie.rs:21:28:21:34 | [0; 64] | test_cookie.rs:21:28:21:34 | [0; 64] | test_cookie.rs:22:16:22:24 | ...::from | This hard-coded value is used as $@. | test_cookie.rs:22:16:22:24 | ...::from | a key |
|
||||
| test_cookie.rs:38:28:38:36 | [0u8; 64] | test_cookie.rs:38:28:38:36 | [0u8; 64] | test_cookie.rs:42:14:42:32 | ...::from | This hard-coded value is used as $@. | test_cookie.rs:42:14:42:32 | ...::from | a key |
|
||||
| test_cookie.rs:49:23:49:25 | 0u8 | test_cookie.rs:49:23:49:25 | 0u8 | test_cookie.rs:53:14:53:32 | ...::from | This hard-coded value is used as $@. | test_cookie.rs:53:14:53:32 | ...::from | a key |
|
||||
| test_heuristic.rs:44:31:44:38 | [0u8; 16] | test_heuristic.rs:44:31:44:38 | [0u8; 16] | test_heuristic.rs:45:41:45:48 | const_iv | This hard-coded value is used as $@. | test_heuristic.rs:45:41:45:48 | const_iv | an initialization vector |
|
||||
| test_heuristic.rs:63:30:63:37 | "secret" | test_heuristic.rs:63:30:63:37 | "secret" | test_heuristic.rs:63:30:63:37 | "secret" | This hard-coded value is used as $@. | test_heuristic.rs:63:30:63:37 | "secret" | a password |
|
||||
| test_heuristic.rs:64:20:64:27 | [0u8; 16] | test_heuristic.rs:64:20:64:27 | [0u8; 16] | test_heuristic.rs:64:19:64:27 | &... | This hard-coded value is used as $@. | test_heuristic.rs:64:19:64:27 | &... | a nonce |
|
||||
| test_heuristic.rs:65:31:65:38 | [0u8; 16] | test_heuristic.rs:65:31:65:38 | [0u8; 16] | test_heuristic.rs:65:30:65:38 | &... | This hard-coded value is used as $@. | test_heuristic.rs:65:30:65:38 | &... | a salt |
|
||||
| test_heuristic.rs:67:22:67:22 | 0 | test_heuristic.rs:67:22:67:22 | 0 | test_heuristic.rs:67:22:67:22 | 0 | This hard-coded value is used as $@. | test_heuristic.rs:67:22:67:22 | 0 | a salt |
|
||||
| test_heuristic.rs:69:32:69:32 | 1 | test_heuristic.rs:69:32:69:32 | 1 | test_heuristic.rs:69:22:69:32 | ... + ... | This hard-coded value is used as $@. | test_heuristic.rs:69:22:69:32 | ... + ... | a salt |
|
||||
| test_heuristic.rs:70:34:70:35 | 32 | test_heuristic.rs:70:34:70:35 | 32 | test_heuristic.rs:70:22:70:62 | ... ^ ... | This hard-coded value is used as $@. | test_heuristic.rs:70:22:70:62 | ... ^ ... | a salt |
|
||||
| test_heuristic.rs:70:52:70:61 | 0xFFFFFFFF | test_heuristic.rs:70:52:70:61 | 0xFFFFFFFF | test_heuristic.rs:70:22:70:62 | ... ^ ... | This hard-coded value is used as $@. | test_heuristic.rs:70:22:70:62 | ... ^ ... | a salt |
|
||||
| test_heuristic.rs:38:25:38:30 | 0xFFFF | test_heuristic.rs:38:25:38:30 | 0xFFFF | test_heuristic.rs:81:22:81:31 | MY_CONST_1 | This hard-coded value is used as $@. | test_heuristic.rs:81:22:81:31 | MY_CONST_1 | a salt |
|
||||
| test_heuristic.rs:39:25:39:59 | ... as u64 | test_heuristic.rs:39:25:39:59 | ... as u64 | test_heuristic.rs:82:22:82:31 | MY_CONST_2 | This hard-coded value is used as $@. | test_heuristic.rs:82:22:82:31 | MY_CONST_2 | a salt |
|
||||
| test_heuristic.rs:40:27:40:32 | 0xFFFF | test_heuristic.rs:40:27:40:32 | 0xFFFF | test_heuristic.rs:83:22:83:32 | MY_STATIC_3 | This hard-coded value is used as $@. | test_heuristic.rs:83:22:83:32 | MY_STATIC_3 | a salt |
|
||||
| test_heuristic.rs:49:31:49:38 | [0u8; 16] | test_heuristic.rs:49:31:49:38 | [0u8; 16] | test_heuristic.rs:50:41:50:48 | const_iv | This hard-coded value is used as $@. | test_heuristic.rs:50:41:50:48 | const_iv | an initialization vector |
|
||||
| test_heuristic.rs:68:30:68:37 | "secret" | test_heuristic.rs:68:30:68:37 | "secret" | test_heuristic.rs:68:30:68:37 | "secret" | This hard-coded value is used as $@. | test_heuristic.rs:68:30:68:37 | "secret" | a password |
|
||||
| test_heuristic.rs:69:20:69:27 | [0u8; 16] | test_heuristic.rs:69:20:69:27 | [0u8; 16] | test_heuristic.rs:69:19:69:27 | &... | This hard-coded value is used as $@. | test_heuristic.rs:69:19:69:27 | &... | a nonce |
|
||||
| test_heuristic.rs:70:31:70:38 | [0u8; 16] | test_heuristic.rs:70:31:70:38 | [0u8; 16] | test_heuristic.rs:70:30:70:38 | &... | This hard-coded value is used as $@. | test_heuristic.rs:70:30:70:38 | &... | a salt |
|
||||
| test_heuristic.rs:72:22:72:22 | 0 | test_heuristic.rs:72:22:72:22 | 0 | test_heuristic.rs:72:22:72:22 | 0 | This hard-coded value is used as $@. | test_heuristic.rs:72:22:72:22 | 0 | a salt |
|
||||
| test_heuristic.rs:76:22:76:27 | ... << ... | test_heuristic.rs:76:22:76:27 | ... << ... | test_heuristic.rs:76:22:76:27 | ... << ... | This hard-coded value is used as $@. | test_heuristic.rs:76:22:76:27 | ... << ... | a salt |
|
||||
| test_heuristic.rs:78:22:78:29 | ...::MAX | test_heuristic.rs:78:22:78:29 | ...::MAX | test_heuristic.rs:78:22:78:29 | ...::MAX | This hard-coded value is used as $@. | test_heuristic.rs:78:22:78:29 | ...::MAX | a salt |
|
||||
| test_heuristic.rs:79:22:79:33 | ... / ... | test_heuristic.rs:79:22:79:33 | ... / ... | test_heuristic.rs:79:22:79:33 | ... / ... | This hard-coded value is used as $@. | test_heuristic.rs:79:22:79:33 | ... / ... | a salt |
|
||||
| test_heuristic.rs:86:29:86:32 | 1u64 | test_heuristic.rs:86:29:86:32 | 1u64 | test_heuristic.rs:87:22:87:31 | MY_CONST_5 | This hard-coded value is used as $@. | test_heuristic.rs:87:22:87:31 | MY_CONST_5 | a salt |
|
||||
| test_heuristic.rs:88:29:88:33 | ... + ... | test_heuristic.rs:88:29:88:33 | ... + ... | test_heuristic.rs:89:22:89:31 | MY_CONST_6 | This hard-coded value is used as $@. | test_heuristic.rs:89:22:89:31 | MY_CONST_6 | a salt |
|
||||
edges
|
||||
| test_cipher.rs:18:9:18:14 | const1 [&ref] | test_cipher.rs:19:73:19:78 | const1 [&ref] | provenance | |
|
||||
| test_cipher.rs:18:28:18:36 | &... [&ref] | test_cipher.rs:18:9:18:14 | const1 [&ref] | provenance | |
|
||||
| test_cipher.rs:18:29:18:36 | [0u8; 16] | test_cipher.rs:18:28:18:36 | &... [&ref] | provenance | |
|
||||
| test_cipher.rs:19:49:19:79 | ...::from_slice(...) [&ref] | test_cipher.rs:19:30:19:47 | ...::new | provenance | MaD:3 Sink:MaD:3 |
|
||||
| test_cipher.rs:19:73:19:78 | const1 [&ref] | test_cipher.rs:19:49:19:79 | ...::from_slice(...) [&ref] | provenance | MaD:17 |
|
||||
| test_cipher.rs:19:73:19:78 | const1 [&ref] | test_cipher.rs:19:49:19:79 | ...::from_slice(...) [&ref] | provenance | MaD:13 |
|
||||
| test_cipher.rs:25:9:25:14 | const4 [&ref] | test_cipher.rs:26:66:26:71 | const4 [&ref] | provenance | |
|
||||
| test_cipher.rs:25:28:25:36 | &... [&ref] | test_cipher.rs:25:9:25:14 | const4 [&ref] | provenance | |
|
||||
| test_cipher.rs:25:29:25:36 | [0u8; 16] | test_cipher.rs:25:28:25:36 | &... [&ref] | provenance | |
|
||||
| test_cipher.rs:26:42:26:72 | ...::from_slice(...) [&ref] | test_cipher.rs:26:30:26:40 | ...::new | provenance | MaD:4 Sink:MaD:4 |
|
||||
| test_cipher.rs:26:66:26:71 | const4 [&ref] | test_cipher.rs:26:42:26:72 | ...::from_slice(...) [&ref] | provenance | MaD:17 |
|
||||
| test_cipher.rs:26:66:26:71 | const4 [&ref] | test_cipher.rs:26:42:26:72 | ...::from_slice(...) [&ref] | provenance | MaD:13 |
|
||||
| test_cipher.rs:29:9:29:14 | const5 [&ref] | test_cipher.rs:30:95:30:100 | const5 [&ref] | provenance | |
|
||||
| test_cipher.rs:29:28:29:36 | &... [&ref] | test_cipher.rs:29:9:29:14 | const5 [&ref] | provenance | |
|
||||
| test_cipher.rs:29:29:29:36 | [0u8; 16] | test_cipher.rs:29:28:29:36 | &... [&ref] | provenance | |
|
||||
| test_cipher.rs:30:72:30:101 | ...::from_slice(...) [&ref] | test_cipher.rs:30:30:30:40 | ...::new | provenance | MaD:5 Sink:MaD:5 |
|
||||
| test_cipher.rs:30:95:30:100 | const5 [&ref] | test_cipher.rs:30:72:30:101 | ...::from_slice(...) [&ref] | provenance | MaD:17 |
|
||||
| test_cipher.rs:30:95:30:100 | const5 [&ref] | test_cipher.rs:30:72:30:101 | ...::from_slice(...) [&ref] | provenance | MaD:13 |
|
||||
| test_cipher.rs:37:9:37:14 | const7 | test_cipher.rs:38:74:38:79 | const7 | provenance | |
|
||||
| test_cipher.rs:37:27:37:74 | [...] | test_cipher.rs:37:9:37:14 | const7 | provenance | |
|
||||
| test_cipher.rs:38:49:38:80 | ...::from_slice(...) [&ref] | test_cipher.rs:38:30:38:47 | ...::new | provenance | MaD:3 Sink:MaD:3 |
|
||||
| test_cipher.rs:38:73:38:79 | &const7 [&ref] | test_cipher.rs:38:49:38:80 | ...::from_slice(...) [&ref] | provenance | MaD:17 |
|
||||
| test_cipher.rs:38:73:38:79 | &const7 [&ref] | test_cipher.rs:38:49:38:80 | ...::from_slice(...) [&ref] | provenance | MaD:13 |
|
||||
| test_cipher.rs:38:74:38:79 | const7 | test_cipher.rs:38:73:38:79 | &const7 [&ref] | provenance | |
|
||||
| test_cipher.rs:41:9:41:14 | const8 [&ref] | test_cipher.rs:42:73:42:78 | const8 [&ref] | provenance | |
|
||||
| test_cipher.rs:41:28:41:76 | &... [&ref] | test_cipher.rs:41:9:41:14 | const8 [&ref] | provenance | |
|
||||
| test_cipher.rs:41:29:41:76 | [...] | test_cipher.rs:41:28:41:76 | &... [&ref] | provenance | |
|
||||
| test_cipher.rs:42:49:42:79 | ...::from_slice(...) [&ref] | test_cipher.rs:42:30:42:47 | ...::new | provenance | MaD:3 Sink:MaD:3 |
|
||||
| test_cipher.rs:42:73:42:78 | const8 [&ref] | test_cipher.rs:42:49:42:79 | ...::from_slice(...) [&ref] | provenance | MaD:17 |
|
||||
| test_cipher.rs:42:73:42:78 | const8 [&ref] | test_cipher.rs:42:49:42:79 | ...::from_slice(...) [&ref] | provenance | MaD:13 |
|
||||
| test_cipher.rs:50:9:50:15 | const10 [element] | test_cipher.rs:51:75:51:81 | const10 [element] | provenance | |
|
||||
| test_cipher.rs:50:37:50:52 | ...::zeroed | test_cipher.rs:50:37:50:54 | ...::zeroed(...) [element] | provenance | Src:MaD:7 |
|
||||
| test_cipher.rs:50:37:50:54 | ...::zeroed(...) [element] | test_cipher.rs:50:9:50:15 | const10 [element] | provenance | |
|
||||
| test_cipher.rs:51:50:51:82 | ...::from_slice(...) [&ref, element] | test_cipher.rs:51:31:51:48 | ...::new | provenance | MaD:3 Sink:MaD:3 Sink:MaD:3 |
|
||||
| test_cipher.rs:51:74:51:81 | &const10 [&ref, element] | test_cipher.rs:51:50:51:82 | ...::from_slice(...) [&ref, element] | provenance | MaD:17 |
|
||||
| test_cipher.rs:51:74:51:81 | &const10 [&ref, element] | test_cipher.rs:51:50:51:82 | ...::from_slice(...) [&ref, element] | provenance | MaD:13 |
|
||||
| test_cipher.rs:51:75:51:81 | const10 [element] | test_cipher.rs:51:74:51:81 | &const10 [&ref, element] | provenance | |
|
||||
| test_cipher.rs:73:9:73:14 | const2 [&ref] | test_cipher.rs:74:46:74:51 | const2 [&ref] | provenance | |
|
||||
| test_cipher.rs:73:18:73:26 | &... [&ref] | test_cipher.rs:73:9:73:14 | const2 [&ref] | provenance | |
|
||||
@@ -64,26 +69,27 @@ edges
|
||||
| test_cookie.rs:22:27:22:32 | array2 | test_cookie.rs:22:26:22:32 | &array2 [&ref] | provenance | |
|
||||
| test_cookie.rs:38:9:38:14 | array2 | test_cookie.rs:42:34:42:39 | array2 | provenance | |
|
||||
| test_cookie.rs:38:18:38:37 | ...::from(...) | test_cookie.rs:38:9:38:14 | array2 | provenance | |
|
||||
| test_cookie.rs:38:28:38:36 | [0u8; 64] | test_cookie.rs:38:18:38:37 | ...::from(...) | provenance | MaD:8 |
|
||||
| test_cookie.rs:38:28:38:36 | [0u8; 64] | test_cookie.rs:38:18:38:37 | ...::from(...) | provenance | MaD:9 |
|
||||
| test_cookie.rs:38:28:38:36 | [0u8; 64] | test_cookie.rs:38:18:38:37 | ...::from(...) | provenance | MaD:10 |
|
||||
| test_cookie.rs:38:28:38:36 | [0u8; 64] | test_cookie.rs:38:18:38:37 | ...::from(...) | provenance | MaD:11 |
|
||||
| test_cookie.rs:38:28:38:36 | [0u8; 64] | test_cookie.rs:38:18:38:37 | ...::from(...) | provenance | MaD:12 |
|
||||
| test_cookie.rs:38:28:38:36 | [0u8; 64] | test_cookie.rs:38:18:38:37 | ...::from(...) | provenance | MaD:13 |
|
||||
| test_cookie.rs:38:28:38:36 | [0u8; 64] | test_cookie.rs:38:18:38:37 | ...::from(...) | provenance | MaD:14 |
|
||||
| test_cookie.rs:38:28:38:36 | [0u8; 64] | test_cookie.rs:38:18:38:37 | ...::from(...) | provenance | MaD:15 |
|
||||
| test_cookie.rs:38:28:38:36 | [0u8; 64] | test_cookie.rs:38:18:38:37 | ...::from(...) | provenance | MaD:16 |
|
||||
| test_cookie.rs:42:34:42:39 | array2 | test_cookie.rs:42:14:42:32 | ...::from | provenance | MaD:2 Sink:MaD:2 |
|
||||
| test_cookie.rs:49:9:49:14 | array3 [element] | test_cookie.rs:53:34:53:39 | array3 [element] | provenance | |
|
||||
| test_cookie.rs:49:23:49:25 | 0u8 | test_cookie.rs:49:23:49:29 | ...::from_elem(...) [element] | provenance | MaD:18 |
|
||||
| test_cookie.rs:49:23:49:25 | 0u8 | test_cookie.rs:49:23:49:29 | ...::from_elem(...) [element] | provenance | MaD:14 |
|
||||
| test_cookie.rs:49:23:49:29 | ...::from_elem(...) [element] | test_cookie.rs:49:9:49:14 | array3 [element] | provenance | |
|
||||
| test_cookie.rs:53:34:53:39 | array3 [element] | test_cookie.rs:53:14:53:32 | ...::from | provenance | MaD:2 Sink:MaD:2 |
|
||||
| test_heuristic.rs:44:9:44:16 | const_iv [&ref] | test_heuristic.rs:45:41:45:48 | const_iv | provenance | |
|
||||
| test_heuristic.rs:44:30:44:38 | &... [&ref] | test_heuristic.rs:44:9:44:16 | const_iv [&ref] | provenance | |
|
||||
| test_heuristic.rs:44:31:44:38 | [0u8; 16] | test_heuristic.rs:44:30:44:38 | &... [&ref] | provenance | |
|
||||
| test_heuristic.rs:64:20:64:27 | [0u8; 16] | test_heuristic.rs:64:19:64:27 | &... | provenance | |
|
||||
| test_heuristic.rs:65:31:65:38 | [0u8; 16] | test_heuristic.rs:65:30:65:38 | &... | provenance | |
|
||||
| test_heuristic.rs:69:32:69:32 | 1 | test_heuristic.rs:69:22:69:32 | ... + ... | provenance | MaD:8 |
|
||||
| test_heuristic.rs:70:23:70:35 | ... << ... | test_heuristic.rs:70:22:70:62 | ... ^ ... | provenance | MaD:10 |
|
||||
| test_heuristic.rs:70:34:70:35 | 32 | test_heuristic.rs:70:23:70:35 | ... << ... | provenance | MaD:11 |
|
||||
| test_heuristic.rs:70:41:70:61 | ... & ... | test_heuristic.rs:70:22:70:62 | ... ^ ... | provenance | MaD:10 |
|
||||
| test_heuristic.rs:70:52:70:61 | 0xFFFFFFFF | test_heuristic.rs:70:41:70:61 | ... & ... | provenance | MaD:9 |
|
||||
| test_heuristic.rs:38:25:38:30 | 0xFFFF | test_heuristic.rs:81:22:81:31 | MY_CONST_1 | provenance | |
|
||||
| test_heuristic.rs:39:25:39:59 | ... as u64 | test_heuristic.rs:82:22:82:31 | MY_CONST_2 | provenance | |
|
||||
| test_heuristic.rs:39:62:40:33 | static MY_STATIC_3 | test_heuristic.rs:83:22:83:32 | MY_STATIC_3 | provenance | |
|
||||
| test_heuristic.rs:40:27:40:32 | 0xFFFF | test_heuristic.rs:39:62:40:33 | static MY_STATIC_3 | provenance | |
|
||||
| test_heuristic.rs:49:9:49:16 | const_iv [&ref] | test_heuristic.rs:50:41:50:48 | const_iv | provenance | |
|
||||
| test_heuristic.rs:49:30:49:38 | &... [&ref] | test_heuristic.rs:49:9:49:16 | const_iv [&ref] | provenance | |
|
||||
| test_heuristic.rs:49:31:49:38 | [0u8; 16] | test_heuristic.rs:49:30:49:38 | &... [&ref] | provenance | |
|
||||
| test_heuristic.rs:69:20:69:27 | [0u8; 16] | test_heuristic.rs:69:19:69:27 | &... | provenance | |
|
||||
| test_heuristic.rs:70:31:70:38 | [0u8; 16] | test_heuristic.rs:70:30:70:38 | &... | provenance | |
|
||||
| test_heuristic.rs:86:29:86:32 | 1u64 | test_heuristic.rs:87:22:87:31 | MY_CONST_5 | provenance | |
|
||||
| test_heuristic.rs:88:29:88:33 | ... + ... | test_heuristic.rs:89:22:89:31 | MY_CONST_6 | provenance | |
|
||||
models
|
||||
| 1 | Sink: <_ as crypto_common::KeyInit>::new_from_slice; Argument[0]; credentials-key |
|
||||
| 2 | Sink: <biscotti::crypto::master::Key>::from; Argument[0]; credentials-key |
|
||||
@@ -92,17 +98,13 @@ models
|
||||
| 5 | Sink: <cipher::stream_wrapper::StreamCipherCoreWrapper as crypto_common::KeyIvInit>::new; Argument[1]; credentials-iv |
|
||||
| 6 | Sink: <cookie::secure::key::Key>::from; Argument[0].Reference; credentials-key |
|
||||
| 7 | Source: core::mem::zeroed; ReturnValue.Element; constant-source |
|
||||
| 8 | Summary: <_ as core::ops::arith::Add>::add; Argument[self,0]; ReturnValue; taint |
|
||||
| 9 | Summary: <_ as core::ops::bit::BitAnd>::bitand; Argument[self,0]; ReturnValue; taint |
|
||||
| 10 | Summary: <_ as core::ops::bit::BitXor>::bitxor; Argument[self,0]; ReturnValue; taint |
|
||||
| 11 | Summary: <_ as core::ops::bit::Shl>::shl; Argument[self,0]; ReturnValue; taint |
|
||||
| 12 | Summary: <alloc::vec::Vec as core::convert::From>::from; Argument[0].Field[alloc::borrow::Cow::Owned(0)]; ReturnValue; value |
|
||||
| 13 | Summary: <alloc::vec::Vec as core::convert::From>::from; Argument[0].Field[alloc::bstr::ByteString(0)]; ReturnValue; value |
|
||||
| 14 | Summary: <alloc::vec::Vec as core::convert::From>::from; Argument[0].Field[alloc::collections::binary_heap::BinaryHeap::data]; ReturnValue; value |
|
||||
| 15 | Summary: <alloc::vec::Vec as core::convert::From>::from; Argument[0].Field[alloc::string::String::vec]; ReturnValue; value |
|
||||
| 16 | Summary: <alloc::vec::Vec as core::convert::From>::from; Argument[0]; ReturnValue; taint |
|
||||
| 17 | Summary: <generic_array::GenericArray>::from_slice; Argument[0].Reference; ReturnValue.Reference; value |
|
||||
| 18 | Summary: alloc::vec::from_elem; Argument[0]; ReturnValue.Element; value |
|
||||
| 8 | Summary: <alloc::vec::Vec as core::convert::From>::from; Argument[0].Field[alloc::borrow::Cow::Owned(0)]; ReturnValue; value |
|
||||
| 9 | Summary: <alloc::vec::Vec as core::convert::From>::from; Argument[0].Field[alloc::bstr::ByteString(0)]; ReturnValue; value |
|
||||
| 10 | Summary: <alloc::vec::Vec as core::convert::From>::from; Argument[0].Field[alloc::collections::binary_heap::BinaryHeap::data]; ReturnValue; value |
|
||||
| 11 | Summary: <alloc::vec::Vec as core::convert::From>::from; Argument[0].Field[alloc::string::String::vec]; ReturnValue; value |
|
||||
| 12 | Summary: <alloc::vec::Vec as core::convert::From>::from; Argument[0]; ReturnValue; taint |
|
||||
| 13 | Summary: <generic_array::GenericArray>::from_slice; Argument[0].Reference; ReturnValue.Reference; value |
|
||||
| 14 | Summary: alloc::vec::from_elem; Argument[0]; ReturnValue.Element; value |
|
||||
nodes
|
||||
| test_cipher.rs:18:9:18:14 | const1 [&ref] | semmle.label | const1 [&ref] |
|
||||
| test_cipher.rs:18:28:18:36 | &... [&ref] | semmle.label | &... [&ref] |
|
||||
@@ -166,21 +168,28 @@ nodes
|
||||
| test_cookie.rs:49:23:49:29 | ...::from_elem(...) [element] | semmle.label | ...::from_elem(...) [element] |
|
||||
| test_cookie.rs:53:14:53:32 | ...::from | semmle.label | ...::from |
|
||||
| test_cookie.rs:53:34:53:39 | array3 [element] | semmle.label | array3 [element] |
|
||||
| test_heuristic.rs:44:9:44:16 | const_iv [&ref] | semmle.label | const_iv [&ref] |
|
||||
| test_heuristic.rs:44:30:44:38 | &... [&ref] | semmle.label | &... [&ref] |
|
||||
| test_heuristic.rs:44:31:44:38 | [0u8; 16] | semmle.label | [0u8; 16] |
|
||||
| test_heuristic.rs:45:41:45:48 | const_iv | semmle.label | const_iv |
|
||||
| test_heuristic.rs:63:30:63:37 | "secret" | semmle.label | "secret" |
|
||||
| test_heuristic.rs:64:19:64:27 | &... | semmle.label | &... |
|
||||
| test_heuristic.rs:64:20:64:27 | [0u8; 16] | semmle.label | [0u8; 16] |
|
||||
| test_heuristic.rs:65:30:65:38 | &... | semmle.label | &... |
|
||||
| test_heuristic.rs:65:31:65:38 | [0u8; 16] | semmle.label | [0u8; 16] |
|
||||
| test_heuristic.rs:67:22:67:22 | 0 | semmle.label | 0 |
|
||||
| test_heuristic.rs:69:22:69:32 | ... + ... | semmle.label | ... + ... |
|
||||
| test_heuristic.rs:69:32:69:32 | 1 | semmle.label | 1 |
|
||||
| test_heuristic.rs:70:22:70:62 | ... ^ ... | semmle.label | ... ^ ... |
|
||||
| test_heuristic.rs:70:23:70:35 | ... << ... | semmle.label | ... << ... |
|
||||
| test_heuristic.rs:70:34:70:35 | 32 | semmle.label | 32 |
|
||||
| test_heuristic.rs:70:41:70:61 | ... & ... | semmle.label | ... & ... |
|
||||
| test_heuristic.rs:70:52:70:61 | 0xFFFFFFFF | semmle.label | 0xFFFFFFFF |
|
||||
| test_heuristic.rs:38:25:38:30 | 0xFFFF | semmle.label | 0xFFFF |
|
||||
| test_heuristic.rs:39:25:39:59 | ... as u64 | semmle.label | ... as u64 |
|
||||
| test_heuristic.rs:39:62:40:33 | static MY_STATIC_3 | semmle.label | static MY_STATIC_3 |
|
||||
| test_heuristic.rs:40:27:40:32 | 0xFFFF | semmle.label | 0xFFFF |
|
||||
| test_heuristic.rs:49:9:49:16 | const_iv [&ref] | semmle.label | const_iv [&ref] |
|
||||
| test_heuristic.rs:49:30:49:38 | &... [&ref] | semmle.label | &... [&ref] |
|
||||
| test_heuristic.rs:49:31:49:38 | [0u8; 16] | semmle.label | [0u8; 16] |
|
||||
| test_heuristic.rs:50:41:50:48 | const_iv | semmle.label | const_iv |
|
||||
| test_heuristic.rs:68:30:68:37 | "secret" | semmle.label | "secret" |
|
||||
| test_heuristic.rs:69:19:69:27 | &... | semmle.label | &... |
|
||||
| test_heuristic.rs:69:20:69:27 | [0u8; 16] | semmle.label | [0u8; 16] |
|
||||
| test_heuristic.rs:70:30:70:38 | &... | semmle.label | &... |
|
||||
| test_heuristic.rs:70:31:70:38 | [0u8; 16] | semmle.label | [0u8; 16] |
|
||||
| test_heuristic.rs:72:22:72:22 | 0 | semmle.label | 0 |
|
||||
| test_heuristic.rs:76:22:76:27 | ... << ... | semmle.label | ... << ... |
|
||||
| test_heuristic.rs:78:22:78:29 | ...::MAX | semmle.label | ...::MAX |
|
||||
| test_heuristic.rs:79:22:79:33 | ... / ... | semmle.label | ... / ... |
|
||||
| test_heuristic.rs:81:22:81:31 | MY_CONST_1 | semmle.label | MY_CONST_1 |
|
||||
| test_heuristic.rs:82:22:82:31 | MY_CONST_2 | semmle.label | MY_CONST_2 |
|
||||
| test_heuristic.rs:83:22:83:32 | MY_STATIC_3 | semmle.label | MY_STATIC_3 |
|
||||
| test_heuristic.rs:86:29:86:32 | 1u64 | semmle.label | 1u64 |
|
||||
| test_heuristic.rs:87:22:87:31 | MY_CONST_5 | semmle.label | MY_CONST_5 |
|
||||
| test_heuristic.rs:88:29:88:33 | ... + ... | semmle.label | ... + ... |
|
||||
| test_heuristic.rs:89:22:89:31 | MY_CONST_6 | semmle.label | MY_CONST_6 |
|
||||
subpaths
|
||||
|
||||
@@ -35,6 +35,11 @@ impl MyCryptor {
|
||||
}
|
||||
}
|
||||
|
||||
const MY_CONST_1: u64 = 0xFFFF; // $ Alert[rust/hard-coded-cryptographic-value]
|
||||
const MY_CONST_2: u64 = std::env::consts::ARCH.len() as u64; // $ Alert[rust/hard-coded-cryptographic-value]
|
||||
static MY_STATIC_3: u64 = 0xFFFF; // $ Alert[rust/hard-coded-cryptographic-value]
|
||||
static MY_STATIC_4: u64 = std::env::consts::ARCH.len() as u64;
|
||||
|
||||
fn test(var_string: &str, var_data: &[u8;16], var_u64: u64) {
|
||||
encrypt_with("plaintext", var_data, var_data);
|
||||
|
||||
@@ -66,6 +71,32 @@ fn test(var_string: &str, var_data: &[u8;16], var_u64: u64) {
|
||||
|
||||
mc2.set_salt_u64(0); // $ Alert[rust/hard-coded-cryptographic-value]
|
||||
mc2.set_salt_u64(var_u64);
|
||||
mc2.set_salt_u64(var_u64 + 1); // $ SPURIOUS: Alert[rust/hard-coded-cryptographic-value]
|
||||
mc2.set_salt_u64((var_u64 << 32) ^ (var_u64 & 0xFFFFFFFF)); // $ SPURIOUS: Alert[rust/hard-coded-cryptographic-value]
|
||||
mc2.set_salt_u64(var_u64 + 1);
|
||||
mc2.set_salt_u64((var_u64 << 32) ^ (var_u64 & 0xFFFFFFFF));
|
||||
mc2.set_salt_u64(1 << 4); // $ Alert[rust/hard-coded-cryptographic-value]
|
||||
|
||||
mc2.set_salt_u64(u64::MAX); // $ Alert[rust/hard-coded-cryptographic-value]
|
||||
mc2.set_salt_u64(u64::MAX / 4); // $ Alert[rust/hard-coded-cryptographic-value]
|
||||
|
||||
mc2.set_salt_u64(MY_CONST_1); // $ Sink[rust/hard-coded-cryptographic-value]
|
||||
mc2.set_salt_u64(MY_CONST_2); // $ Sink[rust/hard-coded-cryptographic-value]
|
||||
mc2.set_salt_u64(MY_STATIC_3); // $ Sink[rust/hard-coded-cryptographic-value]
|
||||
mc2.set_salt_u64(MY_STATIC_4);
|
||||
|
||||
const MY_CONST_5: u64 = 1u64; // $ Alert[rust/hard-coded-cryptographic-value]
|
||||
mc2.set_salt_u64(MY_CONST_5); // $ Sink[rust/hard-coded-cryptographic-value]
|
||||
const MY_CONST_6: u64 = 2 + 3; // $ Alert[rust/hard-coded-cryptographic-value]
|
||||
mc2.set_salt_u64(MY_CONST_6); // $ Sink[rust/hard-coded-cryptographic-value]
|
||||
|
||||
let mut key1 = "foo".to_string(); // $ MISSING: Alert[rust/hard-coded-cryptographic-value]
|
||||
key1 += "bar"; // $ MISSING: Alert[rust/hard-coded-cryptographic-value]
|
||||
let _ = MyCryptor::new(&key1);
|
||||
|
||||
let mut key2 = "foo".to_string();
|
||||
key2 += var_string;
|
||||
let _ = MyCryptor::new(&key2);
|
||||
|
||||
let mut key3 = var_string.to_string();
|
||||
key3 += "bar";
|
||||
let _ = MyCryptor::new(&key3);
|
||||
}
|
||||
|
||||
@@ -54,6 +54,24 @@ signature module InputSig<LocationSig Location, DF::InputSig<Location> Lang> {
|
||||
none()
|
||||
}
|
||||
|
||||
/**
|
||||
* A base class of calls that are candidates for flow summary modeling.
|
||||
*/
|
||||
class FlowSummaryCallBase {
|
||||
string toString();
|
||||
}
|
||||
|
||||
/** Gets a call that targets summarized callable `sc`. */
|
||||
default FlowSummaryCallBase getASourceCall(SummarizedCallableBase sc) { none() }
|
||||
|
||||
/** Gets the callable corresponding to summarized callable `c`. */
|
||||
default Lang::DataFlowCallable getSummarizedCallableAsDataFlowCallable(SummarizedCallableBase c) {
|
||||
none()
|
||||
}
|
||||
|
||||
/** Gets the enclosing callable of `call`. */
|
||||
default Lang::DataFlowCallable getSourceCallEnclosingCallable(FlowSummaryCallBase call) { none() }
|
||||
|
||||
/** Gets the parameter position representing a callback itself, if any. */
|
||||
default Lang::ArgumentPosition callbackSelfParameterPosition() { none() }
|
||||
|
||||
@@ -74,6 +92,9 @@ signature module InputSig<LocationSig Location, DF::InputSig<Location> Lang> {
|
||||
result = getStandardReturnValueKind()
|
||||
}
|
||||
|
||||
/** Gets the parameter position corresponding to a flow-summary return kind `rk`, if any. */
|
||||
default Lang::ParameterPosition getFlowSummaryParameterPosition(Lang::ReturnKind rk) { none() }
|
||||
|
||||
/** Gets the textual representation of parameter position `pos` used in MaD. */
|
||||
string encodeParameterPosition(Lang::ParameterPosition pos);
|
||||
|
||||
@@ -660,6 +681,10 @@ module Make<
|
||||
s.length() = 1 and
|
||||
s.head() instanceof TArgumentSummaryComponent
|
||||
or
|
||||
// ReturnValue.*
|
||||
s.length() = 1 and
|
||||
s.head() instanceof TReturnSummaryComponent
|
||||
or
|
||||
// Argument[n].ReturnValue.*
|
||||
s.length() = 2 and
|
||||
s.head() instanceof TReturnSummaryComponent and
|
||||
@@ -1137,6 +1162,13 @@ module Make<
|
||||
outputState(c, s) and s = SummaryComponentStack::argument(_)
|
||||
}
|
||||
|
||||
private predicate relevantFlowSummaryPosition(SummarizedCallable c, ReturnKind rk) {
|
||||
exists(SummaryComponentStack input |
|
||||
summary(c, input, _, _, _) and
|
||||
input = TSingletonSummaryComponentStack(TReturnSummaryComponent(rk))
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate sourceOutputStateEntry(
|
||||
SourceElement source, SummaryComponentStack s, string kind, string model
|
||||
@@ -1272,6 +1304,12 @@ module Make<
|
||||
TSummaryParameterNode(SummarizedCallable c, ParameterPosition pos) {
|
||||
summaryParameterNodeRange(c, pos)
|
||||
} or
|
||||
TSummaryReturnArgumentNode(FlowSummaryCallBase call, ReturnKind rk) {
|
||||
exists(SummarizedCallable sc |
|
||||
call = getASourceCall(sc) and
|
||||
relevantFlowSummaryPosition(sc, rk)
|
||||
)
|
||||
} or
|
||||
TSourceOutputNode(SourceElement source, SummaryNodeState state, string kind, string model) {
|
||||
state.isSourceOutputState(source, _, kind, model)
|
||||
} or
|
||||
@@ -1321,6 +1359,40 @@ module Make<
|
||||
override SinkElement getSinkElement() { none() }
|
||||
}
|
||||
|
||||
private class SummaryReturnArgumentNode extends SummaryNode, TSummaryReturnArgumentNode {
|
||||
private FlowSummaryCallBase call;
|
||||
private ReturnKind rk;
|
||||
|
||||
SummaryReturnArgumentNode() { this = TSummaryReturnArgumentNode(call, rk) }
|
||||
|
||||
override string toString() { result = "[summary] value written to " + rk + " at " + call }
|
||||
|
||||
override SummarizedCallable getSummarizedCallable() { none() }
|
||||
|
||||
override SourceElement getSourceElement() { none() }
|
||||
|
||||
override SinkElement getSinkElement() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the summary node that represents the argument node used to transfer
|
||||
* flow into the caller when a value is written to the value returned by
|
||||
* `call` with kind `rk`.
|
||||
*/
|
||||
SummaryNode summaryArgumentNode(FlowSummaryCallBase call, ReturnKind rk) {
|
||||
result = TSummaryReturnArgumentNode(call, rk)
|
||||
}
|
||||
|
||||
/** Gets the enclosing callable for summary node `sn`. */
|
||||
DataFlowCallable getEnclosingCallable(SummaryNode sn) {
|
||||
result = getSummarizedCallableAsDataFlowCallable(sn.getSummarizedCallable())
|
||||
or
|
||||
exists(FlowSummaryCallBase call |
|
||||
sn = TSummaryReturnArgumentNode(call, _) and
|
||||
result = getSourceCallEnclosingCallable(call)
|
||||
)
|
||||
}
|
||||
|
||||
class SourceOutputNode extends SummaryNode, TSourceOutputNode {
|
||||
private SourceElement source_;
|
||||
private SummaryNodeState state_;
|
||||
@@ -1427,6 +1499,12 @@ module Make<
|
||||
SummarizedCallable c, SummaryNodeState state, ParameterPosition pos
|
||||
) {
|
||||
state.isInputState(c, SummaryComponentStack::argument(pos))
|
||||
or
|
||||
exists(ReturnKind rk |
|
||||
relevantFlowSummaryPosition(c, rk) and
|
||||
state.isInputState(c, SummaryComponentStack::return(rk)) and
|
||||
pos = getFlowSummaryParameterPosition(rk)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1560,6 +1638,9 @@ module Make<
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if return kind `rk` is a relevant return kind for flow summary modeling. */
|
||||
predicate relevantFlowSummaryPosition(ReturnKind rk) { relevantFlowSummaryPosition(_, rk) }
|
||||
|
||||
/**
|
||||
* Holds if flow is allowed to pass from the parameter at position `pos` of `c`,
|
||||
* to a return node, and back out to the parameter.
|
||||
@@ -1736,9 +1817,15 @@ module Make<
|
||||
}
|
||||
|
||||
signature module StepsInputSig {
|
||||
/** Gets the summary node represented by data-flow node `n`, if any. */
|
||||
SummaryNode getSummaryNode(Node n);
|
||||
|
||||
/** Gets a call that targets summarized callable `sc`. */
|
||||
DataFlowCall getACall(SummarizedCallable sc);
|
||||
|
||||
/** Gets the out node of kind `rk` for `call`, if any. */
|
||||
default Node getSourceOutNode(FlowSummaryCallBase call, ReturnKind rk) { none() }
|
||||
|
||||
/** Gets the enclosing callable of `source`. */
|
||||
DataFlowCallable getSourceNodeEnclosingCallable(SourceBase source);
|
||||
|
||||
@@ -1765,7 +1852,7 @@ module Make<
|
||||
* Holds if there is a local step from `pred` to `succ`, which is synthesized
|
||||
* from a flow summary.
|
||||
*/
|
||||
predicate summaryLocalStep(
|
||||
private predicate summaryLocalStepImpl(
|
||||
SummaryNode pred, SummaryNode succ, boolean preservesValue, string model
|
||||
) {
|
||||
exists(
|
||||
@@ -1811,9 +1898,24 @@ module Make<
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if there is a local step between data-flow nodes synthesized from a flow summary. */
|
||||
predicate summaryLocalStep(Node pred, SummaryNode succ, boolean preservesValue, string model) {
|
||||
exists(SummaryNode predSummary |
|
||||
predSummary = StepsInput::getSummaryNode(pred) and
|
||||
summaryLocalStepImpl(predSummary, succ, preservesValue, model)
|
||||
)
|
||||
or
|
||||
exists(FlowSummaryCallBase summaryCall, ReturnKind rk, SummarizedCallable sc |
|
||||
pred = StepsInput::getSourceOutNode(summaryCall, rk) and
|
||||
summaryCall = getASourceCall(sc) and
|
||||
summary(sc, SummaryComponentStack::return(rk), _, preservesValue, model) and
|
||||
succ = TSummaryReturnArgumentNode(summaryCall, rk)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if the value of `succ` is uniquely determined by the value of `pred`. */
|
||||
predicate summaryLocalMustFlowStep(SummaryNode pred, SummaryNode succ) {
|
||||
pred = unique(SummaryNode n1 | summaryLocalStep(n1, succ, true, _))
|
||||
pred = unique(SummaryNode n1 | summaryLocalStepImpl(n1, succ, true, _))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1935,7 +2037,7 @@ module Make<
|
||||
or
|
||||
exists(SummaryNode mid, boolean clearsOrExpectsMid |
|
||||
paramReachesLocal(p, mid, clearsOrExpectsMid) and
|
||||
summaryLocalStep(mid, n, true, _) and
|
||||
summaryLocalStepImpl(mid, n, true, _) and
|
||||
if
|
||||
summaryClearsContent(n, _) or
|
||||
summaryExpectsContent(n, _)
|
||||
@@ -1993,7 +2095,7 @@ module Make<
|
||||
*/
|
||||
predicate summaryThroughStepValue(ArgNode arg, Node out, SummarizedCallable sc) {
|
||||
exists(SummaryNode ret |
|
||||
summaryLocalStep(summaryArgParamRetOut(arg, ret, out, sc), ret, true, _)
|
||||
summaryLocalStepImpl(summaryArgParamRetOut(arg, ret, out, sc), ret, true, _)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2006,7 +2108,7 @@ module Make<
|
||||
*/
|
||||
predicate summaryThroughStepTaint(ArgNode arg, Node out, SummarizedCallable sc) {
|
||||
exists(SummaryNode ret |
|
||||
summaryLocalStep(summaryArgParamRetOut(arg, ret, out, sc), ret, false, _)
|
||||
summaryLocalStepImpl(summaryArgParamRetOut(arg, ret, out, sc), ret, false, _)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2020,7 +2122,7 @@ module Make<
|
||||
predicate summaryGetterStep(ArgNode arg, ContentSet c, Node out, SummarizedCallable sc) {
|
||||
exists(SummaryNode mid, SummaryNode ret |
|
||||
summaryReadStep(summaryArgParamRetOut(arg, ret, out, sc), c, mid) and
|
||||
summaryLocalStep(mid, ret, _, _)
|
||||
summaryLocalStepImpl(mid, ret, _, _)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2033,7 +2135,7 @@ module Make<
|
||||
*/
|
||||
predicate summarySetterStep(ArgNode arg, ContentSet c, Node out, SummarizedCallable sc) {
|
||||
exists(SummaryNode mid, SummaryNode ret |
|
||||
summaryLocalStep(summaryArgParamRetOut(arg, ret, out, sc), mid, _, _) and
|
||||
summaryLocalStepImpl(summaryArgParamRetOut(arg, ret, out, sc), mid, _, _) and
|
||||
summaryStoreStep(mid, c, ret)
|
||||
)
|
||||
}
|
||||
@@ -2749,9 +2851,11 @@ module Make<
|
||||
key = "semmle.label" and val = n.toString()
|
||||
}
|
||||
|
||||
private Node getNode(SummaryNode sn) { sn = StepsInput::getSummaryNode(result) }
|
||||
|
||||
private predicate edgesComponent(NodeOrCall a, NodeOrCall b, string value) {
|
||||
exists(boolean preservesValue |
|
||||
PrivateSteps::summaryLocalStep(a.asNode(), b.asNode(), preservesValue, _) and
|
||||
PrivateSteps::summaryLocalStep(getNode(a.asNode()), b.asNode(), preservesValue, _) and
|
||||
if preservesValue = true then value = "value" else value = "taint"
|
||||
)
|
||||
or
|
||||
|
||||
@@ -1346,6 +1346,14 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
|
||||
module MatchingWithEnvironment<MatchingWithEnvironmentInputSig Input> {
|
||||
private import Input
|
||||
|
||||
pragma[nomagic]
|
||||
private TypeParameter getDeclTypeParameter(Declaration decl, TypeArgumentPosition tapos) {
|
||||
exists(TypeParameterPosition tppos |
|
||||
result = decl.getTypeParameter(tppos) and
|
||||
typeArgumentParameterPositionMatch(tapos, tppos)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of the type argument at `path` in `a` that corresponds to
|
||||
* the type parameter `tp` in `target`, if any.
|
||||
@@ -1356,11 +1364,11 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
|
||||
*/
|
||||
bindingset[a, target]
|
||||
pragma[inline_late]
|
||||
private Type getTypeArgument(Access a, Declaration target, TypeParameter tp, TypePath path) {
|
||||
exists(TypeArgumentPosition tapos, TypeParameterPosition tppos |
|
||||
Type getTypeArgument(Access a, Declaration target, TypeParameter tp, TypePath path) {
|
||||
exists(TypeArgumentPosition tapos |
|
||||
result = a.getTypeArgument(tapos, path) and
|
||||
tp = target.getTypeParameter(tppos) and
|
||||
typeArgumentParameterPositionMatch(tapos, tppos)
|
||||
tp = getDeclTypeParameter(target, tapos) and
|
||||
not isPseudoType(result)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1526,42 +1534,35 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
|
||||
|
||||
private module AccessConstraint {
|
||||
private predicate relevantAccessConstraint(
|
||||
Access a, AccessEnvironment e, Declaration target, AccessPosition apos, TypePath path,
|
||||
Access a, AccessEnvironment e, Declaration target, TypeParameter constrainedTp,
|
||||
TypeMention constraint
|
||||
) {
|
||||
target = a.getTarget(e) and
|
||||
typeParameterHasConstraint(target, apos, _, path, constraint)
|
||||
typeParameterHasConstraint(target, constrainedTp, constraint)
|
||||
}
|
||||
|
||||
private newtype TRelevantAccess =
|
||||
MkRelevantAccess(Access a, AccessPosition apos, AccessEnvironment e, TypePath path) {
|
||||
relevantAccessConstraint(a, e, _, apos, path, _)
|
||||
MkRelevantAccess(Access a, AccessEnvironment e, TypeParameter constrainedTp) {
|
||||
relevantAccessConstraint(a, e, _, constrainedTp, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* If the access `a` for `apos`, environment `e`, and `path` has an inferred type
|
||||
* which type inference requires to satisfy some constraint.
|
||||
*/
|
||||
private class RelevantAccess extends MkRelevantAccess {
|
||||
Access a;
|
||||
AccessPosition apos;
|
||||
AccessEnvironment e;
|
||||
TypePath path;
|
||||
TypeParameter constrainedTp;
|
||||
|
||||
RelevantAccess() { this = MkRelevantAccess(a, apos, e, path) }
|
||||
RelevantAccess() { this = MkRelevantAccess(a, e, constrainedTp) }
|
||||
|
||||
pragma[nomagic]
|
||||
Type getTypeAt(TypePath suffix) {
|
||||
result = a.getInferredType(e, apos, path.appendInverse(suffix))
|
||||
}
|
||||
Type getTypeAt(TypePath path) { typeMatch(a, e, _, path, result, constrainedTp) }
|
||||
|
||||
/** Gets the constraint that this relevant access should satisfy. */
|
||||
TypeMention getConstraint(Declaration target) {
|
||||
relevantAccessConstraint(a, e, target, apos, path, result)
|
||||
relevantAccessConstraint(a, e, target, constrainedTp, result)
|
||||
}
|
||||
|
||||
string toString() {
|
||||
result = a.toString() + ", " + apos.toString() + ", " + path.toString()
|
||||
result = a.toString() + ", " + e.toString() + ", " + constrainedTp.toString()
|
||||
}
|
||||
|
||||
Location getLocation() { result = a.getLocation() }
|
||||
@@ -1577,7 +1578,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
|
||||
class TypeMatchingContext = Access;
|
||||
|
||||
TypeMatchingContext getTypeMatchingContext(RelevantAccess at) {
|
||||
at = MkRelevantAccess(result, _, _, _)
|
||||
at = MkRelevantAccess(result, _, _)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1591,41 +1592,32 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
|
||||
SatisfiesTypeParameterConstraintInput>;
|
||||
|
||||
pragma[nomagic]
|
||||
predicate satisfiesConstraintAtTypeParameter(
|
||||
predicate argSatisfiesConstraintAtTypeParameter(
|
||||
Access a, AccessEnvironment e, Declaration target, AccessPosition apos, TypePath prefix,
|
||||
TypeMention constraint, TypePath pathToTypeParamInConstraint,
|
||||
TypePath pathToTypeParamInSub
|
||||
) {
|
||||
exists(RelevantAccess ra |
|
||||
ra = MkRelevantAccess(a, apos, e, prefix) and
|
||||
exists(RelevantAccess ra, TypeParameter constrainedTp |
|
||||
ra = MkRelevantAccess(a, e, constrainedTp) and
|
||||
relevantAccessConstraint(a, e, target, constrainedTp, constraint) and
|
||||
SatisfiesTypeParameterConstraint::satisfiesConstraintAtTypeParameter(ra, constraint,
|
||||
pathToTypeParamInConstraint, pathToTypeParamInSub) and
|
||||
constraint = ra.getConstraint(target)
|
||||
exists(DeclarationPosition dpos |
|
||||
accessDeclarationPositionMatch(apos, dpos) and
|
||||
constrainedTp = target.getDeclaredType(dpos, prefix)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate satisfiesConstraint(
|
||||
Access a, AccessEnvironment e, Declaration target, AccessPosition apos, TypePath prefix,
|
||||
Access a, AccessEnvironment e, Declaration target, TypeParameter constrainedTp,
|
||||
TypeMention constraint, TypePath path, Type t
|
||||
) {
|
||||
exists(RelevantAccess ra |
|
||||
ra = MkRelevantAccess(a, apos, e, prefix) and
|
||||
SatisfiesTypeParameterConstraint::satisfiesConstraint(ra, constraint, path, t) and
|
||||
constraint = ra.getConstraint(target)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
predicate satisfiesConstraintThrough(
|
||||
Access a, AccessEnvironment e, Declaration target, AccessPosition apos, TypePath prefix,
|
||||
TypeAbstraction abs, TypeMention constraint, TypePath path, Type t
|
||||
) {
|
||||
exists(RelevantAccess ra |
|
||||
ra = MkRelevantAccess(a, apos, e, prefix) and
|
||||
SatisfiesTypeParameterConstraint::satisfiesConstraintThrough(ra, abs, constraint, path,
|
||||
t) and
|
||||
constraint = ra.getConstraint(target)
|
||||
ra = MkRelevantAccess(a, e, constrainedTp) and
|
||||
relevantAccessConstraint(a, e, target, constrainedTp, constraint) and
|
||||
SatisfiesTypeParameterConstraint::satisfiesConstraint(ra, constraint, path, t)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1644,51 +1636,44 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the type parameter `constrainedTp` occurs in the declared type of
|
||||
* `target` at `apos` and `pathToConstrained`, and there is a constraint
|
||||
* `constraint` on `constrainedTp`.
|
||||
* Holds if the type parameter `constrainedTp` applies to `target` and the
|
||||
* constraint `constraint` applies to `constrainedTp`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate typeParameterHasConstraint(
|
||||
Declaration target, AccessPosition apos, TypeParameter constrainedTp,
|
||||
TypePath pathToConstrained, TypeMention constraint
|
||||
Declaration target, TypeParameter constrainedTp, TypeMention constraint
|
||||
) {
|
||||
exists(DeclarationPosition dpos |
|
||||
accessDeclarationPositionMatch(apos, dpos) and
|
||||
constrainedTp = target.getTypeParameter(_) and
|
||||
constrainedTp = target.getDeclaredType(dpos, pathToConstrained) and
|
||||
constraint = getATypeParameterConstraint(constrainedTp, target)
|
||||
)
|
||||
constrainedTp = target.getTypeParameter(_) and
|
||||
constraint = getATypeParameterConstraint(constrainedTp, target)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the declared type of `target` contains a type parameter at
|
||||
* `apos` and `pathToConstrained` that must satisfy `constraint` and `tp`
|
||||
* occurs at `pathToTp` in `constraint`.
|
||||
* Holds if the type parameter `constrainedTp` applies to `target`, the
|
||||
* constraint `constraint` applies to `constrainedTp`, and type parameter
|
||||
* `tp` occurs at `pathToTp` in `constraint`.
|
||||
*
|
||||
* For example, in
|
||||
*
|
||||
* ```csharp
|
||||
* interface IFoo<A> { }
|
||||
* T1 M<T1, T2>(T2 item) where T2 : IFoo<T1> { }
|
||||
* ```
|
||||
* with the method declaration being the target and with `apos`
|
||||
* corresponding to `item`, we have the following
|
||||
* - `pathToConstrained = ""`,
|
||||
* - `tp = T1`,
|
||||
*
|
||||
* with the method declaration being the target, we have the following
|
||||
* - `constrainedTp = T2`,
|
||||
* - `constraint = IFoo`,
|
||||
* - `tp = T1`, and
|
||||
* - `pathToTp = "A"`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate typeParameterConstraintHasTypeParameter(
|
||||
Declaration target, AccessPosition apos, TypePath pathToConstrained, TypeMention constraint,
|
||||
TypePath pathToTp, TypeParameter tp
|
||||
Declaration target, TypeParameter constrainedTp, TypeMention constraint, TypePath pathToTp,
|
||||
TypeParameter tp
|
||||
) {
|
||||
exists(TypeParameter constrainedTp |
|
||||
typeParameterHasConstraint(target, apos, constrainedTp, pathToConstrained, constraint) and
|
||||
tp = target.getTypeParameter(_) and
|
||||
tp = constraint.getTypeAt(pathToTp) and
|
||||
constrainedTp != tp
|
||||
)
|
||||
typeParameterHasConstraint(target, constrainedTp, constraint) and
|
||||
tp = target.getTypeParameter(_) and
|
||||
tp = constraint.getTypeAt(pathToTp) and
|
||||
constrainedTp != tp
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -1696,9 +1681,9 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
|
||||
Access a, AccessEnvironment e, Declaration target, TypePath path, Type t, TypeParameter tp
|
||||
) {
|
||||
not exists(getTypeArgument(a, target, tp, _)) and
|
||||
exists(TypeMention constraint, AccessPosition apos, TypePath pathToTp, TypePath pathToTp2 |
|
||||
typeParameterConstraintHasTypeParameter(target, apos, pathToTp2, constraint, pathToTp, tp) and
|
||||
AccessConstraint::satisfiesConstraint(a, e, target, apos, pathToTp2, constraint,
|
||||
exists(TypeMention constraint, TypeParameter constrainedTp, TypePath pathToTp |
|
||||
typeParameterConstraintHasTypeParameter(target, constrainedTp, constraint, pathToTp, tp) and
|
||||
AccessConstraint::satisfiesConstraint(a, e, target, constrainedTp, constraint,
|
||||
pathToTp.appendInverse(path), t)
|
||||
)
|
||||
}
|
||||
@@ -1781,7 +1766,7 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
|
||||
Declaration target, TypePath prefix, TypeMention constraint,
|
||||
TypePath pathToTypeParamInConstraint, TypePath pathToTypeParamInSub
|
||||
|
|
||||
AccessConstraint::satisfiesConstraintAtTypeParameter(a, e, target, apos, prefix,
|
||||
AccessConstraint::argSatisfiesConstraintAtTypeParameter(a, e, target, apos, prefix,
|
||||
constraint, pathToTypeParamInConstraint, pathToTypeParamInSub)
|
||||
|
|
||||
exists(TypePath suffix |
|
||||
@@ -1847,7 +1832,12 @@ module Make1<LocationSig Location, InputSig1<Location> Input1> {
|
||||
*/
|
||||
|
||||
typeMatch(a, e, target, suffix, result, tp) and
|
||||
typeParameterConstraintHasTypeParameter(target, apos, _, constraint, pathToTp, tp) and
|
||||
exists(TypeParameter constrainedTp, DeclarationPosition dpos |
|
||||
typeParameterConstraintHasTypeParameter(target, constrainedTp, constraint, pathToTp,
|
||||
tp) and
|
||||
accessDeclarationPositionMatch(apos, dpos) and
|
||||
constrainedTp = target.getDeclaredType(dpos, _)
|
||||
) and
|
||||
pathToTp = pathToTypeParamInConstraint.appendInverse(mid) and
|
||||
path = prefix.append(pathToTypeParamInSub.append(mid).append(suffix))
|
||||
)
|
||||
|
||||
@@ -305,7 +305,7 @@ private module Cached {
|
||||
model = ""
|
||||
or
|
||||
// flow through a flow summary (extension of `SummaryModelCsv`)
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom.(FlowSummaryNode).getSummaryNode(),
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom,
|
||||
nodeTo.(FlowSummaryNode).getSummaryNode(), true, model)
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@ module Input implements InputSig<Location, DataFlowImplSpecific::SwiftDataFlow>
|
||||
|
||||
class SinkBase = Void;
|
||||
|
||||
class FlowSummaryCallBase = Void;
|
||||
|
||||
predicate callableFromSource(SummarizedCallableBase c) { c.hasBody() }
|
||||
|
||||
ArgumentPosition callbackSelfParameterPosition() { result instanceof ThisArgumentPosition }
|
||||
@@ -113,6 +115,10 @@ module Input implements InputSig<Location, DataFlowImplSpecific::SwiftDataFlow>
|
||||
private import Make<Location, DataFlowImplSpecific::SwiftDataFlow, Input> as Impl
|
||||
|
||||
private module StepsInput implements Impl::Private::StepsInputSig {
|
||||
Impl::Private::SummaryNode getSummaryNode(Node n) {
|
||||
result = n.(FlowSummaryNode).getSummaryNode()
|
||||
}
|
||||
|
||||
DataFlowCall getACall(Public::SummarizedCallable sc) { result.asCall().getStaticTarget() = sc }
|
||||
|
||||
DataFlowCallable getSourceNodeEnclosingCallable(Input::SourceBase source) { none() }
|
||||
|
||||
@@ -76,7 +76,7 @@ private module Cached {
|
||||
model = ""
|
||||
or
|
||||
// flow through a flow summary (extension of `SummaryModelCsv`)
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom.(FlowSummaryNode).getSummaryNode(),
|
||||
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom,
|
||||
nodeTo.(FlowSummaryNode).getSummaryNode(), false, model)
|
||||
or
|
||||
any(AdditionalTaintStep a).step(nodeFrom, nodeTo) and model = "AdditionalTaintStep"
|
||||
|
||||
2
unified/swift-syntax-rs/.gitignore
vendored
Normal file
2
unified/swift-syntax-rs/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/target
|
||||
/swift/.build
|
||||
1
unified/swift-syntax-rs/.swift-version
Normal file
1
unified/swift-syntax-rs/.swift-version
Normal file
@@ -0,0 +1 @@
|
||||
6.2.4
|
||||
55
unified/swift-syntax-rs/BUILD.bazel
Normal file
55
unified/swift-syntax-rs/BUILD.bazel
Normal file
@@ -0,0 +1,55 @@
|
||||
load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_library", "rust_test")
|
||||
load("@rules_swift//swift:swift.bzl", "swift_library")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
# The pinned Swift version, shared with the local `cargo` build and
|
||||
# `swift/Package.swift`. Referenced by the `swift.toolchain` extension in
|
||||
# //:MODULE.bazel via `swift_version_file`.
|
||||
exports_files([".swift-version"])
|
||||
|
||||
# The Swift FFI shim: wraps swift-syntax and exposes a small C ABI
|
||||
# (`ssr_parse_json` / `ssr_string_free`). Built with the hermetic Swift
|
||||
# toolchain registered in //:MODULE.bazel; provides a `CcInfo` that the Rust
|
||||
# targets below link against.
|
||||
swift_library(
|
||||
name = "swift_syntax_ffi",
|
||||
srcs = ["swift/Sources/SwiftSyntaxFFI/SwiftSyntaxFFI.swift"],
|
||||
module_name = "SwiftSyntaxFFI",
|
||||
deps = [
|
||||
"@swift-syntax//:SwiftParser",
|
||||
"@swift-syntax//:SwiftSyntax",
|
||||
],
|
||||
)
|
||||
|
||||
# Safe Rust bindings on top of the C ABI. `build.rs` is intentionally *not*
|
||||
# wired up here (no `cargo_build_script`): under Bazel the Swift side is
|
||||
# provided by `:swift_syntax_ffi`, whereas `build.rs` only exists to build the
|
||||
# Swift shim in the local `cargo` workflow.
|
||||
rust_library(
|
||||
name = "swift_syntax_rs",
|
||||
srcs = ["src/lib.rs"],
|
||||
edition = "2024",
|
||||
deps = [":swift_syntax_ffi"],
|
||||
)
|
||||
|
||||
rust_binary(
|
||||
name = "swift-syntax-parse",
|
||||
srcs = ["src/main.rs"],
|
||||
# The Swift toolchain propagates a runfiles-relative RPATH to its runtime
|
||||
# `.so`s via `swift_syntax_ffi`'s `CcInfo`, but (unlike `swift_binary`)
|
||||
# `rust_binary` does not copy those libraries into runfiles. Add the
|
||||
# toolchain files as `data` so the runtime is found at execution time.
|
||||
# (rules_swift does not yet support statically linking the runtime.)
|
||||
data = ["@swift_toolchain_ubuntu24.04//:files"],
|
||||
edition = "2024",
|
||||
deps = [":swift_syntax_rs"],
|
||||
)
|
||||
|
||||
rust_test(
|
||||
name = "swift_syntax_rs_test",
|
||||
size = "small",
|
||||
crate = ":swift_syntax_rs",
|
||||
data = ["@swift_toolchain_ubuntu24.04//:files"],
|
||||
edition = "2024",
|
||||
)
|
||||
14
unified/swift-syntax-rs/Cargo.toml
Normal file
14
unified/swift-syntax-rs/Cargo.toml
Normal file
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "swift-syntax-rs"
|
||||
description = "Rust wrapper around the swift-syntax package for parsing Swift source"
|
||||
version = "0.1.0"
|
||||
authors = ["GitHub"]
|
||||
edition = "2024"
|
||||
|
||||
[lib]
|
||||
name = "swift_syntax_rs"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "swift-syntax-parse"
|
||||
path = "src/main.rs"
|
||||
180
unified/swift-syntax-rs/README.md
Normal file
180
unified/swift-syntax-rs/README.md
Normal file
@@ -0,0 +1,180 @@
|
||||
# swift-syntax-rs
|
||||
|
||||
A Rust wrapper around the [swift-syntax](https://github.com/swiftlang/swift-syntax)
|
||||
package, allowing Swift source code to be parsed from Rust.
|
||||
|
||||
Parsing is delegated to a small Swift shim (in [`swift/`](swift/)) that links
|
||||
against `SwiftSyntax`/`SwiftParser` and exposes a tiny C ABI. The Rust crate
|
||||
builds that shim (via `build.rs`) and provides safe bindings on top of it.
|
||||
|
||||
## Output format
|
||||
|
||||
The emitted JSON tree preserves the AST's named structure. Every node has a
|
||||
`kind` and a `range` with `start`/`end` positions (UTF-8 `offset` plus 1-based
|
||||
`line`/`column`). Beyond that:
|
||||
|
||||
- **Tokens** carry `text`, `tokenKind`, and `leadingTrivia`/`trailingTrivia`
|
||||
arrays of `{ kind, text }` pieces.
|
||||
- **Layout nodes** (e.g. `functionDecl`) embed their children directly as
|
||||
members keyed by the child's name in the parent (`name`, `signature`,
|
||||
`body`, …), alongside `kind`/`range`. Absent optional children are omitted.
|
||||
- **Collection nodes** (e.g. `codeBlockItemList`) are elided: a list-valued
|
||||
field is simply a JSON array of its elements (e.g. `parameters`, `statements`).
|
||||
This drops the collection node's own `kind`/`range`.
|
||||
|
||||
Only meaningful trivia is kept — the four comment kinds (`lineComment`,
|
||||
`blockComment`, `docLineComment`, `docBlockComment`) and `unexpectedText`
|
||||
(source the parser skipped). Whitespace trivia is dropped, since node ranges
|
||||
already encode positions.
|
||||
|
||||
### Example
|
||||
|
||||
Parsing `let x = 1 // c` produces the following (each `range` object is
|
||||
abbreviated here as `…`):
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"kind": "sourceFile",
|
||||
"range": …,
|
||||
"statements": [ // collection node elided to an array
|
||||
{
|
||||
"kind": "codeBlockItem",
|
||||
"range": …,
|
||||
"item": {
|
||||
"kind": "variableDecl",
|
||||
"range": …,
|
||||
"attributes": [], // empty collection → empty array
|
||||
"modifiers": [],
|
||||
"bindingSpecifier": { // a token
|
||||
"kind": "token",
|
||||
"text": "let",
|
||||
"tokenKind": "keyword(SwiftSyntax.Keyword.let)",
|
||||
"range": …,
|
||||
"leadingTrivia": [],
|
||||
"trailingTrivia": []
|
||||
},
|
||||
"bindings": [
|
||||
{
|
||||
"kind": "patternBinding",
|
||||
"range": …,
|
||||
"pattern": {
|
||||
"kind": "identifierPattern",
|
||||
"range": …,
|
||||
"identifier": { "kind": "token", "text": "x", "tokenKind": "identifier(\"x\")", "range": …, "leadingTrivia": [], "trailingTrivia": [] }
|
||||
},
|
||||
"initializer": {
|
||||
"kind": "initializerClause",
|
||||
"range": …,
|
||||
"equal": { "kind": "token", "text": "=", "tokenKind": "equal", "range": …, "leadingTrivia": [], "trailingTrivia": [] },
|
||||
"value": {
|
||||
"kind": "integerLiteralExpr",
|
||||
"range": …,
|
||||
"literal": {
|
||||
"kind": "token",
|
||||
"text": "1",
|
||||
"tokenKind": "integerLiteral(\"1\")",
|
||||
"range": …,
|
||||
"leadingTrivia": [],
|
||||
"trailingTrivia": [ { "kind": "lineComment", "text": "// c" } ]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"endOfFileToken": { "kind": "token", "text": "", "tokenKind": "endOfFile", "range": …, "leadingTrivia": [], "trailingTrivia": [] }
|
||||
}
|
||||
```
|
||||
|
||||
Note how `statements`, `bindings`, `attributes`, and `modifiers` are plain
|
||||
arrays (their collection nodes are elided), layout children such as
|
||||
`bindingSpecifier` and `initializer` are embedded by name, and the `// c`
|
||||
comment rides along as `trailingTrivia` on the token it follows.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
The build does not depend on any particular version manager. You need:
|
||||
|
||||
- **Rust** — pinned to `1.88` by the repo-root [`rust-toolchain.toml`](../../rust-toolchain.toml),
|
||||
which `rustup` picks up automatically.
|
||||
- **Swift** — pinned to the version in [`.swift-version`](.swift-version)
|
||||
(currently `6.2.4`), used to build `swift-syntax` `602.0.0`. Install it any way
|
||||
you like — [swift.org](https://www.swift.org/install/) or
|
||||
[swiftly](https://www.swift.org/swiftly/) (which reads `.swift-version`), or a
|
||||
system package. Just make sure `swift` is on your `PATH` (or point `build.rs`
|
||||
at it with the `SWIFT` environment variable).
|
||||
|
||||
On Debian/Ubuntu the Swift runtime also needs `libncurses6` (and related libs)
|
||||
available on the system.
|
||||
|
||||
## Building & testing
|
||||
|
||||
With `cargo` and `swift` on `PATH`:
|
||||
|
||||
```sh
|
||||
cargo build
|
||||
cargo test
|
||||
```
|
||||
|
||||
If your `swift`/`swiftc` are not on `PATH`, point the build at them explicitly:
|
||||
|
||||
```sh
|
||||
SWIFT=/path/to/swift SWIFTC=/path/to/swiftc cargo build
|
||||
```
|
||||
|
||||
The first build compiles `swift-syntax` and can take several minutes.
|
||||
|
||||
## Building with Bazel (CI)
|
||||
|
||||
CI builds this crate hermetically with Bazel. A Swift toolchain is downloaded
|
||||
from swift.org by the official `rules_swift` standalone toolchain extension
|
||||
(wired up in the repo-root `MODULE.bazel`), `swift-syntax` is pulled from the
|
||||
Bazel Central Registry, and the FFI shim is compiled as a `swift_library` that
|
||||
the Rust targets link against. `build.rs` is not used under Bazel; it only
|
||||
builds the Swift shim for the local `cargo` workflow.
|
||||
|
||||
```sh
|
||||
bazel build //unified/swift-syntax-rs:swift-syntax-parse
|
||||
bazel test //unified/swift-syntax-rs:swift_syntax_rs_test
|
||||
bazel run //unified/swift-syntax-rs:swift-syntax-parse < some.swift
|
||||
```
|
||||
|
||||
Requirements:
|
||||
|
||||
- **`clang`** must be installed on the runner. `rules_swift` requires the Bazel
|
||||
CC toolchain to use clang; the repo's `.bazelrc` already sets
|
||||
`--repo_env=CC=clang`, so no extra flags are needed.
|
||||
- The registered Swift toolchain currently targets **ubuntu24.04 / x86_64**
|
||||
only (Bazel cannot auto-select between Linux distributions). Add more
|
||||
platforms in `MODULE.bazel` (`swift.toolchain` + `register_toolchains`) if CI
|
||||
grows to cover them.
|
||||
|
||||
The Swift compiler version is read from [`.swift-version`](.swift-version) by
|
||||
both the Bazel toolchain (`swift.toolchain(swift_version_file = …)`) and the
|
||||
local build, and is kept in sync with the `swift-syntax` release pinned in
|
||||
`swift/Package.swift`, so local and CI builds behave identically.
|
||||
|
||||
## Usage
|
||||
|
||||
Library:
|
||||
|
||||
```rust
|
||||
let json = swift_syntax_rs::parse_to_json("let x = 1")?;
|
||||
println!("{json}");
|
||||
```
|
||||
|
||||
CLI (reads a file argument or stdin, prints the syntax tree as JSON):
|
||||
|
||||
```sh
|
||||
echo 'let x = 1' | cargo run --bin swift-syntax-parse
|
||||
```
|
||||
|
||||
## Layout
|
||||
|
||||
- `swift/` — Swift package exposing the `ssr_parse_json` / `ssr_string_free` C ABI.
|
||||
- `build.rs` — builds the Swift package and emits link/rpath flags (local `cargo` only).
|
||||
- `BUILD.bazel` — Bazel targets for the hermetic CI build (swift_library + rust targets).
|
||||
- `src/lib.rs` — safe Rust bindings (`parse_to_json`).
|
||||
- `src/main.rs` — demo CLI.
|
||||
102
unified/swift-syntax-rs/build.rs
Normal file
102
unified/swift-syntax-rs/build.rs
Normal file
@@ -0,0 +1,102 @@
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
|
||||
fn main() {
|
||||
let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
|
||||
let swift_dir = manifest_dir.join("swift");
|
||||
|
||||
println!(
|
||||
"cargo:rerun-if-changed={}",
|
||||
swift_dir.join("Package.swift").display()
|
||||
);
|
||||
println!(
|
||||
"cargo:rerun-if-changed={}",
|
||||
swift_dir
|
||||
.join("Sources/SwiftSyntaxFFI/SwiftSyntaxFFI.swift")
|
||||
.display()
|
||||
);
|
||||
println!("cargo:rerun-if-env-changed=SWIFT");
|
||||
println!("cargo:rerun-if-env-changed=SWIFTC");
|
||||
|
||||
// Build the Swift FFI package as a release dynamic library.
|
||||
let mut command = Command::new(swift_bin());
|
||||
command
|
||||
.args(["build", "-c", "release"])
|
||||
.current_dir(&swift_dir);
|
||||
apply_bare_repository_workaround(&mut command);
|
||||
let status = command.status().unwrap_or_else(|e| {
|
||||
panic!(
|
||||
"failed to run `{swift} build`: {e}\n\
|
||||
Install a Swift toolchain (see https://www.swift.org/install/, e.g. via \
|
||||
swiftly) and ensure `swift` is on PATH, or set the `SWIFT` environment \
|
||||
variable to the `swift` executable. The pinned version is in `.swift-version`.",
|
||||
swift = swift_bin(),
|
||||
)
|
||||
});
|
||||
assert!(status.success(), "`swift build` failed");
|
||||
|
||||
// Link against the freshly built dynamic library.
|
||||
let build_dir = swift_dir.join(".build/release");
|
||||
println!("cargo:rustc-link-search=native={}", build_dir.display());
|
||||
println!("cargo:rustc-link-lib=dylib=SwiftSyntaxFFI");
|
||||
println!("cargo:rustc-link-arg=-Wl,-rpath,{}", build_dir.display());
|
||||
|
||||
// The executable also needs to find the Swift runtime libraries at run time.
|
||||
if let Some(runtime) = swift_runtime_dir() {
|
||||
println!("cargo:rustc-link-search=native={}", runtime.display());
|
||||
println!("cargo:rustc-link-arg=-Wl,-rpath,{}", runtime.display());
|
||||
}
|
||||
}
|
||||
|
||||
/// Query the active Swift toolchain for the directory containing its runtime
|
||||
/// shared libraries (e.g. `libswiftCore.so`).
|
||||
fn swift_runtime_dir() -> Option<PathBuf> {
|
||||
let output = Command::new(swiftc_bin())
|
||||
.arg("-print-target-info")
|
||||
.output()
|
||||
.ok()?;
|
||||
if !output.status.success() {
|
||||
return None;
|
||||
}
|
||||
let info = String::from_utf8_lossy(&output.stdout);
|
||||
|
||||
// Extract the value of `"runtimeResourcePath": "..."` without pulling in a
|
||||
// JSON dependency.
|
||||
let key = "\"runtimeResourcePath\"";
|
||||
let start = info.find(key)?;
|
||||
let rest = &info[start + key.len()..];
|
||||
let colon = rest.find(':')?;
|
||||
let after = &rest[colon + 1..];
|
||||
let open = after.find('"')?;
|
||||
let value_start = &after[open + 1..];
|
||||
let close = value_start.find('"')?;
|
||||
let resource_path = &value_start[..close];
|
||||
|
||||
Some(PathBuf::from(resource_path).join("linux"))
|
||||
}
|
||||
|
||||
/// The `swift` driver to invoke: `$SWIFT` if set, otherwise `swift` from `PATH`.
|
||||
/// This keeps the build tool-agnostic — any Swift install works; no particular
|
||||
/// version manager is required.
|
||||
fn swift_bin() -> String {
|
||||
env::var("SWIFT").unwrap_or_else(|_| "swift".to_string())
|
||||
}
|
||||
|
||||
/// The `swiftc` compiler to invoke: `$SWIFTC` if set, otherwise `swiftc` from
|
||||
/// `PATH`.
|
||||
fn swiftc_bin() -> String {
|
||||
env::var("SWIFTC").unwrap_or_else(|_| "swiftc".to_string())
|
||||
}
|
||||
|
||||
/// Some environments (notably GitHub Codespaces) inject
|
||||
/// `GIT_CONFIG_KEY_0=safe.bareRepository` / `GIT_CONFIG_VALUE_0=explicit`, which
|
||||
/// breaks the cached bare git repositories `swift build` uses. When exactly that
|
||||
/// key is present, relax it to `all` for the `swift build` subprocess only
|
||||
/// (rather than unconditionally, which could clobber an unrelated
|
||||
/// `GIT_CONFIG_VALUE_0`).
|
||||
fn apply_bare_repository_workaround(command: &mut Command) {
|
||||
if env::var("GIT_CONFIG_KEY_0").as_deref() == Ok("safe.bareRepository") {
|
||||
command.env("GIT_CONFIG_VALUE_0", "all");
|
||||
}
|
||||
}
|
||||
125
unified/swift-syntax-rs/src/lib.rs
Normal file
125
unified/swift-syntax-rs/src/lib.rs
Normal file
@@ -0,0 +1,125 @@
|
||||
//! Rust wrapper around the [swift-syntax](https://github.com/swiftlang/swift-syntax)
|
||||
//! package, allowing Swift source code to be parsed from Rust.
|
||||
//!
|
||||
//! The heavy lifting is done by a small Swift shim (see `swift/`) that links
|
||||
//! against `SwiftSyntax`/`SwiftParser` and exposes a tiny C ABI. This module
|
||||
//! provides safe Rust bindings on top of that ABI.
|
||||
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::os::raw::c_char;
|
||||
|
||||
// C ABI exported by the `SwiftSyntaxFFI` dynamic library.
|
||||
unsafe extern "C" {
|
||||
/// Parse a NUL-terminated Swift source string, returning a heap-allocated
|
||||
/// NUL-terminated JSON string (or null on failure). The caller owns the
|
||||
/// returned pointer and must release it with `ssr_string_free`.
|
||||
fn ssr_parse_json(source: *const c_char) -> *mut c_char;
|
||||
|
||||
/// Free a string previously returned by `ssr_parse_json`.
|
||||
fn ssr_string_free(ptr: *mut c_char);
|
||||
}
|
||||
|
||||
/// Errors that can occur while parsing Swift source.
|
||||
#[derive(Debug)]
|
||||
pub enum ParseError {
|
||||
/// The provided source contained an interior NUL byte.
|
||||
NulByte,
|
||||
/// The Swift side failed to produce a result.
|
||||
SwiftFailure,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ParseError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ParseError::NulByte => write!(f, "source contained an interior NUL byte"),
|
||||
ParseError::SwiftFailure => write!(f, "swift-syntax failed to parse the source"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for ParseError {}
|
||||
|
||||
/// Parse the given Swift `source` and return a JSON representation of its
|
||||
/// syntax tree.
|
||||
///
|
||||
/// # Example
|
||||
/// ```no_run
|
||||
/// let json = swift_syntax_rs::parse_to_json("let x = 1").unwrap();
|
||||
/// println!("{json}");
|
||||
/// ```
|
||||
pub fn parse_to_json(source: &str) -> Result<String, ParseError> {
|
||||
let c_source = CString::new(source).map_err(|_| ParseError::NulByte)?;
|
||||
|
||||
// SAFETY: `c_source` is a valid NUL-terminated string for the duration of
|
||||
// the call. The returned pointer, if non-null, is owned by us and freed via
|
||||
// `ssr_string_free` before returning.
|
||||
unsafe {
|
||||
let ptr = ssr_parse_json(c_source.as_ptr());
|
||||
if ptr.is_null() {
|
||||
return Err(ParseError::SwiftFailure);
|
||||
}
|
||||
let json = CStr::from_ptr(ptr).to_string_lossy().into_owned();
|
||||
ssr_string_free(ptr);
|
||||
Ok(json)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn parses_simple_declaration() {
|
||||
let json = parse_to_json("let x = 1").expect("parsing should succeed");
|
||||
assert!(
|
||||
json.contains("\"kind\":\"sourceFile\""),
|
||||
"unexpected tree: {json}"
|
||||
);
|
||||
assert!(json.contains("\"text\":\"x\""), "unexpected tree: {json}");
|
||||
// Source ranges are emitted for every node.
|
||||
assert!(json.contains("\"range\""), "missing ranges: {json}");
|
||||
assert!(
|
||||
json.contains("\"line\"") && json.contains("\"column\"") && json.contains("\"offset\""),
|
||||
"missing location fields: {json}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn preserves_named_structure() {
|
||||
let json = parse_to_json("let x = 1").expect("parsing should succeed");
|
||||
// Layout children are embedded directly under their field name.
|
||||
assert!(
|
||||
json.contains("\"initializer\""),
|
||||
"missing initializer field: {json}"
|
||||
);
|
||||
// Collection-valued fields are elided to plain JSON arrays.
|
||||
assert!(
|
||||
json.contains("\"statements\":["),
|
||||
"statements should be an array: {json}"
|
||||
);
|
||||
assert!(
|
||||
json.contains("\"bindings\":["),
|
||||
"bindings should be an array: {json}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_empty_source() {
|
||||
let json = parse_to_json("").expect("parsing empty source should succeed");
|
||||
assert!(
|
||||
json.contains("\"kind\":\"sourceFile\""),
|
||||
"unexpected tree: {json}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn captures_trivia() {
|
||||
// A leading comment is kept as trivia on the token it precedes.
|
||||
let json = parse_to_json("// hi\nlet x = 1").expect("parsing should succeed");
|
||||
assert!(json.contains("\"leadingTrivia\""), "missing trivia: {json}");
|
||||
assert!(
|
||||
json.contains("lineComment"),
|
||||
"comment trivia not captured: {json}"
|
||||
);
|
||||
}
|
||||
}
|
||||
42
unified/swift-syntax-rs/src/main.rs
Normal file
42
unified/swift-syntax-rs/src/main.rs
Normal file
@@ -0,0 +1,42 @@
|
||||
//! Small demo CLI: parse Swift source and print the syntax tree as JSON.
|
||||
//!
|
||||
//! Usage:
|
||||
//! swift-syntax-parse [FILE]
|
||||
//!
|
||||
//! Reads from FILE if given, otherwise from standard input.
|
||||
|
||||
use std::io::Read;
|
||||
use std::process::ExitCode;
|
||||
|
||||
fn main() -> ExitCode {
|
||||
let source = match read_source() {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
eprintln!("error reading source: {e}");
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
};
|
||||
|
||||
match swift_syntax_rs::parse_to_json(&source) {
|
||||
Ok(json) => {
|
||||
println!("{json}");
|
||||
ExitCode::SUCCESS
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("error: {e}");
|
||||
ExitCode::FAILURE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn read_source() -> std::io::Result<String> {
|
||||
let mut args = std::env::args().skip(1);
|
||||
match args.next() {
|
||||
Some(path) => std::fs::read_to_string(path),
|
||||
None => {
|
||||
let mut buf = String::new();
|
||||
std::io::stdin().read_to_string(&mut buf)?;
|
||||
Ok(buf)
|
||||
}
|
||||
}
|
||||
}
|
||||
15
unified/swift-syntax-rs/swift/Package.resolved
Normal file
15
unified/swift-syntax-rs/swift/Package.resolved
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"originHash" : "ad84ad340ee01aa6e38b877dd29e38fa9cb40603dfdefa4e6a69e27f2db208d1",
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "swift-syntax",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/swiftlang/swift-syntax.git",
|
||||
"state" : {
|
||||
"revision" : "4799286537280063c85a32f09884cfbca301b1a1",
|
||||
"version" : "602.0.0"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 3
|
||||
}
|
||||
30
unified/swift-syntax-rs/swift/Package.swift
Normal file
30
unified/swift-syntax-rs/swift/Package.swift
Normal file
@@ -0,0 +1,30 @@
|
||||
// swift-tools-version:6.0
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "SwiftSyntaxFFI",
|
||||
products: [
|
||||
// Dynamic library so the produced .so records its dependency on the
|
||||
// Swift runtime libraries, keeping the Rust link step simple.
|
||||
.library(
|
||||
name: "SwiftSyntaxFFI",
|
||||
type: .dynamic,
|
||||
targets: ["SwiftSyntaxFFI"]
|
||||
)
|
||||
],
|
||||
dependencies: [
|
||||
.package(
|
||||
url: "https://github.com/swiftlang/swift-syntax.git",
|
||||
exact: "602.0.0"
|
||||
)
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
name: "SwiftSyntaxFFI",
|
||||
dependencies: [
|
||||
.product(name: "SwiftSyntax", package: "swift-syntax"),
|
||||
.product(name: "SwiftParser", package: "swift-syntax"),
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
@@ -0,0 +1,145 @@
|
||||
import Foundation
|
||||
import SwiftParser
|
||||
|
||||
// `@_spi(RawSyntax)` exposes the `childName(_:)` helper that maps a child's
|
||||
// key path to its field name in the parent layout; used to emit named fields.
|
||||
@_spi(RawSyntax) import SwiftSyntax
|
||||
|
||||
#if canImport(Glibc)
|
||||
import Glibc
|
||||
#elseif canImport(Darwin)
|
||||
import Darwin
|
||||
#endif
|
||||
|
||||
/// Convert an absolute position into an `{ offset, line, column }` dictionary.
|
||||
///
|
||||
/// `offset` is a UTF-8 byte offset; `line`/`column` are 1-based.
|
||||
private func location(
|
||||
_ position: AbsolutePosition,
|
||||
_ converter: SourceLocationConverter
|
||||
) -> [String: Any] {
|
||||
let loc = converter.location(for: position)
|
||||
return [
|
||||
"offset": position.utf8Offset,
|
||||
"line": loc.line,
|
||||
"column": loc.column,
|
||||
]
|
||||
}
|
||||
|
||||
/// Trivia kinds worth preserving in the serialized tree. Comments carry
|
||||
/// developer intent (including doc comments), and `unexpectedText` flags source
|
||||
/// the parser had to skip. Whitespace and multi-line-string escape markers are
|
||||
/// dropped: node ranges already encode positions, so they would only bloat the
|
||||
/// output.
|
||||
private let keptTriviaKinds: Set<String> = [
|
||||
"lineComment",
|
||||
"blockComment",
|
||||
"docLineComment",
|
||||
"docBlockComment",
|
||||
"unexpectedText",
|
||||
]
|
||||
|
||||
/// Serialize a trivia collection into an array of `{ kind, text }` pieces,
|
||||
/// keeping only the kinds in `keptTriviaKinds`.
|
||||
private func serializeTrivia(_ trivia: Trivia) -> [Any] {
|
||||
trivia.pieces.compactMap { piece -> [String: Any]? in
|
||||
// The label of an enum case mirror is the case name (e.g. "spaces",
|
||||
// "lineComment"), which gives us a stable kind without an exhaustive
|
||||
// switch over every `TriviaPiece` case.
|
||||
let kind = Mirror(reflecting: piece).children.first?.label ?? "\(piece)"
|
||||
guard keptTriviaKinds.contains(kind) else { return nil }
|
||||
return [
|
||||
"kind": kind,
|
||||
"text": Trivia(pieces: [piece]).description,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/// Recursively convert a SwiftSyntax node into a JSON-serializable value.
|
||||
///
|
||||
/// * Tokens and layout nodes (e.g. `functionDecl`) become objects carrying
|
||||
/// `kind` and source `range`. Layout nodes additionally embed their children
|
||||
/// directly as members keyed by the child's name in the parent (e.g. `name`,
|
||||
/// `signature`, `body`); absent optional children are omitted. Field names
|
||||
/// never collide with `kind`/`range`.
|
||||
/// * Collection nodes (e.g. `codeBlockItemList`) are *elided*: they become a
|
||||
/// plain array of their serialized elements, taking the place of the
|
||||
/// collection node itself. A list-valued layout field (e.g. `parameters`) is
|
||||
/// therefore simply a JSON array. This drops the collection node's own
|
||||
/// `kind`/`range`, which are unnamed and largely recoverable from the
|
||||
/// elements.
|
||||
private func serialize(
|
||||
_ node: Syntax,
|
||||
_ converter: SourceLocationConverter
|
||||
) -> Any {
|
||||
if node.kind.isSyntaxCollection {
|
||||
return node.children(viewMode: .sourceAccurate).map {
|
||||
serialize($0, converter)
|
||||
}
|
||||
}
|
||||
|
||||
// Source range covering the node's content, excluding surrounding trivia.
|
||||
let range: [String: Any] = [
|
||||
"start": location(node.positionAfterSkippingLeadingTrivia, converter),
|
||||
"end": location(node.endPositionBeforeTrailingTrivia, converter),
|
||||
]
|
||||
|
||||
if let token = node.as(TokenSyntax.self) {
|
||||
return [
|
||||
"kind": "token",
|
||||
"tokenKind": "\(token.tokenKind)",
|
||||
"text": token.text,
|
||||
"range": range,
|
||||
"leadingTrivia": serializeTrivia(token.leadingTrivia),
|
||||
"trailingTrivia": serializeTrivia(token.trailingTrivia),
|
||||
]
|
||||
}
|
||||
|
||||
var result: [String: Any] = [
|
||||
"kind": "\(node.kind)",
|
||||
"range": range,
|
||||
]
|
||||
var unnamed = 0
|
||||
for child in node.children(viewMode: .sourceAccurate) {
|
||||
// Layout children are named; embed each under its field name in the
|
||||
// parent (the same mechanism SwiftSyntax uses for its debug dump). A
|
||||
// child that is a collection serializes to an array (see above).
|
||||
if let keyPath = child.keyPathInParent, let name = childName(keyPath) {
|
||||
result[name] = serialize(child, converter)
|
||||
} else {
|
||||
// Defensive fallback for any unnamed layout child.
|
||||
result["child\(unnamed)"] = serialize(child, converter)
|
||||
unnamed += 1
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/// Parse the given NUL-terminated Swift source string and return a
|
||||
/// heap-allocated, NUL-terminated JSON representation of the syntax tree.
|
||||
///
|
||||
/// The returned pointer is owned by the caller and MUST be released with
|
||||
/// `ssr_string_free`. Returns `nil` on failure.
|
||||
@_cdecl("ssr_parse_json")
|
||||
public func ssr_parse_json(_ source: UnsafePointer<CChar>?) -> UnsafeMutablePointer<CChar>? {
|
||||
guard let source = source else { return nil }
|
||||
let code = String(cString: source)
|
||||
let tree = Parser.parse(source: code)
|
||||
let converter = SourceLocationConverter(fileName: "<input>", tree: tree)
|
||||
let json = serialize(Syntax(tree), converter)
|
||||
|
||||
guard
|
||||
let data = try? JSONSerialization.data(
|
||||
withJSONObject: json, options: [.sortedKeys]),
|
||||
let string = String(data: data, encoding: .utf8)
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
return strdup(string)
|
||||
}
|
||||
|
||||
/// Free a string previously returned by this library.
|
||||
@_cdecl("ssr_string_free")
|
||||
public func ssr_string_free(_ ptr: UnsafeMutablePointer<CChar>?) {
|
||||
free(ptr)
|
||||
}
|
||||
Reference in New Issue
Block a user