mirror of
https://github.com/github/codeql.git
synced 2026-05-05 13:45:19 +02:00
Merge branch 'main' into redsun82/swift-open-redirection
This commit is contained in:
@@ -3,7 +3,26 @@ load("//swift:rules.bzl", "swift_cc_library")
|
||||
swift_cc_library(
|
||||
name = "file",
|
||||
srcs = glob(["*.cpp"]),
|
||||
hdrs = glob(["*.h"]),
|
||||
hdrs = glob(["*.h"]) + [":path_hash_workaround"],
|
||||
visibility = ["//swift:__subpackages__"],
|
||||
deps = ["@picosha2"],
|
||||
)
|
||||
|
||||
genrule(
|
||||
name = "path_hash_workaround",
|
||||
srcs = [
|
||||
"PathHash.h.workaround",
|
||||
"PathHash.h.fixed",
|
||||
],
|
||||
outs = ["PathHash.h"],
|
||||
# see if https://cplusplus.github.io/LWG/issue3657 is fixed with the current compiler or not
|
||||
# if fixed, PathHash.h.workaround will not compile
|
||||
cmd = "\n".join([
|
||||
"if clang -c -x c++ -std=c++17 -Wno-pragma-once-outside-header \\",
|
||||
" $(rootpath PathHash.h.workaround) -o /dev/null &> /dev/null; then",
|
||||
" cp $(rootpath PathHash.h.workaround) $@",
|
||||
"else",
|
||||
" cp $(rootpath PathHash.h.fixed) $@",
|
||||
"fi",
|
||||
]),
|
||||
)
|
||||
|
||||
4
swift/extractor/infra/file/PathHash.h.fixed
Normal file
4
swift/extractor/infra/file/PathHash.h.fixed
Normal file
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
@@ -8,11 +8,10 @@
|
||||
// but it works, and this is recognized as a defect of the standard.
|
||||
// Using a non-standard Hasher type would be a huge pain, as the automatic hash implementation of
|
||||
// std::variant would not kick in (we use std::filesystem::path in a variant used as a map key).
|
||||
// We can reconsider when the fix to the above issue hits clang, which might require a version check
|
||||
// here
|
||||
namespace std {
|
||||
template <>
|
||||
struct std::hash<std::filesystem::path> {
|
||||
std::size_t operator()(const std::filesystem::path& path) const { return hash_value(path); }
|
||||
struct hash<filesystem::path> {
|
||||
size_t operator()(const filesystem::path& path) const { return hash_value(path); }
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
@@ -90,6 +90,7 @@ private module Frameworks {
|
||||
private import codeql.swift.frameworks.StandardLibrary.WebView
|
||||
private import codeql.swift.frameworks.Alamofire.Alamofire
|
||||
private import codeql.swift.security.PathInjection
|
||||
private import codeql.swift.security.PredicateInjection
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -134,7 +135,7 @@ predicate summaryModel(string row) { any(SummaryModelCsv s).row(row) }
|
||||
/** Holds if a source model exists for the given parameters. */
|
||||
predicate sourceModel(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string output, string kind, boolean generated
|
||||
string output, string kind, string provenance
|
||||
) {
|
||||
exists(string row |
|
||||
sourceModel(row) and
|
||||
@@ -148,13 +149,13 @@ predicate sourceModel(
|
||||
row.splitAt(";", 6) = output and
|
||||
row.splitAt(";", 7) = kind
|
||||
) and
|
||||
generated = false
|
||||
provenance = "manual"
|
||||
}
|
||||
|
||||
/** Holds if a sink model exists for the given parameters. */
|
||||
predicate sinkModel(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string input, string kind, boolean generated
|
||||
string input, string kind, string provenance
|
||||
) {
|
||||
exists(string row |
|
||||
sinkModel(row) and
|
||||
@@ -168,13 +169,13 @@ predicate sinkModel(
|
||||
row.splitAt(";", 6) = input and
|
||||
row.splitAt(";", 7) = kind
|
||||
) and
|
||||
generated = false
|
||||
provenance = "manual"
|
||||
}
|
||||
|
||||
/** Holds if a summary model exists for the given parameters. */
|
||||
predicate summaryModel(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string input, string output, string kind, boolean generated
|
||||
string input, string output, string kind, string provenance
|
||||
) {
|
||||
exists(string row |
|
||||
summaryModel(row) and
|
||||
@@ -189,7 +190,7 @@ predicate summaryModel(
|
||||
row.splitAt(";", 7) = output and
|
||||
row.splitAt(";", 8) = kind
|
||||
) and
|
||||
generated = false
|
||||
provenance = "manual"
|
||||
}
|
||||
|
||||
private predicate relevantNamespace(string namespace) {
|
||||
@@ -223,25 +224,25 @@ predicate modelCoverage(string namespace, int namespaces, string kind, string pa
|
||||
part = "source" and
|
||||
n =
|
||||
strictcount(string subns, string type, boolean subtypes, string name, string signature,
|
||||
string ext, string output, boolean generated |
|
||||
string ext, string output, string provenance |
|
||||
canonicalNamespaceLink(namespace, subns) and
|
||||
sourceModel(subns, type, subtypes, name, signature, ext, output, kind, generated)
|
||||
sourceModel(subns, type, subtypes, name, signature, ext, output, kind, provenance)
|
||||
)
|
||||
or
|
||||
part = "sink" and
|
||||
n =
|
||||
strictcount(string subns, string type, boolean subtypes, string name, string signature,
|
||||
string ext, string input, boolean generated |
|
||||
string ext, string input, string provenance |
|
||||
canonicalNamespaceLink(namespace, subns) and
|
||||
sinkModel(subns, type, subtypes, name, signature, ext, input, kind, generated)
|
||||
sinkModel(subns, type, subtypes, name, signature, ext, input, kind, provenance)
|
||||
)
|
||||
or
|
||||
part = "summary" and
|
||||
n =
|
||||
strictcount(string subns, string type, boolean subtypes, string name, string signature,
|
||||
string ext, string input, string output, boolean generated |
|
||||
string ext, string input, string output, string provenance |
|
||||
canonicalNamespaceLink(namespace, subns) and
|
||||
summaryModel(subns, type, subtypes, name, signature, ext, input, output, kind, generated)
|
||||
summaryModel(subns, type, subtypes, name, signature, ext, input, output, kind, provenance)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -165,8 +165,8 @@ module Ssa {
|
||||
class PhiDefinition extends Definition, SsaImpl::PhiNode {
|
||||
cached
|
||||
override Location getLocation() {
|
||||
exists(SsaInput::BasicBlock bb, int i |
|
||||
this.definesAt(_, bb, i) and
|
||||
exists(SsaInput::BasicBlock bb |
|
||||
this.definesAt(_, bb, _) and
|
||||
result = bb.getLocation()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -876,9 +876,9 @@ private module Stage1 implements StageSig {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate revFlowOut(ReturnPosition pos, Configuration config) {
|
||||
exists(DataFlowCall call, NodeEx out |
|
||||
exists(NodeEx out |
|
||||
revFlow(out, _, config) and
|
||||
viableReturnPosOutNodeCandFwd1(call, pos, out, config)
|
||||
viableReturnPosOutNodeCandFwd1(_, pos, out, config)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1731,8 +1731,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
)
|
||||
or
|
||||
// flow through a callable
|
||||
exists(DataFlowCall call, ParamNodeEx p, ReturnPosition pos, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, pos, returnAp, ap, innerReturnAp, config) and
|
||||
exists(DataFlowCall call, ParamNodeEx p, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, _, returnAp, ap, innerReturnAp, config) and
|
||||
flowThroughIntoCall(call, node, p, _, ap, innerReturnAp, config)
|
||||
)
|
||||
or
|
||||
@@ -1901,8 +1901,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
|
||||
pragma[nomagic]
|
||||
predicate parameterMayFlowThrough(ParamNodeEx p, Ap ap, Configuration config) {
|
||||
exists(RetNodeEx ret, ReturnPosition pos |
|
||||
returnFlowsThrough(ret, pos, _, _, p, ap, _, config) and
|
||||
exists(ReturnPosition pos |
|
||||
returnFlowsThrough(_, pos, _, _, p, ap, _, config) and
|
||||
parameterFlowsThroughRev(p, ap, pos, _, config)
|
||||
)
|
||||
}
|
||||
@@ -1923,8 +1923,8 @@ private module MkStage<StageSig PrevStage> {
|
||||
DataFlowCall call, ArgNodeEx arg, FlowState state, ReturnCtx returnCtx, ApOption returnAp,
|
||||
Ap ap, Configuration config
|
||||
) {
|
||||
exists(ParamNodeEx p, ReturnPosition pos, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, pos, returnAp, ap, innerReturnAp, config) and
|
||||
exists(ParamNodeEx p, Ap innerReturnAp |
|
||||
revFlowThrough(call, returnCtx, p, state, _, returnAp, ap, innerReturnAp, config) and
|
||||
flowThroughIntoCall(call, arg, p, _, ap, innerReturnAp, config)
|
||||
)
|
||||
}
|
||||
@@ -3749,8 +3749,8 @@ private predicate paramFlowsThrough(
|
||||
ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap,
|
||||
AccessPathApprox apa, Configuration config
|
||||
) {
|
||||
exists(PathNodeMid mid, RetNodeEx ret |
|
||||
pathNode(mid, ret, state, cc, sc, ap, config, _) and
|
||||
exists(RetNodeEx ret |
|
||||
pathNode(_, ret, state, cc, sc, ap, config, _) and
|
||||
kind = ret.getKind() and
|
||||
apa = ap.getApprox() and
|
||||
parameterFlowThroughAllowed(sc.getParamNode(), kind)
|
||||
@@ -4212,17 +4212,15 @@ private module FlowExploration {
|
||||
ap = TRevPartialNil() and
|
||||
exists(config.explorationLimit())
|
||||
or
|
||||
exists(PartialPathNodeRev mid |
|
||||
revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and
|
||||
not clearsContentEx(node, ap.getHead()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead())
|
||||
) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
)
|
||||
revPartialPathStep(_, node, state, sc1, sc2, sc3, ap, config) and
|
||||
not clearsContentEx(node, ap.getHead()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead())
|
||||
) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
distSink(node.getEnclosingCallable(), config) <= config.explorationLimit()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -4230,19 +4228,17 @@ private module FlowExploration {
|
||||
NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2,
|
||||
TSummaryCtx3 sc3, PartialAccessPath ap, Configuration config
|
||||
) {
|
||||
exists(PartialPathNodeFwd mid |
|
||||
partialPathStep(mid, node, state, cc, sc1, sc2, sc3, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
not clearsContentEx(node, ap.getHead().getContent()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead().getContent())
|
||||
) and
|
||||
if node.asNode() instanceof CastingNode
|
||||
then compatibleTypes(node.getDataFlowType(), ap.getType())
|
||||
else any()
|
||||
)
|
||||
partialPathStep(_, node, state, cc, sc1, sc2, sc3, ap, config) and
|
||||
not fullBarrier(node, config) and
|
||||
not stateBarrier(node, state, config) and
|
||||
not clearsContentEx(node, ap.getHead().getContent()) and
|
||||
(
|
||||
notExpectsContent(node) or
|
||||
expectsContentEx(node, ap.getHead().getContent())
|
||||
) and
|
||||
if node.asNode() instanceof CastingNode
|
||||
then compatibleTypes(node.getDataFlowType(), ap.getType())
|
||||
else any()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -101,9 +101,7 @@ module Consistency {
|
||||
exists(int c |
|
||||
c =
|
||||
strictcount(Node n |
|
||||
not exists(string filepath, int startline, int startcolumn, int endline, int endcolumn |
|
||||
n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
) and
|
||||
not n.hasLocationInfo(_, _, _, _, _) and
|
||||
not any(ConsistencyConfiguration conf).missingLocationExclude(n)
|
||||
) and
|
||||
msg = "Nodes without location: " + c
|
||||
|
||||
@@ -241,15 +241,19 @@ module Public {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the summary is auto generated and not manually generated.
|
||||
* Holds if all the summaries that apply to `this` are auto generated and not manually created.
|
||||
*/
|
||||
predicate isAutoGenerated() { none() }
|
||||
final predicate isAutoGenerated() { this.hasProvenance("generated") and not this.isManual() }
|
||||
|
||||
/**
|
||||
* Holds if the summary has the given provenance where `true` is
|
||||
* `generated` and `false` is `manual`.
|
||||
* Holds if there exists a manual summary that applies to `this`.
|
||||
*/
|
||||
predicate hasProvenance(boolean generated) { none() }
|
||||
final predicate isManual() { this.hasProvenance("manual") }
|
||||
|
||||
/**
|
||||
* Holds if there exists a summary that applies to `this` that has provenance `provenance`.
|
||||
*/
|
||||
predicate hasProvenance(string provenance) { none() }
|
||||
}
|
||||
|
||||
/** A callable where there is no flow via the callable. */
|
||||
@@ -257,15 +261,19 @@ module Public {
|
||||
NeutralCallable() { neutralElement(this, _) }
|
||||
|
||||
/**
|
||||
* Holds if the neutral is auto generated.
|
||||
* Holds if the neutral is auto generated.
|
||||
*/
|
||||
predicate isAutoGenerated() { neutralElement(this, true) }
|
||||
predicate isAutoGenerated() { neutralElement(this, "generated") }
|
||||
|
||||
/**
|
||||
* Holds if the neutral has the given provenance where `true` is
|
||||
* `generated` and `false` is `manual`.
|
||||
* Holds if there exists a manual neutral that applies to `this`.
|
||||
*/
|
||||
predicate hasProvenance(boolean generated) { neutralElement(this, generated) }
|
||||
final predicate isManual() { this.hasProvenance("manual") }
|
||||
|
||||
/**
|
||||
* Holds if the neutral has provenance `provenance`.
|
||||
*/
|
||||
predicate hasProvenance(string provenance) { neutralElement(this, provenance) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -580,10 +588,8 @@ module Private {
|
||||
head = TWithContentSummaryComponent(cont)
|
||||
)
|
||||
or
|
||||
exists(ContentSet cont |
|
||||
head = TWithoutContentSummaryComponent(cont) and
|
||||
result = getNodeType(summaryNodeInputState(c, s.tail()))
|
||||
)
|
||||
head = TWithoutContentSummaryComponent(_) and
|
||||
result = getNodeType(summaryNodeInputState(c, s.tail()))
|
||||
or
|
||||
exists(ReturnKind rk |
|
||||
head = TReturnSummaryComponent(rk) and
|
||||
@@ -658,8 +664,8 @@ module Private {
|
||||
|
||||
/** Holds if summary node `ret` is a return node of kind `rk`. */
|
||||
predicate summaryReturnNode(Node ret, ReturnKind rk) {
|
||||
exists(SummarizedCallable callable, SummaryComponentStack s |
|
||||
ret = summaryNodeOutputState(callable, s) and
|
||||
exists(SummaryComponentStack s |
|
||||
ret = summaryNodeOutputState(_, s) and
|
||||
s = TSingletonSummaryComponentStack(TReturnSummaryComponent(rk))
|
||||
)
|
||||
}
|
||||
@@ -999,12 +1005,12 @@ module Private {
|
||||
private predicate relevantSummaryElementGenerated(
|
||||
AccessPath inSpec, AccessPath outSpec, string kind
|
||||
) {
|
||||
summaryElement(this, inSpec, outSpec, kind, true) and
|
||||
not summaryElement(this, _, _, _, false)
|
||||
summaryElement(this, inSpec, outSpec, kind, "generated") and
|
||||
not summaryElement(this, _, _, _, "manual")
|
||||
}
|
||||
|
||||
private predicate relevantSummaryElement(AccessPath inSpec, AccessPath outSpec, string kind) {
|
||||
summaryElement(this, inSpec, outSpec, kind, false)
|
||||
summaryElement(this, inSpec, outSpec, kind, "manual")
|
||||
or
|
||||
this.relevantSummaryElementGenerated(inSpec, outSpec, kind)
|
||||
}
|
||||
@@ -1023,10 +1029,8 @@ module Private {
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isAutoGenerated() { this.relevantSummaryElementGenerated(_, _, _) }
|
||||
|
||||
override predicate hasProvenance(boolean generated) {
|
||||
summaryElement(this, _, _, _, generated)
|
||||
override predicate hasProvenance(string provenance) {
|
||||
summaryElement(this, _, _, _, provenance)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -56,51 +56,47 @@ DataFlowType getSyntheticGlobalType(SummaryComponent::SyntheticGlobal sg) { any(
|
||||
|
||||
/**
|
||||
* Holds if an external flow summary exists for `c` with input specification
|
||||
* `input`, output specification `output`, kind `kind`, and a flag `generated`
|
||||
* stating whether the summary is autogenerated.
|
||||
* `input`, output specification `output`, kind `kind`, and provenance `provenance`.
|
||||
*/
|
||||
predicate summaryElement(
|
||||
AbstractFunctionDecl c, string input, string output, string kind, boolean generated
|
||||
AbstractFunctionDecl c, string input, string output, string kind, string provenance
|
||||
) {
|
||||
exists(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||
|
|
||||
summaryModel(namespace, type, subtypes, name, signature, ext, input, output, kind, generated) and
|
||||
summaryModel(namespace, type, subtypes, name, signature, ext, input, output, kind, provenance) and
|
||||
c = interpretElement(namespace, type, subtypes, name, signature, ext)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a neutral model exists for `c`, which means that there is no
|
||||
* flow through `c`. The flag `generated` states whether the neutral model is autogenerated.
|
||||
* Note. Neutral models have not been implemented for swift.
|
||||
* Holds if a neutral model exists for `c` with provenance `provenance`,
|
||||
* which means that there is no flow through `c`.
|
||||
*/
|
||||
predicate neutralElement(AbstractFunctionDecl c, boolean generated) { none() }
|
||||
predicate neutralElement(AbstractFunctionDecl c, string provenance) { none() }
|
||||
|
||||
/**
|
||||
* Holds if an external source specification exists for `e` with output specification
|
||||
* `output`, kind `kind`, and a flag `generated` stating whether the source specification is
|
||||
* autogenerated.
|
||||
* `output`, kind `kind`, and provenance `provenance`.
|
||||
*/
|
||||
predicate sourceElement(Element e, string output, string kind, boolean generated) {
|
||||
predicate sourceElement(Element e, string output, string kind, string provenance) {
|
||||
exists(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||
|
|
||||
sourceModel(namespace, type, subtypes, name, signature, ext, output, kind, generated) and
|
||||
sourceModel(namespace, type, subtypes, name, signature, ext, output, kind, provenance) and
|
||||
e = interpretElement(namespace, type, subtypes, name, signature, ext)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an external sink specification exists for `e` with input specification
|
||||
* `input`, kind `kind` and a flag `generated` stating whether the sink specification is
|
||||
* autogenerated.
|
||||
* `input`, kind `kind` and provenance `provenance`.
|
||||
*/
|
||||
predicate sinkElement(Element e, string input, string kind, boolean generated) {
|
||||
predicate sinkElement(Element e, string input, string kind, string provenance) {
|
||||
exists(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext
|
||||
|
|
||||
sinkModel(namespace, type, subtypes, name, signature, ext, input, kind, generated) and
|
||||
sinkModel(namespace, type, subtypes, name, signature, ext, input, kind, provenance) and
|
||||
e = interpretElement(namespace, type, subtypes, name, signature, ext)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -104,7 +104,22 @@ private class PathInjectionSinks extends SinkModelCsv {
|
||||
";FileManager;true;replaceItemAtURL(originalItemURL:withItemAtURL:backupItemName:options:);;;Argument[0..1];path-injection",
|
||||
";NIOFileHandle;true;init(descriptor:);;;Argument[0];path-injection",
|
||||
";NIOFileHandle;true;init(path:mode:flags:);;;Argument[0];path-injection",
|
||||
";NIOFileHandle;true;init(path:);;;Argument[0];path-injection"
|
||||
";NIOFileHandle;true;init(path:);;;Argument[0];path-injection",
|
||||
";NSString;true;write(to:atomically:encoding:);;;Argument[0];path-injection",
|
||||
";NSString;true;write(toFile:atomically:encoding:);;;Argument[0];path-injection",
|
||||
";NSKeyedUnarchiver;true;unarchiveObject(withFile:);;;Argument[0];path-injection",
|
||||
";ArchiveByteStream;true;fileStream(fd:automaticClose:);;;Argument[0];path-injection",
|
||||
";ArchiveByteStream;true;withFileStream(fd:automaticClose:_:);;;Argument[0];path-injection",
|
||||
";ArchiveByteStream;true;fileStream(path:mode:options:permissions:);;;Argument[0];path-injection",
|
||||
";ArchiveByteStream;true;withFileStream(path:mode:options:permissions:_:);;;Argument[0];path-injection",
|
||||
";Bundle;true;init(url:);;;Argument[0];path-injection",
|
||||
";Bundle;true;init(path:);;;Argument[0];path-injection",
|
||||
// GRDB
|
||||
";Database;true;init(path:description:configuration:);;;Argument[0];path-injection",
|
||||
";DatabasePool;true;init(path:configuration:);;;Argument[0];path-injection",
|
||||
";DatabaseQueue;true;init(path:configuration:);;;Argument[0];path-injection",
|
||||
";DatabaseSnapshotPool;true;init(path:configuration:);;;Argument[0];path-injection",
|
||||
";SerializedDatabase;true;init(path:configuration:defaultLabel:purpose:);;;Argument[0];path-injection"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
41
swift/ql/lib/codeql/swift/security/PredicateInjection.qll
Normal file
41
swift/ql/lib/codeql/swift/security/PredicateInjection.qll
Normal file
@@ -0,0 +1,41 @@
|
||||
/** Provides classes and predicates to reason about predicate injection vulnerabilities. */
|
||||
|
||||
import swift
|
||||
private import codeql.swift.dataflow.DataFlow
|
||||
private import codeql.swift.dataflow.ExternalFlow
|
||||
|
||||
/** A data flow sink for predicate injection vulnerabilities. */
|
||||
abstract class PredicateInjectionSink extends DataFlow::Node { }
|
||||
|
||||
/** A sanitizer for predicate injection vulnerabilities. */
|
||||
abstract class PredicateInjectionSanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A unit class for adding additional taint steps.
|
||||
*
|
||||
* Extend this class to add additional taint steps that should apply to paths related to
|
||||
* predicate injection vulnerabilities.
|
||||
*/
|
||||
class PredicateInjectionAdditionalTaintStep extends Unit {
|
||||
/**
|
||||
* Holds if the step from `node1` to `node2` should be considered a taint
|
||||
* step for paths related to predicate injection vulnerabilities.
|
||||
*/
|
||||
abstract predicate step(DataFlow::Node n1, DataFlow::Node n2);
|
||||
}
|
||||
|
||||
private class DefaultPredicateInjectionSink extends PredicateInjectionSink {
|
||||
DefaultPredicateInjectionSink() { sinkNode(this, "predicate-injection") }
|
||||
}
|
||||
|
||||
private class PredicateInjectionSinkCsv extends SinkModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
";NSPredicate;true;init(format:argumentArray:);;;Argument[0];predicate-injection",
|
||||
";NSPredicate;true;init(format:arguments:);;;Argument[0];predicate-injection",
|
||||
";NSPredicate;true;init(format:_:);;;Argument[0];predicate-injection",
|
||||
";NSPredicate;true;init(fromMetadataQueryString:);;;Argument[0];predicate-injection"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Provides a taint-tracking configuration for reasoning about predicate injection
|
||||
* vulnerabilities.
|
||||
*/
|
||||
|
||||
import swift
|
||||
private import codeql.swift.dataflow.DataFlow
|
||||
private import codeql.swift.dataflow.FlowSources
|
||||
private import codeql.swift.dataflow.TaintTracking
|
||||
private import codeql.swift.security.PredicateInjection
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for predicate injection vulnerabilities.
|
||||
*/
|
||||
class PredicateInjectionConf extends TaintTracking::Configuration {
|
||||
PredicateInjectionConf() { this = "PredicateInjectionConf" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof PredicateInjectionSink }
|
||||
|
||||
override predicate isSanitizer(DataFlow::Node sanitizer) {
|
||||
sanitizer instanceof PredicateInjectionSanitizer
|
||||
}
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
any(PredicateInjectionAdditionalTaintStep s).step(n1, n2)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
- description: Extended and experimental security queries for Swift
|
||||
- queries: .
|
||||
- apply: security-experimental-selectors.yml
|
||||
from: codeql/suite-helpers
|
||||
@@ -63,6 +63,80 @@ class SQLiteSwiftSqlSink extends SqlSink {
|
||||
}
|
||||
}
|
||||
|
||||
/** A sink for the GRDB library. */
|
||||
class GrdbSqlSink extends SqlSink {
|
||||
GrdbSqlSink() {
|
||||
exists(CallExpr call, MethodDecl method |
|
||||
call.getStaticTarget() = method and
|
||||
call.getArgument(0).getExpr() = this.asExpr()
|
||||
|
|
||||
method
|
||||
.hasQualifiedName("Database",
|
||||
[
|
||||
"allStatements(sql:arguments:)", "cachedStatement(sql:)",
|
||||
"internalCachedStatement(sql:)", "execute(sql:arguments:)", "makeStatement(sql:)",
|
||||
"makeStatement(sql:prepFlags:)"
|
||||
])
|
||||
or
|
||||
method
|
||||
.hasQualifiedName("SQLRequest",
|
||||
[
|
||||
"init(stringLiteral:)", "init(unicodeScalarLiteral:)",
|
||||
"init(extendedGraphemeClusterLiteral:)", "init(stringInterpolation:)",
|
||||
"init(sql:arguments:adapter:cached:)"
|
||||
])
|
||||
or
|
||||
method
|
||||
.hasQualifiedName("SQL",
|
||||
[
|
||||
"init(stringLiteral:)", "init(unicodeScalarLiteral:)",
|
||||
"init(extendedGraphemeClusterLiteral:)", "init(stringInterpolation:)",
|
||||
"init(sql:arguments:)", "append(sql:arguments:)"
|
||||
])
|
||||
or
|
||||
method
|
||||
.hasQualifiedName("TableDefinition", ["column(sql:)", "check(sql:)", "constraint(sql:)"])
|
||||
or
|
||||
method.hasQualifiedName("TableAlteration", "addColumn(sql:)")
|
||||
or
|
||||
method
|
||||
.hasQualifiedName("ColumnDefinition",
|
||||
["check(sql:)", "defaults(sql:)", "generatedAs(sql:_:)"])
|
||||
or
|
||||
method
|
||||
.hasQualifiedName("TableRecord",
|
||||
[
|
||||
"select(sql:arguments:)", "select(sql:arguments:as:)", "filter(sql:arguments:)",
|
||||
"order(sql:arguments:)"
|
||||
])
|
||||
or
|
||||
method.hasQualifiedName("StatementCache", "statement(_:)")
|
||||
)
|
||||
or
|
||||
exists(CallExpr call, MethodDecl method |
|
||||
call.getStaticTarget() = method and
|
||||
call.getArgument(1).getExpr() = this.asExpr()
|
||||
|
|
||||
method
|
||||
.hasQualifiedName(["Row", "DatabaseValueConvertible"],
|
||||
[
|
||||
"fetchCursor(_:sql:arguments:adapter:)", "fetchAll(_:sql:arguments:adapter:)",
|
||||
"fetchSet(_:sql:arguments:adapter:)", "fetchOne(_:sql:arguments:adapter:)"
|
||||
])
|
||||
or
|
||||
method.hasQualifiedName("SQLStatementCursor", "init(database:sql:arguments:prepFlags:)")
|
||||
)
|
||||
or
|
||||
exists(CallExpr call, MethodDecl method |
|
||||
call.getStaticTarget() = method and
|
||||
call.getArgument(3).getExpr() = this.asExpr()
|
||||
|
|
||||
method
|
||||
.hasQualifiedName("CommonTableExpression", "init(recursive:named:columns:sql:arguments:)")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint configuration for tainted data that reaches a SQL sink.
|
||||
*/
|
||||
|
||||
@@ -32,12 +32,12 @@ class StaticInitializationVectorSource extends Expr {
|
||||
class EncryptionInitializationSink extends Expr {
|
||||
EncryptionInitializationSink() {
|
||||
// `iv` arg in `init` is a sink
|
||||
exists(InitializerCallExpr call, string fName |
|
||||
exists(InitializerCallExpr call |
|
||||
call.getStaticTarget()
|
||||
.hasQualifiedName([
|
||||
"AES", "ChaCha20", "Blowfish", "Rabbit", "CBC", "CFB", "GCM", "OCB", "OFB", "PCBC",
|
||||
"CCM", "CTR"
|
||||
], fName) and
|
||||
], _) and
|
||||
call.getArgumentWithLabel("iv").getExpr() = this
|
||||
)
|
||||
}
|
||||
|
||||
@@ -56,6 +56,61 @@ class RealmStore extends Stored instanceof DataFlow::PostUpdateNode {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `DataFlow::Node` that is an expression stored with the GRDB library.
|
||||
*/
|
||||
class GrdbStore extends Stored {
|
||||
GrdbStore() {
|
||||
exists(CallExpr call, MethodDecl method |
|
||||
call.getStaticTarget() = method and
|
||||
call.getArgumentWithLabel("arguments").getExpr() = this.asExpr()
|
||||
|
|
||||
method
|
||||
.hasQualifiedName("Database",
|
||||
["allStatements(sql:arguments:)", "execute(sql:arguments:)",])
|
||||
or
|
||||
method.hasQualifiedName("SQLRequest", "init(sql:arguments:adapter:cached:)")
|
||||
or
|
||||
method.hasQualifiedName("SQL", ["init(sql:arguments:)", "append(sql:arguments:)"])
|
||||
or
|
||||
method.hasQualifiedName("SQLStatementCursor", "init(database:sql:arguments:prepFlags:)")
|
||||
or
|
||||
method
|
||||
.hasQualifiedName("TableRecord",
|
||||
[
|
||||
"select(sql:arguments:)", "select(sql:arguments:as:)", "filter(sql:arguments:)",
|
||||
"order(sql:arguments:)"
|
||||
])
|
||||
or
|
||||
method
|
||||
.hasQualifiedName(["Row", "DatabaseValueConvertible", "FetchableRecord"],
|
||||
[
|
||||
"fetchCursor(_:sql:arguments:adapter:)", "fetchAll(_:sql:arguments:adapter:)",
|
||||
"fetchSet(_:sql:arguments:adapter:)", "fetchOne(_:sql:arguments:adapter:)"
|
||||
])
|
||||
or
|
||||
method
|
||||
.hasQualifiedName("FetchableRecord",
|
||||
[
|
||||
"fetchCursor(_:arguments:adapter:)", "fetchAll(_:arguments:adapter:)",
|
||||
"fetchSet(_:arguments:adapter:)", "fetchOne(_:arguments:adapter:)",
|
||||
])
|
||||
or
|
||||
method.hasQualifiedName("Statement", ["execute(arguments:)"])
|
||||
or
|
||||
method
|
||||
.hasQualifiedName("CommonTableExpression", "init(recursive:named:columns:sql:arguments:)")
|
||||
)
|
||||
or
|
||||
exists(CallExpr call, MethodDecl method |
|
||||
call.getStaticTarget() = method and
|
||||
call.getArgument(0).getExpr() = this.asExpr()
|
||||
|
|
||||
method.hasQualifiedName("Statement", "setArguments(_:)")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint configuration from sensitive information to expressions that are
|
||||
* transmitted over a network.
|
||||
@@ -77,6 +132,14 @@ class CleartextStorageConfig extends TaintTracking::Configuration {
|
||||
node.asExpr() instanceof EncryptedExpr
|
||||
}
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
// Needed until we have proper content flow through arrays
|
||||
exists(ArrayExpr arr |
|
||||
node1.asExpr() = arr.getAnElement() and
|
||||
node2.asExpr() = arr
|
||||
)
|
||||
}
|
||||
|
||||
override predicate allowImplicitRead(DataFlow::Node node, DataFlow::ContentSet c) {
|
||||
// flow out from fields of a `RealmSwiftObject` at the sink, for example in
|
||||
// `realmObj.data = sensitive`.
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>
|
||||
Predicates represent logical conditions that can be used to check whether an object matches them.
|
||||
If a predicate is built from user-provided data without sufficient sanitization, an attacker may
|
||||
be able to change the overall meaning of the predicate.
|
||||
</p>
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>
|
||||
When building a predicate from untrusted data, you should either pass it to the appropriate <code>arguments</code> parameter during initialization, or as an array of substitution variables before evaluation. You should not append or concatenate it to the body of the predicate.
|
||||
</p>
|
||||
</recommendation>
|
||||
<example>
|
||||
<p>
|
||||
In the following insecure example, <code>NSPredicate</code> is built directly from data obtained from an HTTP request. This is untrusted, and can be arbitrarily set by an attacker to alter the meaning of the predicate:
|
||||
</p>
|
||||
<sample src="PredicateInjectionBad.swift" />
|
||||
<p>
|
||||
A better way to do this is to use the <code>arguments</code> parameter of <code>NSPredicate</code>'s constructor. This prevents attackers from altering the meaning of the predicate, even if they control the externally obtained data, as seen in the following secure example:
|
||||
</p>
|
||||
<sample src="PredicateInjectionGood.swift" />
|
||||
</example>
|
||||
<references>
|
||||
<li>Apple Developer Documentation: <a href="https://developer.apple.com/documentation/foundation/nspredicate">NSPredicate</a> </li>
|
||||
</references>
|
||||
</qhelp>
|
||||
22
swift/ql/src/queries/Security/CWE-943/PredicateInjection.ql
Normal file
22
swift/ql/src/queries/Security/CWE-943/PredicateInjection.ql
Normal file
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* @name Predicate built from user-controlled sources
|
||||
* @description Building an NSPredicate from user-controlled sources may lead to attackers
|
||||
* changing the predicate's intended logic.
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @security-severity 8.8
|
||||
* @precision high
|
||||
* @id swift/predicate-injection
|
||||
* @tags security
|
||||
* external/cwe/cwe-943
|
||||
*/
|
||||
|
||||
import swift
|
||||
import codeql.swift.dataflow.DataFlow
|
||||
import codeql.swift.security.PredicateInjectionQuery
|
||||
import DataFlow::PathGraph
|
||||
|
||||
from DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where any(PredicateInjectionConf c).hasFlowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "This predicate depends on a $@.", source.getNode(),
|
||||
"user-provided value"
|
||||
@@ -0,0 +1,9 @@
|
||||
let remoteString = try String(contentsOf: URL(string: "https://example.com/")!)
|
||||
|
||||
let filenames: [String] = ["img1.png", "img2.png", "img3.png", "img.txt", "img.csv"]
|
||||
|
||||
let predicate = NSPredicate(format: "SELF LIKE \(remoteString)")
|
||||
let filtered = filenames.filter(){ filename in
|
||||
predicate.evaluate(with: filename)
|
||||
}
|
||||
print(filtered)
|
||||
@@ -0,0 +1,9 @@
|
||||
let remoteString = try String(contentsOf: URL(string: "https://example.com/")!)
|
||||
|
||||
let filenames: [String] = ["img1.png", "img2.png", "img3.png", "img.txt", "img.csv"]
|
||||
|
||||
let predicate = NSPredicate(format: "SELF LIKE %@", remoteString)
|
||||
let filtered = filenames.filter(){ filename in
|
||||
predicate.evaluate(with: filename)
|
||||
}
|
||||
print(filtered)
|
||||
@@ -1,399 +1,35 @@
|
||||
/**
|
||||
* Provides a library for writing QL tests whose success or failure is based on expected results
|
||||
* embedded in the test source code as comments, rather than the contents of an `.expected` file
|
||||
* (in that the `.expected` file should always be empty).
|
||||
*
|
||||
* To add this framework to a new language:
|
||||
* - Add a file `InlineExpectationsTestPrivate.qll` that defines a `ExpectationComment` class. This class
|
||||
* must support a `getContents` method that returns the contents of the given comment, _excluding_
|
||||
* the comment indicator itself. It should also define `toString` and `getLocation` as usual.
|
||||
*
|
||||
* To create a new inline expectations test:
|
||||
* - Declare a class that extends `InlineExpectationsTest`. In the characteristic predicate of the
|
||||
* new class, bind `this` to a unique string (usually the name of the test).
|
||||
* - Override the `hasActualResult()` predicate to produce the actual results of the query. For each
|
||||
* result, specify a `Location`, a text description of the element for which the result was
|
||||
* reported, a short string to serve as the tag to identify expected results for this test, and the
|
||||
* expected value of the result.
|
||||
* - Override `getARelevantTag()` to return the set of tags that can be produced by
|
||||
* `hasActualResult()`. Often this is just a single tag.
|
||||
*
|
||||
* Example:
|
||||
* ```ql
|
||||
* class ConstantValueTest extends InlineExpectationsTest {
|
||||
* ConstantValueTest() { this = "ConstantValueTest" }
|
||||
*
|
||||
* override string getARelevantTag() {
|
||||
* // We only use one tag for this test.
|
||||
* result = "const"
|
||||
* }
|
||||
*
|
||||
* override predicate hasActualResult(
|
||||
* Location location, string element, string tag, string value
|
||||
* ) {
|
||||
* exists(Expr e |
|
||||
* tag = "const" and // The tag for this test.
|
||||
* value = e.getValue() and // The expected value. Will only hold for constant expressions.
|
||||
* location = e.getLocation() and // The location of the result to be reported.
|
||||
* element = e.toString() // The display text for the result.
|
||||
* )
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* There is no need to write a `select` clause or query predicate. All of the differences between
|
||||
* expected results and actual results will be reported in the `failures()` query predicate.
|
||||
*
|
||||
* To annotate the test source code with an expected result, place a comment starting with a `$` on the
|
||||
* same line as the expected result, with text of the following format as the body of the comment:
|
||||
*
|
||||
* `tag=expected-value`
|
||||
*
|
||||
* Where `tag` is the value of the `tag` parameter from `hasActualResult()`, and `expected-value` is
|
||||
* the value of the `value` parameter from `hasActualResult()`. The `=expected-value` portion may be
|
||||
* omitted, in which case `expected-value` is treated as the empty string. Multiple expectations may
|
||||
* be placed in the same comment. Any actual result that
|
||||
* appears on a line that does not contain a matching expected result comment will be reported with
|
||||
* a message of the form "Unexpected result: tag=value". Any expected result comment for which there
|
||||
* is no matching actual result will be reported with a message of the form
|
||||
* "Missing result: tag=expected-value".
|
||||
*
|
||||
* Example:
|
||||
* ```cpp
|
||||
* int i = x + 5; // $ const=5
|
||||
* int j = y + (7 - 3) // $ const=7 const=3 const=4 // The result of the subtraction is a constant.
|
||||
* ```
|
||||
*
|
||||
* For tests that contain known missing and spurious results, it is possible to further
|
||||
* annotate that a particular expected result is known to be spurious, or that a particular
|
||||
* missing result is known to be missing:
|
||||
*
|
||||
* `$ SPURIOUS: tag=expected-value` // Spurious result
|
||||
* `$ MISSING: tag=expected-value` // Missing result
|
||||
*
|
||||
* A spurious expectation is treated as any other expected result, except that if there is no
|
||||
* matching actual result, the message will be of the form "Fixed spurious result: tag=value". A
|
||||
* missing expectation is treated as if there were no expected result, except that if a
|
||||
* matching expected result is found, the message will be of the form
|
||||
* "Fixed missing result: tag=value".
|
||||
*
|
||||
* A single line can contain all the expected, spurious and missing results of that line. For instance:
|
||||
* `$ tag1=value1 SPURIOUS: tag2=value2 MISSING: tag3=value3`.
|
||||
*
|
||||
* If the same result value is expected for two or more tags on the same line, there is a shorthand
|
||||
* notation available:
|
||||
*
|
||||
* `tag1,tag2=expected-value`
|
||||
*
|
||||
* is equivalent to:
|
||||
*
|
||||
* `tag1=expected-value tag2=expected-value`
|
||||
* Inline expectation tests for Swift.
|
||||
* See `shared/util/codeql/util/test/InlineExpectationsTest.qll`
|
||||
*/
|
||||
|
||||
private import InlineExpectationsTestPrivate
|
||||
private import swift as S
|
||||
private import codeql.util.test.InlineExpectationsTest
|
||||
|
||||
/**
|
||||
* The base class for tests with inline expectations. The test extends this class to provide the actual
|
||||
* results of the query, which are then compared with the expected results in comments to produce a
|
||||
* list of failure messages that point out where the actual results differ from the expected
|
||||
* results.
|
||||
*/
|
||||
abstract class InlineExpectationsTest extends string {
|
||||
bindingset[this]
|
||||
InlineExpectationsTest() { any() }
|
||||
private module Impl implements InlineExpectationsTestSig {
|
||||
private newtype TExpectationComment = MkExpectationComment(S::SingleLineComment c)
|
||||
|
||||
/**
|
||||
* Returns all tags that can be generated by this test. Most tests will only ever produce a single
|
||||
* tag. Any expected result comments for a tag that is not returned by the `getARelevantTag()`
|
||||
* predicate for an active test will be ignored. This makes it possible to write multiple tests in
|
||||
* different `.ql` files that all query the same source code.
|
||||
* A class representing a line comment.
|
||||
* Unlike the `SingleLineComment` class, however, the string returned by `getContents` does _not_
|
||||
* include the preceding comment marker (`//`).
|
||||
*/
|
||||
abstract string getARelevantTag();
|
||||
class ExpectationComment extends TExpectationComment {
|
||||
S::SingleLineComment comment;
|
||||
|
||||
/**
|
||||
* Returns the actual results of the query that is being tested. Each result consist of the
|
||||
* following values:
|
||||
* - `location` - The source code location of the result. Any expected result comment must appear
|
||||
* on the start line of this location.
|
||||
* - `element` - Display text for the element on which the result is reported.
|
||||
* - `tag` - The tag that marks this result as coming from this test. This must be one of the tags
|
||||
* returned by `getARelevantTag()`.
|
||||
* - `value` - The value of the result, which will be matched against the value associated with
|
||||
* `tag` in any expected result comment on that line.
|
||||
*/
|
||||
abstract predicate hasActualResult(Location location, string element, string tag, string value);
|
||||
ExpectationComment() { this = MkExpectationComment(comment) }
|
||||
|
||||
/**
|
||||
* Holds if there is an optional result on the specified location.
|
||||
*
|
||||
* This is similar to `hasActualResult`, but returns results that do not require a matching annotation.
|
||||
* A failure will still arise if there is an annotation that does not match any results, but not vice versa.
|
||||
* Override this predicate to specify optional results.
|
||||
*/
|
||||
predicate hasOptionalResult(Location location, string element, string tag, string value) {
|
||||
none()
|
||||
/** Returns the contents of the given comment, _without_ the preceding comment marker (`//`). */
|
||||
string getContents() { result = comment.getText().suffix(2) }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = comment.toString() }
|
||||
|
||||
/** Gets the location of this comment. */
|
||||
Location getLocation() { result = comment.getLocation() }
|
||||
}
|
||||
|
||||
final predicate hasFailureMessage(FailureLocatable element, string message) {
|
||||
exists(ActualResult actualResult |
|
||||
actualResult.getTest() = this and
|
||||
actualResult.getTag() = this.getARelevantTag() and
|
||||
element = actualResult and
|
||||
(
|
||||
exists(FalseNegativeExpectation falseNegative |
|
||||
falseNegative.matchesActualResult(actualResult) and
|
||||
message = "Fixed missing result:" + falseNegative.getExpectationText()
|
||||
)
|
||||
or
|
||||
not exists(ValidExpectation expectation | expectation.matchesActualResult(actualResult)) and
|
||||
message = "Unexpected result: " + actualResult.getExpectationText() and
|
||||
not actualResult.isOptional()
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(ActualResult actualResult |
|
||||
actualResult.getTest() = this and
|
||||
not actualResult.getTag() = this.getARelevantTag() and
|
||||
element = actualResult and
|
||||
message =
|
||||
"Tag mismatch: Actual result with tag '" + actualResult.getTag() +
|
||||
"' that is not part of getARelevantTag()"
|
||||
)
|
||||
or
|
||||
exists(ValidExpectation expectation |
|
||||
not exists(ActualResult actualResult | expectation.matchesActualResult(actualResult)) and
|
||||
expectation.getTag() = this.getARelevantTag() and
|
||||
element = expectation and
|
||||
(
|
||||
expectation instanceof GoodExpectation and
|
||||
message = "Missing result:" + expectation.getExpectationText()
|
||||
or
|
||||
expectation instanceof FalsePositiveExpectation and
|
||||
message = "Fixed spurious result:" + expectation.getExpectationText()
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(InvalidExpectation expectation |
|
||||
element = expectation and
|
||||
message = "Invalid expectation syntax: " + expectation.getExpectation()
|
||||
)
|
||||
}
|
||||
class Location = S::Location;
|
||||
}
|
||||
|
||||
/**
|
||||
* RegEx pattern to match a comment containing one or more expected results. The comment must have
|
||||
* `$` as its first non-whitespace character. Any subsequent character
|
||||
* is treated as part of the expected results, except that the comment may contain a `//` sequence
|
||||
* to treat the remainder of the line as a regular (non-interpreted) comment.
|
||||
*/
|
||||
private string expectationCommentPattern() { result = "\\s*\\$((?:[^/]|/[^/])*)(?://.*)?" }
|
||||
|
||||
/**
|
||||
* The possible columns in an expectation comment. The `TDefaultColumn` branch represents the first
|
||||
* column in a comment. This column is not precedeeded by a name. `TNamedColumn(name)` represents a
|
||||
* column containing expected results preceded by the string `name:`.
|
||||
*/
|
||||
private newtype TColumn =
|
||||
TDefaultColumn() or
|
||||
TNamedColumn(string name) { name = ["MISSING", "SPURIOUS"] }
|
||||
|
||||
bindingset[start, content]
|
||||
private int getEndOfColumnPosition(int start, string content) {
|
||||
result =
|
||||
min(string name, int cand |
|
||||
exists(TNamedColumn(name)) and
|
||||
cand = content.indexOf(name + ":") and
|
||||
cand >= start
|
||||
|
|
||||
cand
|
||||
)
|
||||
or
|
||||
not exists(string name |
|
||||
exists(TNamedColumn(name)) and
|
||||
content.indexOf(name + ":") >= start
|
||||
) and
|
||||
result = content.length()
|
||||
}
|
||||
|
||||
private predicate getAnExpectation(
|
||||
ExpectationComment comment, TColumn column, string expectation, string tags, string value
|
||||
) {
|
||||
exists(string content |
|
||||
content = comment.getContents().regexpCapture(expectationCommentPattern(), 1) and
|
||||
(
|
||||
column = TDefaultColumn() and
|
||||
exists(int end |
|
||||
end = getEndOfColumnPosition(0, content) and
|
||||
expectation = content.prefix(end).regexpFind(expectationPattern(), _, _).trim()
|
||||
)
|
||||
or
|
||||
exists(string name, int start, int end |
|
||||
column = TNamedColumn(name) and
|
||||
start = content.indexOf(name + ":") + name.length() + 1 and
|
||||
end = getEndOfColumnPosition(start, content) and
|
||||
expectation = content.substring(start, end).regexpFind(expectationPattern(), _, _).trim()
|
||||
)
|
||||
)
|
||||
) and
|
||||
tags = expectation.regexpCapture(expectationPattern(), 1) and
|
||||
if exists(expectation.regexpCapture(expectationPattern(), 2))
|
||||
then value = expectation.regexpCapture(expectationPattern(), 2)
|
||||
else value = ""
|
||||
}
|
||||
|
||||
private string getColumnString(TColumn column) {
|
||||
column = TDefaultColumn() and result = ""
|
||||
or
|
||||
column = TNamedColumn(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* RegEx pattern to match a single expected result, not including the leading `$`. It consists of one or
|
||||
* more comma-separated tags optionally followed by `=` and the expected value.
|
||||
*
|
||||
* Tags must be only letters, digits, `-` and `_` (note that the first character
|
||||
* must not be a digit), but can contain anything enclosed in a single set of
|
||||
* square brackets.
|
||||
*
|
||||
* Examples:
|
||||
* - `tag`
|
||||
* - `tag=value`
|
||||
* - `tag,tag2=value`
|
||||
* - `tag[foo bar]=value`
|
||||
*
|
||||
* Not allowed:
|
||||
* - `tag[[[foo bar]`
|
||||
*/
|
||||
private string expectationPattern() {
|
||||
exists(string tag, string tags, string value |
|
||||
tag = "[A-Za-z-_](?:[A-Za-z-_0-9]|\\[[^\\]\\]]*\\])*" and
|
||||
tags = "((?:" + tag + ")(?:\\s*,\\s*" + tag + ")*)" and
|
||||
// In Python, we allow both `"` and `'` for strings, as well as the prefixes `bru`.
|
||||
// For example, `b"foo"`.
|
||||
value = "((?:[bru]*\"[^\"]*\"|[bru]*'[^']*'|\\S+)*)" and
|
||||
result = tags + "(?:=" + value + ")?"
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TFailureLocatable =
|
||||
TActualResult(
|
||||
InlineExpectationsTest test, Location location, string element, string tag, string value,
|
||||
boolean optional
|
||||
) {
|
||||
test.hasActualResult(location, element, tag, value) and
|
||||
optional = false
|
||||
or
|
||||
test.hasOptionalResult(location, element, tag, value) and optional = true
|
||||
} or
|
||||
TValidExpectation(ExpectationComment comment, string tag, string value, string knownFailure) {
|
||||
exists(TColumn column, string tags |
|
||||
getAnExpectation(comment, column, _, tags, value) and
|
||||
tag = tags.splitAt(",") and
|
||||
knownFailure = getColumnString(column)
|
||||
)
|
||||
} or
|
||||
TInvalidExpectation(ExpectationComment comment, string expectation) {
|
||||
getAnExpectation(comment, _, expectation, _, _) and
|
||||
not expectation.regexpMatch(expectationPattern())
|
||||
}
|
||||
|
||||
class FailureLocatable extends TFailureLocatable {
|
||||
string toString() { none() }
|
||||
|
||||
Location getLocation() { none() }
|
||||
|
||||
final string getExpectationText() { result = getTag() + "=" + getValue() }
|
||||
|
||||
string getTag() { none() }
|
||||
|
||||
string getValue() { none() }
|
||||
}
|
||||
|
||||
class ActualResult extends FailureLocatable, TActualResult {
|
||||
InlineExpectationsTest test;
|
||||
Location location;
|
||||
string element;
|
||||
string tag;
|
||||
string value;
|
||||
boolean optional;
|
||||
|
||||
ActualResult() { this = TActualResult(test, location, element, tag, value, optional) }
|
||||
|
||||
override string toString() { result = element }
|
||||
|
||||
override Location getLocation() { result = location }
|
||||
|
||||
InlineExpectationsTest getTest() { result = test }
|
||||
|
||||
override string getTag() { result = tag }
|
||||
|
||||
override string getValue() { result = value }
|
||||
|
||||
predicate isOptional() { optional = true }
|
||||
}
|
||||
|
||||
abstract private class Expectation extends FailureLocatable {
|
||||
ExpectationComment comment;
|
||||
|
||||
override string toString() { result = comment.toString() }
|
||||
|
||||
override Location getLocation() { result = comment.getLocation() }
|
||||
}
|
||||
|
||||
private predicate onSameLine(ValidExpectation a, ActualResult b) {
|
||||
exists(string fname, int line, Location la, Location lb |
|
||||
// Join order intent:
|
||||
// Take the locations of ActualResults,
|
||||
// join with locations in the same file / on the same line,
|
||||
// then match those against ValidExpectations.
|
||||
la = a.getLocation() and
|
||||
pragma[only_bind_into](lb) = b.getLocation() and
|
||||
pragma[only_bind_into](la).hasLocationInfo(fname, line, _, _, _) and
|
||||
lb.hasLocationInfo(fname, line, _, _, _)
|
||||
)
|
||||
}
|
||||
|
||||
private class ValidExpectation extends Expectation, TValidExpectation {
|
||||
string tag;
|
||||
string value;
|
||||
string knownFailure;
|
||||
|
||||
ValidExpectation() { this = TValidExpectation(comment, tag, value, knownFailure) }
|
||||
|
||||
override string getTag() { result = tag }
|
||||
|
||||
override string getValue() { result = value }
|
||||
|
||||
string getKnownFailure() { result = knownFailure }
|
||||
|
||||
predicate matchesActualResult(ActualResult actualResult) {
|
||||
onSameLine(pragma[only_bind_into](this), actualResult) and
|
||||
getTag() = actualResult.getTag() and
|
||||
getValue() = actualResult.getValue()
|
||||
}
|
||||
}
|
||||
|
||||
/* Note: These next three classes correspond to all the possible values of type `TColumn`. */
|
||||
class GoodExpectation extends ValidExpectation {
|
||||
GoodExpectation() { getKnownFailure() = "" }
|
||||
}
|
||||
|
||||
class FalsePositiveExpectation extends ValidExpectation {
|
||||
FalsePositiveExpectation() { getKnownFailure() = "SPURIOUS" }
|
||||
}
|
||||
|
||||
class FalseNegativeExpectation extends ValidExpectation {
|
||||
FalseNegativeExpectation() { getKnownFailure() = "MISSING" }
|
||||
}
|
||||
|
||||
class InvalidExpectation extends Expectation, TInvalidExpectation {
|
||||
string expectation;
|
||||
|
||||
InvalidExpectation() { this = TInvalidExpectation(comment, expectation) }
|
||||
|
||||
string getExpectation() { result = expectation }
|
||||
}
|
||||
|
||||
query predicate failures(FailureLocatable element, string message) {
|
||||
exists(InlineExpectationsTest test | test.hasFailureMessage(element, message))
|
||||
}
|
||||
import Make<Impl>
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
import swift
|
||||
|
||||
private newtype TExpectationComment = MkExpectationComment(SingleLineComment c)
|
||||
|
||||
/**
|
||||
* Represents a line comment.
|
||||
* Unlike the `SingleLineComment` class, however, the string returned by `getContents` does _not_
|
||||
* include the preceding comment marker (`//`).
|
||||
*/
|
||||
class ExpectationComment extends TExpectationComment {
|
||||
SingleLineComment comment;
|
||||
|
||||
ExpectationComment() { this = MkExpectationComment(comment) }
|
||||
|
||||
/** Returns the contents of the given comment, _without_ the preceding comment marker (`//`). */
|
||||
string getContents() { result = comment.getText().suffix(2) }
|
||||
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() { result = comment.toString() }
|
||||
|
||||
/** Gets the location of this comment. */
|
||||
Location getLocation() { result = comment.getLocation() }
|
||||
}
|
||||
@@ -15,6 +15,11 @@ extension String {
|
||||
}
|
||||
}
|
||||
|
||||
class NSString {
|
||||
func write(toFile: String, atomically: Bool, encoding: UInt) {}
|
||||
func write(to: URL, atomically: Bool, encoding: UInt) {}
|
||||
}
|
||||
|
||||
class Data {
|
||||
struct WritingOptions : OptionSet { let rawValue: Int }
|
||||
init<S>(_ elements: S) {}
|
||||
@@ -29,6 +34,10 @@ class NSData {
|
||||
func write(toFile: String, options: NSData.WritingOptions) {}
|
||||
}
|
||||
|
||||
class NSKeyedUnarchiver {
|
||||
func unarchiveObject(withFile: String) -> Any? { return nil }
|
||||
}
|
||||
|
||||
struct URLResourceKey {}
|
||||
|
||||
struct FileAttributeKey : Hashable {}
|
||||
@@ -37,9 +46,10 @@ struct ObjCBool {}
|
||||
|
||||
struct AutoreleasingUnsafeMutablePointer<Pointee> {}
|
||||
|
||||
struct FilePath {
|
||||
struct FilePath : ExpressibleByStringLiteral {
|
||||
typealias StringLiteralType = String
|
||||
init(stringLiteral: String) {}
|
||||
func lexicallyNormalized() -> FilePath { return FilePath(stringLiteral: "") }
|
||||
func lexicallyNormalized() -> FilePath { return "" }
|
||||
func starts(with: FilePath) -> Bool { return false }
|
||||
}
|
||||
|
||||
@@ -91,6 +101,62 @@ class FileManager {
|
||||
func replaceItemAtURL(originalItemURL: NSURL, withItemAtURL: NSURL, backupItemName: String?, options: FileManager.ItemReplacementOptions) -> NSURL? { return nil }
|
||||
}
|
||||
|
||||
struct FileDescriptor {
|
||||
struct AccessMode : RawRepresentable {
|
||||
static let readOnly = AccessMode(rawValue: 0)
|
||||
let rawValue: UInt8
|
||||
init(rawValue: UInt8) { self.rawValue = rawValue}
|
||||
}
|
||||
|
||||
struct OpenOptions : RawRepresentable {
|
||||
static let append = OpenOptions(rawValue: 0)
|
||||
let rawValue: UInt8
|
||||
init(rawValue: UInt8) { self.rawValue = rawValue}
|
||||
}
|
||||
}
|
||||
|
||||
struct FilePermissions : RawRepresentable {
|
||||
static let ownerRead = FilePermissions(rawValue: 0)
|
||||
let rawValue: UInt8
|
||||
init(rawValue: UInt8) { self.rawValue = rawValue}
|
||||
}
|
||||
|
||||
class ArchiveByteStream {
|
||||
static func fileStream(fd: FileDescriptor, automaticClose: Bool = true) -> ArchiveByteStream? { return nil }
|
||||
static func withFileStream<E>(fd: FileDescriptor, automaticClose: Bool = true, _ body: (ArchiveByteStream) -> E) -> E { return body(ArchiveByteStream()) }
|
||||
static func fileStream(path: FilePath, mode: FileDescriptor.AccessMode, options: FileDescriptor.OpenOptions, permissions: FilePermissions) -> ArchiveByteStream? { return nil }
|
||||
static func withFileStream<E>(path: FilePath, mode: FileDescriptor.AccessMode, options: FileDescriptor.OpenOptions, permissions: FilePermissions, _ body: (ArchiveByteStream) -> E) -> E { return body(ArchiveByteStream()) }
|
||||
}
|
||||
|
||||
class Bundle {
|
||||
init?(url: URL) {}
|
||||
init?(path: String) {}
|
||||
}
|
||||
|
||||
// GRDB
|
||||
|
||||
struct Configuration {}
|
||||
|
||||
class Database {
|
||||
init(path: String, description: String, configuration: Configuration) {}
|
||||
}
|
||||
|
||||
class DatabasePool {
|
||||
init(path: String, configuration: Configuration) {}
|
||||
}
|
||||
|
||||
class DatabaseQueue {
|
||||
init(path: String, configuration: Configuration) {}
|
||||
}
|
||||
|
||||
class DatabaseSnapshotPool {
|
||||
init(path: String, configuration: Configuration) {}
|
||||
}
|
||||
|
||||
class SerializedDatabase {
|
||||
init(path: String, configuration: Configuration = Configuration(), defaultLabel: String, purpose: String? = nil) {}
|
||||
}
|
||||
|
||||
// --- tests ---
|
||||
|
||||
func test() {
|
||||
@@ -100,65 +166,92 @@ func test() {
|
||||
let safeUrl = URL(string: "")!
|
||||
let safeNsUrl = NSURL(string: "")!
|
||||
|
||||
Data("").write(to: remoteUrl, options: []) // $ hasPathInjection=97
|
||||
Data("").write(to: remoteUrl, options: []) // $ hasPathInjection=163
|
||||
|
||||
let nsData = NSData()
|
||||
let _ = nsData.write(to: remoteUrl, atomically: false) // $ hasPathInjection=97
|
||||
nsData.write(to: remoteUrl, options: []) // $ hasPathInjection=97
|
||||
let _ = nsData.write(toFile: remoteString, atomically: false) // $ hasPathInjection=97
|
||||
nsData.write(toFile: remoteString, options: []) // $ hasPathInjection=97
|
||||
let _ = nsData.write(to: remoteUrl, atomically: false) // $ hasPathInjection=163
|
||||
nsData.write(to: remoteUrl, options: []) // $ hasPathInjection=163
|
||||
let _ = nsData.write(toFile: remoteString, atomically: false) // $ hasPathInjection=163
|
||||
nsData.write(toFile: remoteString, options: []) // $ hasPathInjection=163
|
||||
|
||||
let fm = FileManager()
|
||||
let _ = fm.contentsOfDirectory(at: remoteUrl, includingPropertiesForKeys: [], options: []) // $ hasPathInjection=97
|
||||
let _ = fm.contentsOfDirectory(atPath: remoteString) // $ hasPathInjection=97
|
||||
let _ = fm.enumerator(at: remoteUrl, includingPropertiesForKeys: [], options: [], errorHandler: nil) // $ hasPathInjection=97
|
||||
let _ = fm.enumerator(atPath: remoteString) // $ hasPathInjection=97
|
||||
let _ = fm.subpathsOfDirectory(atPath: remoteString) // $ hasPathInjection=97
|
||||
let _ = fm.subpaths(atPath: remoteString) // $ hasPathInjection=97
|
||||
fm.createDirectory(at: remoteUrl, withIntermediateDirectories: false, attributes: [:]) // $ hasPathInjection=97
|
||||
let _ = fm.createDirectory(atPath: remoteString, attributes: [:]) // $ hasPathInjection=97
|
||||
let _ = fm.createFile(atPath: remoteString, contents: nil, attributes: [:]) // $ hasPathInjection=97
|
||||
fm.removeItem(at: remoteUrl) // $ hasPathInjection=97
|
||||
fm.removeItem(atPath: remoteString) // $ hasPathInjection=97
|
||||
fm.trashItem(at: remoteUrl, resultingItemURL: AutoreleasingUnsafeMutablePointer<NSURL?>()) // $ hasPathInjection=97
|
||||
let _ = fm.replaceItemAt(remoteUrl, withItemAt: safeUrl, backupItemName: nil, options: []) // $ hasPathInjection=97
|
||||
let _ = fm.replaceItemAt(safeUrl, withItemAt: remoteUrl, backupItemName: nil, options: []) // $ hasPathInjection=97
|
||||
fm.replaceItem(at: remoteUrl, withItemAt: safeUrl, backupItemName: nil, options: [], resultingItemURL: AutoreleasingUnsafeMutablePointer<NSURL?>()) // $ hasPathInjection=97
|
||||
fm.replaceItem(at: safeUrl, withItemAt: remoteUrl, backupItemName: nil, options: [], resultingItemURL: AutoreleasingUnsafeMutablePointer<NSURL?>()) // $ hasPathInjection=97
|
||||
fm.copyItem(at: remoteUrl, to: safeUrl) // $ hasPathInjection=97
|
||||
fm.copyItem(at: safeUrl, to: remoteUrl) // $ hasPathInjection=97
|
||||
fm.copyItem(atPath: remoteString, toPath: "") // $ hasPathInjection=97
|
||||
fm.copyItem(atPath: "", toPath: remoteString) // $ hasPathInjection=97
|
||||
fm.moveItem(at: remoteUrl, to: safeUrl) // $ hasPathInjection=97
|
||||
fm.moveItem(at: safeUrl, to: remoteUrl) // $ hasPathInjection=97
|
||||
fm.moveItem(atPath: remoteString, toPath: "") // $ hasPathInjection=97
|
||||
fm.moveItem(atPath: "", toPath: remoteString) // $ hasPathInjection=97
|
||||
fm.createSymbolicLink(at: remoteUrl, withDestinationURL: safeUrl) // $ hasPathInjection=97
|
||||
fm.createSymbolicLink(at: safeUrl, withDestinationURL: remoteUrl) // $ hasPathInjection=97
|
||||
fm.createSymbolicLink(atPath: remoteString, withDestinationPath: "") // $ hasPathInjection=97
|
||||
fm.createSymbolicLink(atPath: "", withDestinationPath: remoteString) // $ hasPathInjection=97
|
||||
fm.linkItem(at: remoteUrl, to: safeUrl) // $ hasPathInjection=97
|
||||
fm.linkItem(at: safeUrl, to: remoteUrl) // $ hasPathInjection=97
|
||||
fm.linkItem(atPath: remoteString, toPath: "") // $ hasPathInjection=97
|
||||
fm.linkItem(atPath: "", toPath: remoteString) // $ hasPathInjection=97
|
||||
let _ = fm.destinationOfSymbolicLink(atPath: remoteString) // $ hasPathInjection=97
|
||||
let _ = fm.fileExists(atPath: remoteString) // $ hasPathInjection=97
|
||||
let _ = fm.fileExists(atPath: remoteString, isDirectory: UnsafeMutablePointer<ObjCBool>.init(bitPattern: 0)) // $ hasPathInjection=97
|
||||
fm.setAttributes([:], ofItemAtPath: remoteString) // $ hasPathInjection=97
|
||||
let _ = fm.contents(atPath: remoteString) // $ hasPathInjection=97
|
||||
let _ = fm.contentsEqual(atPath: remoteString, andPath: "") // $ hasPathInjection=97
|
||||
let _ = fm.contentsEqual(atPath: "", andPath: remoteString) // $ hasPathInjection=97
|
||||
let _ = fm.changeCurrentDirectoryPath(remoteString) // $ hasPathInjection=97
|
||||
let _ = fm.unmountVolume(at: remoteUrl, options: [], completionHandler: { _ in }) // $ hasPathInjection=97
|
||||
let _ = fm.contentsOfDirectory(at: remoteUrl, includingPropertiesForKeys: [], options: []) // $ hasPathInjection=163
|
||||
let _ = fm.contentsOfDirectory(atPath: remoteString) // $ hasPathInjection=163
|
||||
let _ = fm.enumerator(at: remoteUrl, includingPropertiesForKeys: [], options: [], errorHandler: nil) // $ hasPathInjection=163
|
||||
let _ = fm.enumerator(atPath: remoteString) // $ hasPathInjection=163
|
||||
let _ = fm.subpathsOfDirectory(atPath: remoteString) // $ hasPathInjection=163
|
||||
let _ = fm.subpaths(atPath: remoteString) // $ hasPathInjection=163
|
||||
fm.createDirectory(at: remoteUrl, withIntermediateDirectories: false, attributes: [:]) // $ hasPathInjection=163
|
||||
let _ = fm.createDirectory(atPath: remoteString, attributes: [:]) // $ hasPathInjection=163
|
||||
let _ = fm.createFile(atPath: remoteString, contents: nil, attributes: [:]) // $ hasPathInjection=163
|
||||
fm.removeItem(at: remoteUrl) // $ hasPathInjection=163
|
||||
fm.removeItem(atPath: remoteString) // $ hasPathInjection=163
|
||||
fm.trashItem(at: remoteUrl, resultingItemURL: AutoreleasingUnsafeMutablePointer<NSURL?>()) // $ hasPathInjection=163
|
||||
let _ = fm.replaceItemAt(remoteUrl, withItemAt: safeUrl, backupItemName: nil, options: []) // $ hasPathInjection=163
|
||||
let _ = fm.replaceItemAt(safeUrl, withItemAt: remoteUrl, backupItemName: nil, options: []) // $ hasPathInjection=163
|
||||
fm.replaceItem(at: remoteUrl, withItemAt: safeUrl, backupItemName: nil, options: [], resultingItemURL: AutoreleasingUnsafeMutablePointer<NSURL?>()) // $ hasPathInjection=163
|
||||
fm.replaceItem(at: safeUrl, withItemAt: remoteUrl, backupItemName: nil, options: [], resultingItemURL: AutoreleasingUnsafeMutablePointer<NSURL?>()) // $ hasPathInjection=163
|
||||
fm.copyItem(at: remoteUrl, to: safeUrl) // $ hasPathInjection=163
|
||||
fm.copyItem(at: safeUrl, to: remoteUrl) // $ hasPathInjection=163
|
||||
fm.copyItem(atPath: remoteString, toPath: "") // $ hasPathInjection=163
|
||||
fm.copyItem(atPath: "", toPath: remoteString) // $ hasPathInjection=163
|
||||
fm.moveItem(at: remoteUrl, to: safeUrl) // $ hasPathInjection=163
|
||||
fm.moveItem(at: safeUrl, to: remoteUrl) // $ hasPathInjection=163
|
||||
fm.moveItem(atPath: remoteString, toPath: "") // $ hasPathInjection=163
|
||||
fm.moveItem(atPath: "", toPath: remoteString) // $ hasPathInjection=163
|
||||
fm.createSymbolicLink(at: remoteUrl, withDestinationURL: safeUrl) // $ hasPathInjection=163
|
||||
fm.createSymbolicLink(at: safeUrl, withDestinationURL: remoteUrl) // $ hasPathInjection=163
|
||||
fm.createSymbolicLink(atPath: remoteString, withDestinationPath: "") // $ hasPathInjection=163
|
||||
fm.createSymbolicLink(atPath: "", withDestinationPath: remoteString) // $ hasPathInjection=163
|
||||
fm.linkItem(at: remoteUrl, to: safeUrl) // $ hasPathInjection=163
|
||||
fm.linkItem(at: safeUrl, to: remoteUrl) // $ hasPathInjection=163
|
||||
fm.linkItem(atPath: remoteString, toPath: "") // $ hasPathInjection=163
|
||||
fm.linkItem(atPath: "", toPath: remoteString) // $ hasPathInjection=163
|
||||
let _ = fm.destinationOfSymbolicLink(atPath: remoteString) // $ hasPathInjection=163
|
||||
let _ = fm.fileExists(atPath: remoteString) // $ hasPathInjection=163
|
||||
let _ = fm.fileExists(atPath: remoteString, isDirectory: UnsafeMutablePointer<ObjCBool>.init(bitPattern: 0)) // $ hasPathInjection=163
|
||||
fm.setAttributes([:], ofItemAtPath: remoteString) // $ hasPathInjection=163
|
||||
let _ = fm.contents(atPath: remoteString) // $ hasPathInjection=163
|
||||
let _ = fm.contentsEqual(atPath: remoteString, andPath: "") // $ hasPathInjection=163
|
||||
let _ = fm.contentsEqual(atPath: "", andPath: remoteString) // $ hasPathInjection=163
|
||||
let _ = fm.changeCurrentDirectoryPath(remoteString) // $ hasPathInjection=163
|
||||
let _ = fm.unmountVolume(at: remoteUrl, options: [], completionHandler: { _ in }) // $ hasPathInjection=163
|
||||
// Deprecated methods
|
||||
let _ = fm.changeFileAttributes([:], atPath: remoteString) // $ hasPathInjection=97
|
||||
let _ = fm.directoryContents(atPath: remoteString) // $ hasPathInjection=97
|
||||
let _ = fm.createDirectory(atPath: remoteString, attributes: [:]) // $ hasPathInjection=97
|
||||
let _ = fm.createSymbolicLink(atPath: remoteString, pathContent: "") // $ hasPathInjection=97
|
||||
let _ = fm.createSymbolicLink(atPath: "", pathContent: remoteString) // $ hasPathInjection=97
|
||||
let _ = fm.pathContentOfSymbolicLink(atPath: remoteString) // $ hasPathInjection=97
|
||||
let _ = fm.replaceItemAtURL(originalItemURL: remoteNsUrl, withItemAtURL: safeNsUrl, backupItemName: nil, options: []) // $ hasPathInjection=97
|
||||
let _ = fm.replaceItemAtURL(originalItemURL: safeNsUrl, withItemAtURL: remoteNsUrl, backupItemName: nil, options: []) // $ hasPathInjection=97
|
||||
let _ = fm.changeFileAttributes([:], atPath: remoteString) // $ hasPathInjection=163
|
||||
let _ = fm.directoryContents(atPath: remoteString) // $ hasPathInjection=163
|
||||
let _ = fm.createDirectory(atPath: remoteString, attributes: [:]) // $ hasPathInjection=163
|
||||
let _ = fm.createSymbolicLink(atPath: remoteString, pathContent: "") // $ hasPathInjection=163
|
||||
let _ = fm.createSymbolicLink(atPath: "", pathContent: remoteString) // $ hasPathInjection=163
|
||||
let _ = fm.pathContentOfSymbolicLink(atPath: remoteString) // $ hasPathInjection=163
|
||||
let _ = fm.replaceItemAtURL(originalItemURL: remoteNsUrl, withItemAtURL: safeNsUrl, backupItemName: nil, options: []) // $ hasPathInjection=163
|
||||
let _ = fm.replaceItemAtURL(originalItemURL: safeNsUrl, withItemAtURL: remoteNsUrl, backupItemName: nil, options: []) // $ hasPathInjection=163
|
||||
|
||||
NSString().write(to: remoteUrl, atomically: true, encoding: 0) // $ hasPathInjection=163
|
||||
NSString().write(toFile: remoteString, atomically: true, encoding: 0) // $ hasPathInjection=163
|
||||
let _ = NSKeyedUnarchiver().unarchiveObject(withFile: remoteString) // $ hasPathInjection=163
|
||||
let _ = ArchiveByteStream.fileStream(fd: remoteString as! FileDescriptor, automaticClose: true) // $ hasPathInjection=163
|
||||
ArchiveByteStream.withFileStream(fd: remoteString as! FileDescriptor, automaticClose: true) { _ in } // $ hasPathInjection=163
|
||||
let _ = ArchiveByteStream.fileStream(path: FilePath(stringLiteral: remoteString), mode: .readOnly, options: .append, permissions: .ownerRead) // $ hasPathInjection=163
|
||||
ArchiveByteStream.withFileStream(path: FilePath(stringLiteral: remoteString), mode: .readOnly, options: .append, permissions: .ownerRead) { _ in } // $ hasPathInjection=163
|
||||
let _ = Bundle(url: remoteUrl) // $ hasPathInjection=163
|
||||
let _ = Bundle(path: remoteString) // $ hasPathInjection=163
|
||||
|
||||
let _ = Database(path: remoteString, description: "", configuration: Configuration()) // $ hasPathInjection=163
|
||||
let _ = Database(path: "", description: "", configuration: Configuration()) // Safe
|
||||
let _ = DatabasePool(path: remoteString, configuration: Configuration()) // $ hasPathInjection=163
|
||||
let _ = DatabasePool(path: "", configuration: Configuration()) // Safe
|
||||
let _ = DatabaseQueue(path: remoteString, configuration: Configuration()) // $ hasPathInjection=163
|
||||
let _ = DatabaseQueue(path: "", configuration: Configuration()) // Safe
|
||||
let _ = DatabaseSnapshotPool(path: remoteString, configuration: Configuration()) // $ hasPathInjection=163
|
||||
let _ = DatabaseSnapshotPool(path: "", configuration: Configuration()) // Safe
|
||||
let _ = SerializedDatabase(path: remoteString, defaultLabel: "") // $ hasPathInjection=163
|
||||
let _ = SerializedDatabase(path: "", defaultLabel: "") // Safe
|
||||
let _ = SerializedDatabase(path: remoteString, defaultLabel: "", purpose: nil) // $ hasPathInjection=163
|
||||
let _ = SerializedDatabase(path: "", defaultLabel: "", purpose: nil) // Safe
|
||||
let _ = SerializedDatabase(path: remoteString, configuration: Configuration(), defaultLabel: "") // $ hasPathInjection=163
|
||||
let _ = SerializedDatabase(path: "", configuration: Configuration(), defaultLabel: "") // Safe
|
||||
let _ = SerializedDatabase(path: remoteString, configuration: Configuration(), defaultLabel: "", purpose: nil) // $ hasPathInjection=163
|
||||
let _ = SerializedDatabase(path: "", configuration: Configuration(), defaultLabel: "", purpose: nil) // Safe
|
||||
}
|
||||
|
||||
func testSanitizers() {
|
||||
@@ -167,8 +260,8 @@ func testSanitizers() {
|
||||
let fm = FileManager()
|
||||
|
||||
let filePath = FilePath(stringLiteral: remoteString)
|
||||
if (filePath.lexicallyNormalized().starts(with: FilePath(stringLiteral: "/safe"))) {
|
||||
fm.contents(atPath: remoteString) // Safe
|
||||
if (filePath.lexicallyNormalized().starts(with: "/safe")) {
|
||||
let _ = fm.contents(atPath: remoteString) // Safe
|
||||
}
|
||||
fm.contents(atPath: remoteString) // $ hasPathInjection=165
|
||||
let _ = fm.contents(atPath: remoteString) // $ hasPathInjection=258
|
||||
}
|
||||
|
||||
360
swift/ql/test/query-tests/Security/CWE-089/GRDB.swift
Normal file
360
swift/ql/test/query-tests/Security/CWE-089/GRDB.swift
Normal file
@@ -0,0 +1,360 @@
|
||||
// --- stubs ---
|
||||
|
||||
struct URL
|
||||
{
|
||||
init?(string: String) {}
|
||||
}
|
||||
|
||||
extension String {
|
||||
init(contentsOf: URL) throws {
|
||||
let data = ""
|
||||
|
||||
// ...
|
||||
|
||||
self.init(data)
|
||||
}
|
||||
}
|
||||
|
||||
struct StatementArguments {}
|
||||
class Statement {}
|
||||
protocol RowAdapter {}
|
||||
class RowDecoder {}
|
||||
enum GeneratedColumnQualification { case virtual }
|
||||
struct QueryInterfaceRequest<T> {}
|
||||
|
||||
class Database {
|
||||
func allStatements(sql: String, arguments: StatementArguments? = nil) -> SQLStatementCursor { return SQLStatementCursor(database: self, sql: "", arguments: nil) }
|
||||
func cachedStatement(sql: String) -> Statement { return Statement() }
|
||||
func internalCachedStatement(sql: String) -> Statement { return Statement() }
|
||||
func execute(sql: String, arguments: StatementArguments = StatementArguments()) {}
|
||||
func makeStatement(sql: String) -> Statement { return Statement() }
|
||||
func makeStatement(sql: String, prepFlags: CUnsignedInt) -> Statement { return Statement() }
|
||||
}
|
||||
|
||||
struct SQLRequest {
|
||||
init(stringLiteral: String) {}
|
||||
init(unicodeScalarLiteral: String) {}
|
||||
init(extendedGraphemeClusterLiteral: String) {}
|
||||
init(stringInterpolation: String) {}
|
||||
init(sql: String, arguments: StatementArguments = StatementArguments(), adapter: (any RowAdapter)? = nil, cached: Bool = false) {}
|
||||
}
|
||||
|
||||
struct SQL {
|
||||
init(stringLiteral: String) {}
|
||||
init(unicodeScalarLiteral: String) {}
|
||||
init(extendedGraphemeClusterLiteral: String) {}
|
||||
init(stringInterpolation: String) {}
|
||||
init(sql: String, arguments: StatementArguments = StatementArguments()) {}
|
||||
func append(sql: String, arguments: StatementArguments = StatementArguments()) {}
|
||||
}
|
||||
|
||||
class TableDefinition {
|
||||
func column(sql: String) {}
|
||||
func check(sql: String) {}
|
||||
func constraint(sql: String) {}
|
||||
}
|
||||
|
||||
class TableAlteration {
|
||||
func addColumn(sql: String) {}
|
||||
}
|
||||
|
||||
class ColumnDefinition {
|
||||
func check(sql: String) -> Self { return self }
|
||||
func defaults(sql: String) -> Self { return self }
|
||||
func generatedAs(sql: String, _: GeneratedColumnQualification = .virtual) -> Self { return self }
|
||||
}
|
||||
|
||||
class TableRecord {
|
||||
static func select(sql: String, arguments: StatementArguments = StatementArguments()) -> QueryInterfaceRequest<TableRecord> { QueryInterfaceRequest<TableRecord>() }
|
||||
static func select<RowDecoder>(sql: String, arguments: StatementArguments = StatementArguments(), as: RowDecoder.Type = RowDecoder.self) -> QueryInterfaceRequest<TableRecord>{ QueryInterfaceRequest<TableRecord>() }
|
||||
static func filter(sql: String, arguments: StatementArguments = StatementArguments()) -> QueryInterfaceRequest<TableRecord> { QueryInterfaceRequest<TableRecord>() }
|
||||
static func order(sql: String, arguments: StatementArguments = StatementArguments()) -> QueryInterfaceRequest<TableRecord> { QueryInterfaceRequest<TableRecord>() }
|
||||
}
|
||||
|
||||
struct StatementCache {
|
||||
func statement(_: String) -> Statement { return Statement() }
|
||||
}
|
||||
|
||||
class Row {
|
||||
func fetchCursor(_: Statement, sql: String, arguments: StatementArguments? = nil, adapter: (any RowAdapter)? = nil) {}
|
||||
func fetchAll(_: Statement, sql: String, arguments: StatementArguments? = nil, adapter: (any RowAdapter)? = nil) {}
|
||||
func fetchSet(_: Statement, sql: String, arguments: StatementArguments? = nil, adapter: (any RowAdapter)? = nil) {}
|
||||
func fetchOne(_: Statement, sql: String, arguments: StatementArguments? = nil, adapter: (any RowAdapter)? = nil) {}
|
||||
}
|
||||
|
||||
class DatabaseValueConvertible {
|
||||
func fetchCursor(_: Statement, sql: String, arguments: StatementArguments? = nil, adapter: (any RowAdapter)? = nil) {}
|
||||
func fetchAll(_: Statement, sql: String, arguments: StatementArguments? = nil, adapter: (any RowAdapter)? = nil) {}
|
||||
func fetchSet(_: Statement, sql: String, arguments: StatementArguments? = nil, adapter: (any RowAdapter)? = nil) {}
|
||||
func fetchOne(_: Statement, sql: String, arguments: StatementArguments? = nil, adapter: (any RowAdapter)? = nil) {}
|
||||
}
|
||||
|
||||
class SQLStatementCursor {
|
||||
init(database: Database, sql: String, arguments: StatementArguments?, prepFlags: CUnsignedInt = 0) {}
|
||||
}
|
||||
|
||||
class CommonTableExpression {
|
||||
init(recursive: Bool = false, named: String, columns: [String]? = nil, sql: String, arguments: StatementArguments = StatementArguments()) {}
|
||||
}
|
||||
|
||||
// --- tests ---
|
||||
|
||||
func test(database: Database) throws {
|
||||
let localString = "user"
|
||||
let remoteString = try String(contentsOf: URL(string: "http://example.com/")!)
|
||||
|
||||
let _ = database.allStatements(sql: remoteString) // BAD
|
||||
let _ = database.allStatements(sql: localString) // GOOD
|
||||
let _ = database.allStatements(sql: remoteString, arguments: nil) // BAD
|
||||
let _ = database.allStatements(sql: localString, arguments: nil) // GOOD
|
||||
|
||||
let _ = database.cachedStatement(sql: remoteString) // BAD
|
||||
let _ = database.cachedStatement(sql: localString) // GOOD
|
||||
|
||||
let _ = database.internalCachedStatement(sql: remoteString) // BAD
|
||||
let _ = database.internalCachedStatement(sql: localString) // GOOD
|
||||
|
||||
database.execute(sql: remoteString) // BAD
|
||||
database.execute(sql: localString) // GOOD
|
||||
database.execute(sql: remoteString, arguments: StatementArguments()) // BAD
|
||||
database.execute(sql: localString, arguments: StatementArguments()) // GOOD
|
||||
|
||||
let _ = database.makeStatement(sql: remoteString) // BAD
|
||||
let _ = database.makeStatement(sql: localString) // GOOD
|
||||
let _ = database.makeStatement(sql: remoteString, prepFlags: 0) // BAD
|
||||
let _ = database.makeStatement(sql: localString, prepFlags: 0) // GOOD
|
||||
}
|
||||
|
||||
func testSqlRequest() throws {
|
||||
let localString = "user"
|
||||
let remoteString = try String(contentsOf: URL(string: "http://example.com/")!)
|
||||
|
||||
let _ = SQLRequest(stringLiteral: remoteString) // BAD
|
||||
let _ = SQLRequest(stringLiteral: localString) // GOOD
|
||||
|
||||
let _ = SQLRequest(unicodeScalarLiteral: remoteString) // BAD
|
||||
let _ = SQLRequest(unicodeScalarLiteral: localString) // GOOD
|
||||
|
||||
let _ = SQLRequest(extendedGraphemeClusterLiteral: remoteString) // BAD
|
||||
let _ = SQLRequest(extendedGraphemeClusterLiteral: localString) // GOOD
|
||||
|
||||
let _ = SQLRequest(stringInterpolation: remoteString) // BAD
|
||||
let _ = SQLRequest(stringInterpolation: localString) // GOOD
|
||||
|
||||
let _ = SQLRequest(sql: remoteString) // BAD
|
||||
let _ = SQLRequest(sql: remoteString, arguments: StatementArguments()) // BAD
|
||||
let _ = SQLRequest(sql: remoteString, arguments: StatementArguments(), adapter: nil) // BAD
|
||||
let _ = SQLRequest(sql: remoteString, arguments: StatementArguments(), cached: false) // BAD
|
||||
let _ = SQLRequest(sql: remoteString, arguments: StatementArguments(), adapter: nil, cached: false) // BAD
|
||||
let _ = SQLRequest(sql: remoteString, adapter: nil) // BAD
|
||||
let _ = SQLRequest(sql: remoteString, adapter: nil, cached: false) // BAD
|
||||
let _ = SQLRequest(sql: remoteString, cached: false) // BAD
|
||||
let _ = SQLRequest(sql: localString) // GOOD
|
||||
let _ = SQLRequest(sql: localString, arguments: StatementArguments()) // GOOD
|
||||
let _ = SQLRequest(sql: localString, arguments: StatementArguments(), adapter: nil) // GOOD
|
||||
let _ = SQLRequest(sql: localString, arguments: StatementArguments(), cached: false) // GOOD
|
||||
let _ = SQLRequest(sql: localString, arguments: StatementArguments(), adapter: nil, cached: false) // GOOD
|
||||
let _ = SQLRequest(sql: localString, adapter: nil) // GOOD
|
||||
let _ = SQLRequest(sql: localString, adapter: nil, cached: false) // GOOD
|
||||
let _ = SQLRequest(sql: localString, cached: false) // GOOD
|
||||
}
|
||||
|
||||
func testSql() throws {
|
||||
let localString = "user"
|
||||
let remoteString = try String(contentsOf: URL(string: "http://example.com/")!)
|
||||
|
||||
let _ = SQL(stringLiteral: remoteString) // BAD
|
||||
let _ = SQL(unicodeScalarLiteral: remoteString) // BAD
|
||||
let _ = SQL(extendedGraphemeClusterLiteral: remoteString) // BAD
|
||||
let _ = SQL(stringInterpolation: remoteString) // BAD
|
||||
let _ = SQL(sql: remoteString) // BAD
|
||||
let sql1 = SQL(stringLiteral: "")
|
||||
sql1.append(sql: remoteString) // BAD
|
||||
|
||||
let _ = SQL(stringLiteral: localString) // GOOD
|
||||
let _ = SQL(unicodeScalarLiteral: localString) // GOOD
|
||||
let _ = SQL(extendedGraphemeClusterLiteral: localString) // GOOD
|
||||
let _ = SQL(stringInterpolation: localString) // GOOD
|
||||
let _ = SQL(sql: localString) // GOOD
|
||||
let sql2 = SQL(stringLiteral: "")
|
||||
sql2.append(sql: localString) // GOOD
|
||||
}
|
||||
|
||||
func test(tableDefinition: TableDefinition) throws {
|
||||
let localString = "user"
|
||||
let remoteString = try String(contentsOf: URL(string: "http://example.com/")!)
|
||||
|
||||
tableDefinition.column(sql: remoteString) // BAD
|
||||
tableDefinition.column(sql: localString) // GOOD
|
||||
|
||||
tableDefinition.check(sql: remoteString) // BAD
|
||||
tableDefinition.check(sql: localString) // GOOD
|
||||
|
||||
tableDefinition.constraint(sql: remoteString) // BAD
|
||||
tableDefinition.constraint(sql: localString) // GOOD
|
||||
}
|
||||
|
||||
func test(tableAlteration: TableAlteration) throws {
|
||||
let localString = "user"
|
||||
let remoteString = try String(contentsOf: URL(string: "http://example.com/")!)
|
||||
|
||||
tableAlteration.addColumn(sql: remoteString) // BAD
|
||||
tableAlteration.addColumn(sql: localString) // GOOD
|
||||
}
|
||||
|
||||
func test(columnDefinition: ColumnDefinition) throws {
|
||||
let localString = "user"
|
||||
let remoteString = try String(contentsOf: URL(string: "http://example.com/")!)
|
||||
|
||||
let _ = columnDefinition.check(sql: remoteString) // BAD
|
||||
let _ = columnDefinition.defaults(sql: remoteString) // BAD
|
||||
let _ = columnDefinition.generatedAs(sql: remoteString) // BAD
|
||||
let _ = columnDefinition.generatedAs(sql: remoteString, .virtual) // BAD
|
||||
|
||||
let _ = columnDefinition.check(sql: localString) // GOOD
|
||||
let _ = columnDefinition.defaults(sql: localString) // GOOD
|
||||
let _ = columnDefinition.generatedAs(sql: localString) // GOOD
|
||||
let _ = columnDefinition.generatedAs(sql: localString, .virtual) // GOOD
|
||||
}
|
||||
|
||||
func testTableRecord() throws {
|
||||
let localString = "user"
|
||||
let remoteString = try String(contentsOf: URL(string: "http://example.com/")!)
|
||||
|
||||
let _ = TableRecord.select(sql: remoteString) // BAD
|
||||
let _ = TableRecord.select(sql: remoteString, arguments: StatementArguments()) // BAD
|
||||
let _ = TableRecord.select(sql: localString) // GOOD
|
||||
let _ = TableRecord.select(sql: localString, arguments: StatementArguments()) // GOOD
|
||||
|
||||
let _ = TableRecord.filter(sql: remoteString) // BAD
|
||||
let _ = TableRecord.filter(sql: remoteString, arguments: StatementArguments()) // BAD
|
||||
let _ = TableRecord.filter(sql: localString) // GOOD
|
||||
let _ = TableRecord.filter(sql: localString, arguments: StatementArguments()) // GOOD
|
||||
|
||||
let _ = TableRecord.order(sql: remoteString) // BAD
|
||||
let _ = TableRecord.order(sql: remoteString, arguments: StatementArguments()) // BAD
|
||||
let _ = TableRecord.order(sql: localString) // GOOD
|
||||
let _ = TableRecord.order(sql: localString, arguments: StatementArguments()) // GOOD
|
||||
}
|
||||
|
||||
func test(statementCache: StatementCache) throws {
|
||||
let localString = "user"
|
||||
let remoteString = try String(contentsOf: URL(string: "http://example.com/")!)
|
||||
|
||||
let _ = statementCache.statement(remoteString) // BAD
|
||||
let _ = statementCache.statement(localString) // GOOD
|
||||
}
|
||||
|
||||
func test(row: Row, stmt: Statement) throws {
|
||||
let localString = "user"
|
||||
let remoteString = try String(contentsOf: URL(string: "http://example.com/")!)
|
||||
|
||||
row.fetchCursor(stmt, sql: remoteString) // BAD
|
||||
row.fetchCursor(stmt, sql: remoteString, arguments: StatementArguments()) // BAD
|
||||
row.fetchCursor(stmt, sql: remoteString, adapter: nil) // BAD
|
||||
row.fetchCursor(stmt, sql: remoteString, arguments: StatementArguments(), adapter: nil) // BAD
|
||||
row.fetchCursor(stmt, sql: localString) // GOOD
|
||||
row.fetchCursor(stmt, sql: localString, arguments: StatementArguments()) // GOOD
|
||||
row.fetchCursor(stmt, sql: localString, adapter: nil) // GOOD
|
||||
row.fetchCursor(stmt, sql: localString, arguments: StatementArguments(), adapter: nil) // GOOD
|
||||
|
||||
row.fetchAll(stmt, sql: remoteString) // BAD
|
||||
row.fetchAll(stmt, sql: remoteString, arguments: StatementArguments()) // BAD
|
||||
row.fetchAll(stmt, sql: remoteString, adapter: nil) // BAD
|
||||
row.fetchAll(stmt, sql: remoteString, arguments: StatementArguments(), adapter: nil) // BAD
|
||||
row.fetchAll(stmt, sql: localString) // GOOD
|
||||
row.fetchAll(stmt, sql: localString, arguments: StatementArguments()) // GOOD
|
||||
row.fetchAll(stmt, sql: localString, adapter: nil) // GOOD
|
||||
row.fetchAll(stmt, sql: localString, arguments: StatementArguments(), adapter: nil) // GOOD
|
||||
|
||||
row.fetchOne(stmt, sql: remoteString) // BAD
|
||||
row.fetchOne(stmt, sql: remoteString, arguments: StatementArguments()) // BAD
|
||||
row.fetchOne(stmt, sql: remoteString, adapter: nil) // BAD
|
||||
row.fetchOne(stmt, sql: remoteString, arguments: StatementArguments(), adapter: nil) // BAD
|
||||
row.fetchOne(stmt, sql: localString) // GOOD
|
||||
row.fetchOne(stmt, sql: localString, arguments: StatementArguments()) // GOOD
|
||||
row.fetchOne(stmt, sql: localString, adapter: nil) // GOOD
|
||||
row.fetchOne(stmt, sql: localString, arguments: StatementArguments(), adapter: nil) // GOOD
|
||||
|
||||
row.fetchSet(stmt, sql: remoteString) // BAD
|
||||
row.fetchSet(stmt, sql: remoteString, arguments: StatementArguments()) // BAD
|
||||
row.fetchSet(stmt, sql: remoteString, adapter: nil) // BAD
|
||||
row.fetchSet(stmt, sql: remoteString, arguments: StatementArguments(), adapter: nil) // BAD
|
||||
row.fetchSet(stmt, sql: localString) // GOOD
|
||||
row.fetchSet(stmt, sql: localString, arguments: StatementArguments()) // GOOD
|
||||
row.fetchSet(stmt, sql: localString, adapter: nil) // GOOD
|
||||
row.fetchSet(stmt, sql: localString, arguments: StatementArguments(), adapter: nil) // GOOD
|
||||
}
|
||||
|
||||
func test(databaseValueConvertible: DatabaseValueConvertible, stmt: Statement) throws {
|
||||
let localString = "user"
|
||||
let remoteString = try String(contentsOf: URL(string: "http://example.com/")!)
|
||||
|
||||
databaseValueConvertible.fetchCursor(stmt, sql: remoteString) // BAD
|
||||
databaseValueConvertible.fetchCursor(stmt, sql: remoteString, arguments: StatementArguments()) // BAD
|
||||
databaseValueConvertible.fetchCursor(stmt, sql: remoteString, adapter: nil) // BAD
|
||||
databaseValueConvertible.fetchCursor(stmt, sql: remoteString, arguments: StatementArguments(), adapter: nil) // BAD
|
||||
databaseValueConvertible.fetchCursor(stmt, sql: localString) // GOOD
|
||||
databaseValueConvertible.fetchCursor(stmt, sql: localString, arguments: StatementArguments()) // GOOD
|
||||
databaseValueConvertible.fetchCursor(stmt, sql: localString, adapter: nil) // GOOD
|
||||
databaseValueConvertible.fetchCursor(stmt, sql: localString, arguments: StatementArguments(), adapter: nil) // GOOD
|
||||
|
||||
databaseValueConvertible.fetchAll(stmt, sql: remoteString) // BAD
|
||||
databaseValueConvertible.fetchAll(stmt, sql: remoteString, arguments: StatementArguments()) // BAD
|
||||
databaseValueConvertible.fetchAll(stmt, sql: remoteString, adapter: nil) // BAD
|
||||
databaseValueConvertible.fetchAll(stmt, sql: remoteString, arguments: StatementArguments(), adapter: nil) // BAD
|
||||
databaseValueConvertible.fetchAll(stmt, sql: localString) // GOOD
|
||||
databaseValueConvertible.fetchAll(stmt, sql: localString, arguments: StatementArguments()) // GOOD
|
||||
databaseValueConvertible.fetchAll(stmt, sql: localString, adapter: nil) // GOOD
|
||||
databaseValueConvertible.fetchAll(stmt, sql: localString, arguments: StatementArguments(), adapter: nil) // GOOD
|
||||
|
||||
databaseValueConvertible.fetchOne(stmt, sql: remoteString) // BAD
|
||||
databaseValueConvertible.fetchOne(stmt, sql: remoteString, arguments: StatementArguments()) // BAD
|
||||
databaseValueConvertible.fetchOne(stmt, sql: remoteString, adapter: nil) // BAD
|
||||
databaseValueConvertible.fetchOne(stmt, sql: remoteString, arguments: StatementArguments(), adapter: nil) // BAD
|
||||
databaseValueConvertible.fetchOne(stmt, sql: localString) // GOOD
|
||||
databaseValueConvertible.fetchOne(stmt, sql: localString, arguments: StatementArguments()) // GOOD
|
||||
databaseValueConvertible.fetchOne(stmt, sql: localString, adapter: nil) // GOOD
|
||||
databaseValueConvertible.fetchOne(stmt, sql: localString, arguments: StatementArguments(), adapter: nil) // GOOD
|
||||
|
||||
databaseValueConvertible.fetchSet(stmt, sql: remoteString) // BAD
|
||||
databaseValueConvertible.fetchSet(stmt, sql: remoteString, arguments: StatementArguments()) // BAD
|
||||
databaseValueConvertible.fetchSet(stmt, sql: remoteString, adapter: nil) // BAD
|
||||
databaseValueConvertible.fetchSet(stmt, sql: remoteString, arguments: StatementArguments(), adapter: nil) // BAD
|
||||
databaseValueConvertible.fetchSet(stmt, sql: localString) // GOOD
|
||||
databaseValueConvertible.fetchSet(stmt, sql: localString, arguments: StatementArguments()) // GOOD
|
||||
databaseValueConvertible.fetchSet(stmt, sql: localString, adapter: nil) // GOOD
|
||||
databaseValueConvertible.fetchSet(stmt, sql: localString, arguments: StatementArguments(), adapter: nil) // GOOD
|
||||
}
|
||||
|
||||
func testSqlStatementCursor(database: Database) throws {
|
||||
let localString = "user"
|
||||
let remoteString = try String(contentsOf: URL(string: "http://example.com/")!)
|
||||
|
||||
let _ = SQLStatementCursor(database: database, sql: remoteString, arguments: StatementArguments()) // BAD
|
||||
let _ = SQLStatementCursor(database: database, sql: remoteString, arguments: StatementArguments(), prepFlags: 0) // BAD
|
||||
let _ = SQLStatementCursor(database: database, sql: localString, arguments: StatementArguments()) // GOOD
|
||||
let _ = SQLStatementCursor(database: database, sql: localString, arguments: StatementArguments(), prepFlags: 0) // GOOD
|
||||
}
|
||||
|
||||
func testCommonTableExpression() throws {
|
||||
let localString = "user"
|
||||
let remoteString = try String(contentsOf: URL(string: "http://example.com/")!)
|
||||
|
||||
let _ = CommonTableExpression(named: "", sql: remoteString) // BAD
|
||||
let _ = CommonTableExpression(named: "", sql: remoteString, arguments: StatementArguments()) // BAD
|
||||
let _ = CommonTableExpression(named: "", columns: [""], sql: remoteString) // BAD
|
||||
let _ = CommonTableExpression(named: "", columns: [""], sql: remoteString, arguments: StatementArguments()) // BAD
|
||||
let _ = CommonTableExpression(recursive: false, named: "", sql: remoteString) // BAD
|
||||
let _ = CommonTableExpression(recursive: false, named: "", columns: [""], sql: remoteString) // BAD
|
||||
let _ = CommonTableExpression(recursive: false, named: "", sql: remoteString, arguments: StatementArguments()) // BAD
|
||||
let _ = CommonTableExpression(recursive: false, named: "", columns: [""], sql: remoteString, arguments: StatementArguments()) // BAD
|
||||
let _ = CommonTableExpression(named: "", sql: localString) // GOOD
|
||||
let _ = CommonTableExpression(named: "", sql: localString, arguments: StatementArguments()) // GOOD
|
||||
let _ = CommonTableExpression(named: "", columns: [""], sql: localString) // GOOD
|
||||
let _ = CommonTableExpression(named: "", columns: [""], sql: localString, arguments: StatementArguments()) // GOOD
|
||||
let _ = CommonTableExpression(recursive: false, named: "", sql: localString) // GOOD
|
||||
let _ = CommonTableExpression(recursive: false, named: "", columns: [""], sql: localString) // GOOD
|
||||
let _ = CommonTableExpression(recursive: false, named: "", sql: localString, arguments: StatementArguments()) // GOOD
|
||||
let _ = CommonTableExpression(recursive: false, named: "", columns: [""], sql: localString, arguments: StatementArguments()) // GOOD
|
||||
}
|
||||
@@ -1,4 +1,87 @@
|
||||
edges
|
||||
| GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) : | GRDB.swift:106:41:106:41 | remoteString |
|
||||
| GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) : | GRDB.swift:108:41:108:41 | remoteString |
|
||||
| GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) : | GRDB.swift:111:43:111:43 | remoteString |
|
||||
| GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) : | GRDB.swift:114:51:114:51 | remoteString |
|
||||
| GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) : | GRDB.swift:117:27:117:27 | remoteString |
|
||||
| GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) : | GRDB.swift:119:27:119:27 | remoteString |
|
||||
| GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) : | GRDB.swift:122:41:122:41 | remoteString |
|
||||
| GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) : | GRDB.swift:124:41:124:41 | remoteString |
|
||||
| GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) : | GRDB.swift:132:39:132:39 | remoteString |
|
||||
| GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) : | GRDB.swift:135:46:135:46 | remoteString |
|
||||
| GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) : | GRDB.swift:138:56:138:56 | remoteString |
|
||||
| GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) : | GRDB.swift:141:45:141:45 | remoteString |
|
||||
| GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) : | GRDB.swift:144:29:144:29 | remoteString |
|
||||
| GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) : | GRDB.swift:145:29:145:29 | remoteString |
|
||||
| GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) : | GRDB.swift:146:29:146:29 | remoteString |
|
||||
| GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) : | GRDB.swift:147:29:147:29 | remoteString |
|
||||
| GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) : | GRDB.swift:148:29:148:29 | remoteString |
|
||||
| GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) : | GRDB.swift:149:29:149:29 | remoteString |
|
||||
| GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) : | GRDB.swift:150:29:150:29 | remoteString |
|
||||
| GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) : | GRDB.swift:151:29:151:29 | remoteString |
|
||||
| GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) : | GRDB.swift:166:32:166:32 | remoteString |
|
||||
| GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) : | GRDB.swift:167:39:167:39 | remoteString |
|
||||
| GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) : | GRDB.swift:168:49:168:49 | remoteString |
|
||||
| GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) : | GRDB.swift:169:38:169:38 | remoteString |
|
||||
| GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) : | GRDB.swift:170:22:170:22 | remoteString |
|
||||
| GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) : | GRDB.swift:172:22:172:22 | remoteString |
|
||||
| GRDB.swift:185:26:185:80 | call to String.init(contentsOf:) : | GRDB.swift:187:33:187:33 | remoteString |
|
||||
| GRDB.swift:185:26:185:80 | call to String.init(contentsOf:) : | GRDB.swift:190:32:190:32 | remoteString |
|
||||
| GRDB.swift:185:26:185:80 | call to String.init(contentsOf:) : | GRDB.swift:193:37:193:37 | remoteString |
|
||||
| GRDB.swift:199:26:199:80 | call to String.init(contentsOf:) : | GRDB.swift:201:36:201:36 | remoteString |
|
||||
| GRDB.swift:207:26:207:80 | call to String.init(contentsOf:) : | GRDB.swift:209:41:209:41 | remoteString |
|
||||
| GRDB.swift:207:26:207:80 | call to String.init(contentsOf:) : | GRDB.swift:210:44:210:44 | remoteString |
|
||||
| GRDB.swift:207:26:207:80 | call to String.init(contentsOf:) : | GRDB.swift:211:47:211:47 | remoteString |
|
||||
| GRDB.swift:207:26:207:80 | call to String.init(contentsOf:) : | GRDB.swift:212:47:212:47 | remoteString |
|
||||
| GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) : | GRDB.swift:224:37:224:37 | remoteString |
|
||||
| GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) : | GRDB.swift:225:37:225:37 | remoteString |
|
||||
| GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) : | GRDB.swift:229:37:229:37 | remoteString |
|
||||
| GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) : | GRDB.swift:230:37:230:37 | remoteString |
|
||||
| GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) : | GRDB.swift:234:36:234:36 | remoteString |
|
||||
| GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) : | GRDB.swift:235:36:235:36 | remoteString |
|
||||
| GRDB.swift:242:26:242:80 | call to String.init(contentsOf:) : | GRDB.swift:244:38:244:38 | remoteString |
|
||||
| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) : | GRDB.swift:252:32:252:32 | remoteString |
|
||||
| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) : | GRDB.swift:253:32:253:32 | remoteString |
|
||||
| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) : | GRDB.swift:254:32:254:32 | remoteString |
|
||||
| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) : | GRDB.swift:255:32:255:32 | remoteString |
|
||||
| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) : | GRDB.swift:261:29:261:29 | remoteString |
|
||||
| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) : | GRDB.swift:262:29:262:29 | remoteString |
|
||||
| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) : | GRDB.swift:263:29:263:29 | remoteString |
|
||||
| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) : | GRDB.swift:264:29:264:29 | remoteString |
|
||||
| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) : | GRDB.swift:270:29:270:29 | remoteString |
|
||||
| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) : | GRDB.swift:271:29:271:29 | remoteString |
|
||||
| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) : | GRDB.swift:272:29:272:29 | remoteString |
|
||||
| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) : | GRDB.swift:273:29:273:29 | remoteString |
|
||||
| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) : | GRDB.swift:279:29:279:29 | remoteString |
|
||||
| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) : | GRDB.swift:280:29:280:29 | remoteString |
|
||||
| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) : | GRDB.swift:281:29:281:29 | remoteString |
|
||||
| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) : | GRDB.swift:282:29:282:29 | remoteString |
|
||||
| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) : | GRDB.swift:293:53:293:53 | remoteString |
|
||||
| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) : | GRDB.swift:294:53:294:53 | remoteString |
|
||||
| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) : | GRDB.swift:295:53:295:53 | remoteString |
|
||||
| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) : | GRDB.swift:296:53:296:53 | remoteString |
|
||||
| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) : | GRDB.swift:302:50:302:50 | remoteString |
|
||||
| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) : | GRDB.swift:303:50:303:50 | remoteString |
|
||||
| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) : | GRDB.swift:304:50:304:50 | remoteString |
|
||||
| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) : | GRDB.swift:305:50:305:50 | remoteString |
|
||||
| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) : | GRDB.swift:311:50:311:50 | remoteString |
|
||||
| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) : | GRDB.swift:312:50:312:50 | remoteString |
|
||||
| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) : | GRDB.swift:313:50:313:50 | remoteString |
|
||||
| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) : | GRDB.swift:314:50:314:50 | remoteString |
|
||||
| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) : | GRDB.swift:320:50:320:50 | remoteString |
|
||||
| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) : | GRDB.swift:321:50:321:50 | remoteString |
|
||||
| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) : | GRDB.swift:322:50:322:50 | remoteString |
|
||||
| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) : | GRDB.swift:323:50:323:50 | remoteString |
|
||||
| GRDB.swift:332:26:332:80 | call to String.init(contentsOf:) : | GRDB.swift:334:57:334:57 | remoteString |
|
||||
| GRDB.swift:332:26:332:80 | call to String.init(contentsOf:) : | GRDB.swift:335:57:335:57 | remoteString |
|
||||
| GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) : | GRDB.swift:344:51:344:51 | remoteString |
|
||||
| GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) : | GRDB.swift:345:51:345:51 | remoteString |
|
||||
| GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) : | GRDB.swift:346:66:346:66 | remoteString |
|
||||
| GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) : | GRDB.swift:347:66:347:66 | remoteString |
|
||||
| GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) : | GRDB.swift:348:69:348:69 | remoteString |
|
||||
| GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) : | GRDB.swift:349:84:349:84 | remoteString |
|
||||
| GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) : | GRDB.swift:350:69:350:69 | remoteString |
|
||||
| GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) : | GRDB.swift:351:84:351:84 | remoteString |
|
||||
| SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) : | SQLite.swift:73:17:73:17 | unsafeQuery1 |
|
||||
| SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) : | SQLite.swift:74:17:74:17 | unsafeQuery2 |
|
||||
| SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) : | SQLite.swift:75:17:75:17 | unsafeQuery3 |
|
||||
@@ -21,6 +104,101 @@ edges
|
||||
| sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) : | sqlite3_c_api.swift:175:29:175:29 | unsafeQuery3 |
|
||||
| sqlite3_c_api.swift:122:26:122:80 | call to String.init(contentsOf:) : | sqlite3_c_api.swift:183:29:183:29 | unsafeQuery3 |
|
||||
nodes
|
||||
| GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) : | semmle.label | call to String.init(contentsOf:) : |
|
||||
| GRDB.swift:106:41:106:41 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:108:41:108:41 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:111:43:111:43 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:114:51:114:51 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:117:27:117:27 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:119:27:119:27 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:122:41:122:41 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:124:41:124:41 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) : | semmle.label | call to String.init(contentsOf:) : |
|
||||
| GRDB.swift:132:39:132:39 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:135:46:135:46 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:138:56:138:56 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:141:45:141:45 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:144:29:144:29 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:145:29:145:29 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:146:29:146:29 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:147:29:147:29 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:148:29:148:29 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:149:29:149:29 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:150:29:150:29 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:151:29:151:29 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) : | semmle.label | call to String.init(contentsOf:) : |
|
||||
| GRDB.swift:166:32:166:32 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:167:39:167:39 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:168:49:168:49 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:169:38:169:38 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:170:22:170:22 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:172:22:172:22 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:185:26:185:80 | call to String.init(contentsOf:) : | semmle.label | call to String.init(contentsOf:) : |
|
||||
| GRDB.swift:187:33:187:33 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:190:32:190:32 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:193:37:193:37 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:199:26:199:80 | call to String.init(contentsOf:) : | semmle.label | call to String.init(contentsOf:) : |
|
||||
| GRDB.swift:201:36:201:36 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:207:26:207:80 | call to String.init(contentsOf:) : | semmle.label | call to String.init(contentsOf:) : |
|
||||
| GRDB.swift:209:41:209:41 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:210:44:210:44 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:211:47:211:47 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:212:47:212:47 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) : | semmle.label | call to String.init(contentsOf:) : |
|
||||
| GRDB.swift:224:37:224:37 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:225:37:225:37 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:229:37:229:37 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:230:37:230:37 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:234:36:234:36 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:235:36:235:36 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:242:26:242:80 | call to String.init(contentsOf:) : | semmle.label | call to String.init(contentsOf:) : |
|
||||
| GRDB.swift:244:38:244:38 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) : | semmle.label | call to String.init(contentsOf:) : |
|
||||
| GRDB.swift:252:32:252:32 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:253:32:253:32 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:254:32:254:32 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:255:32:255:32 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:261:29:261:29 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:262:29:262:29 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:263:29:263:29 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:264:29:264:29 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:270:29:270:29 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:271:29:271:29 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:272:29:272:29 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:273:29:273:29 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:279:29:279:29 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:280:29:280:29 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:281:29:281:29 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:282:29:282:29 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) : | semmle.label | call to String.init(contentsOf:) : |
|
||||
| GRDB.swift:293:53:293:53 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:294:53:294:53 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:295:53:295:53 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:296:53:296:53 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:302:50:302:50 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:303:50:303:50 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:304:50:304:50 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:305:50:305:50 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:311:50:311:50 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:312:50:312:50 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:313:50:313:50 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:314:50:314:50 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:320:50:320:50 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:321:50:321:50 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:322:50:322:50 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:323:50:323:50 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:332:26:332:80 | call to String.init(contentsOf:) : | semmle.label | call to String.init(contentsOf:) : |
|
||||
| GRDB.swift:334:57:334:57 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:335:57:335:57 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) : | semmle.label | call to String.init(contentsOf:) : |
|
||||
| GRDB.swift:344:51:344:51 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:345:51:345:51 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:346:66:346:66 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:347:66:347:66 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:348:69:348:69 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:349:84:349:84 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:350:69:350:69 | remoteString | semmle.label | remoteString |
|
||||
| GRDB.swift:351:84:351:84 | remoteString | semmle.label | remoteString |
|
||||
| SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) : | semmle.label | call to String.init(contentsOf:) : |
|
||||
| SQLite.swift:73:17:73:17 | unsafeQuery1 | semmle.label | unsafeQuery1 |
|
||||
| SQLite.swift:74:17:74:17 | unsafeQuery2 | semmle.label | unsafeQuery2 |
|
||||
@@ -46,6 +224,89 @@ nodes
|
||||
| sqlite3_c_api.swift:183:29:183:29 | unsafeQuery3 | semmle.label | unsafeQuery3 |
|
||||
subpaths
|
||||
#select
|
||||
| GRDB.swift:106:41:106:41 | remoteString | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) : | GRDB.swift:106:41:106:41 | remoteString | This query depends on a $@. | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:108:41:108:41 | remoteString | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) : | GRDB.swift:108:41:108:41 | remoteString | This query depends on a $@. | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:111:43:111:43 | remoteString | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) : | GRDB.swift:111:43:111:43 | remoteString | This query depends on a $@. | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:114:51:114:51 | remoteString | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) : | GRDB.swift:114:51:114:51 | remoteString | This query depends on a $@. | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:117:27:117:27 | remoteString | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) : | GRDB.swift:117:27:117:27 | remoteString | This query depends on a $@. | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:119:27:119:27 | remoteString | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) : | GRDB.swift:119:27:119:27 | remoteString | This query depends on a $@. | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:122:41:122:41 | remoteString | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) : | GRDB.swift:122:41:122:41 | remoteString | This query depends on a $@. | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:124:41:124:41 | remoteString | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) : | GRDB.swift:124:41:124:41 | remoteString | This query depends on a $@. | GRDB.swift:104:25:104:79 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:132:39:132:39 | remoteString | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) : | GRDB.swift:132:39:132:39 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:135:46:135:46 | remoteString | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) : | GRDB.swift:135:46:135:46 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:138:56:138:56 | remoteString | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) : | GRDB.swift:138:56:138:56 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:141:45:141:45 | remoteString | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) : | GRDB.swift:141:45:141:45 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:144:29:144:29 | remoteString | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) : | GRDB.swift:144:29:144:29 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:145:29:145:29 | remoteString | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) : | GRDB.swift:145:29:145:29 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:146:29:146:29 | remoteString | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) : | GRDB.swift:146:29:146:29 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:147:29:147:29 | remoteString | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) : | GRDB.swift:147:29:147:29 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:148:29:148:29 | remoteString | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) : | GRDB.swift:148:29:148:29 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:149:29:149:29 | remoteString | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) : | GRDB.swift:149:29:149:29 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:150:29:150:29 | remoteString | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) : | GRDB.swift:150:29:150:29 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:151:29:151:29 | remoteString | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) : | GRDB.swift:151:29:151:29 | remoteString | This query depends on a $@. | GRDB.swift:130:26:130:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:166:32:166:32 | remoteString | GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) : | GRDB.swift:166:32:166:32 | remoteString | This query depends on a $@. | GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:167:39:167:39 | remoteString | GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) : | GRDB.swift:167:39:167:39 | remoteString | This query depends on a $@. | GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:168:49:168:49 | remoteString | GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) : | GRDB.swift:168:49:168:49 | remoteString | This query depends on a $@. | GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:169:38:169:38 | remoteString | GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) : | GRDB.swift:169:38:169:38 | remoteString | This query depends on a $@. | GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:170:22:170:22 | remoteString | GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) : | GRDB.swift:170:22:170:22 | remoteString | This query depends on a $@. | GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:172:22:172:22 | remoteString | GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) : | GRDB.swift:172:22:172:22 | remoteString | This query depends on a $@. | GRDB.swift:164:26:164:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:187:33:187:33 | remoteString | GRDB.swift:185:26:185:80 | call to String.init(contentsOf:) : | GRDB.swift:187:33:187:33 | remoteString | This query depends on a $@. | GRDB.swift:185:26:185:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:190:32:190:32 | remoteString | GRDB.swift:185:26:185:80 | call to String.init(contentsOf:) : | GRDB.swift:190:32:190:32 | remoteString | This query depends on a $@. | GRDB.swift:185:26:185:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:193:37:193:37 | remoteString | GRDB.swift:185:26:185:80 | call to String.init(contentsOf:) : | GRDB.swift:193:37:193:37 | remoteString | This query depends on a $@. | GRDB.swift:185:26:185:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:201:36:201:36 | remoteString | GRDB.swift:199:26:199:80 | call to String.init(contentsOf:) : | GRDB.swift:201:36:201:36 | remoteString | This query depends on a $@. | GRDB.swift:199:26:199:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:209:41:209:41 | remoteString | GRDB.swift:207:26:207:80 | call to String.init(contentsOf:) : | GRDB.swift:209:41:209:41 | remoteString | This query depends on a $@. | GRDB.swift:207:26:207:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:210:44:210:44 | remoteString | GRDB.swift:207:26:207:80 | call to String.init(contentsOf:) : | GRDB.swift:210:44:210:44 | remoteString | This query depends on a $@. | GRDB.swift:207:26:207:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:211:47:211:47 | remoteString | GRDB.swift:207:26:207:80 | call to String.init(contentsOf:) : | GRDB.swift:211:47:211:47 | remoteString | This query depends on a $@. | GRDB.swift:207:26:207:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:212:47:212:47 | remoteString | GRDB.swift:207:26:207:80 | call to String.init(contentsOf:) : | GRDB.swift:212:47:212:47 | remoteString | This query depends on a $@. | GRDB.swift:207:26:207:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:224:37:224:37 | remoteString | GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) : | GRDB.swift:224:37:224:37 | remoteString | This query depends on a $@. | GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:225:37:225:37 | remoteString | GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) : | GRDB.swift:225:37:225:37 | remoteString | This query depends on a $@. | GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:229:37:229:37 | remoteString | GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) : | GRDB.swift:229:37:229:37 | remoteString | This query depends on a $@. | GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:230:37:230:37 | remoteString | GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) : | GRDB.swift:230:37:230:37 | remoteString | This query depends on a $@. | GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:234:36:234:36 | remoteString | GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) : | GRDB.swift:234:36:234:36 | remoteString | This query depends on a $@. | GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:235:36:235:36 | remoteString | GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) : | GRDB.swift:235:36:235:36 | remoteString | This query depends on a $@. | GRDB.swift:222:26:222:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:244:38:244:38 | remoteString | GRDB.swift:242:26:242:80 | call to String.init(contentsOf:) : | GRDB.swift:244:38:244:38 | remoteString | This query depends on a $@. | GRDB.swift:242:26:242:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:252:32:252:32 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) : | GRDB.swift:252:32:252:32 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:253:32:253:32 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) : | GRDB.swift:253:32:253:32 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:254:32:254:32 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) : | GRDB.swift:254:32:254:32 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:255:32:255:32 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) : | GRDB.swift:255:32:255:32 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:261:29:261:29 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) : | GRDB.swift:261:29:261:29 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:262:29:262:29 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) : | GRDB.swift:262:29:262:29 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:263:29:263:29 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) : | GRDB.swift:263:29:263:29 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:264:29:264:29 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) : | GRDB.swift:264:29:264:29 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:270:29:270:29 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) : | GRDB.swift:270:29:270:29 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:271:29:271:29 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) : | GRDB.swift:271:29:271:29 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:272:29:272:29 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) : | GRDB.swift:272:29:272:29 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:273:29:273:29 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) : | GRDB.swift:273:29:273:29 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:279:29:279:29 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) : | GRDB.swift:279:29:279:29 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:280:29:280:29 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) : | GRDB.swift:280:29:280:29 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:281:29:281:29 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) : | GRDB.swift:281:29:281:29 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:282:29:282:29 | remoteString | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) : | GRDB.swift:282:29:282:29 | remoteString | This query depends on a $@. | GRDB.swift:250:26:250:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:293:53:293:53 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) : | GRDB.swift:293:53:293:53 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:294:53:294:53 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) : | GRDB.swift:294:53:294:53 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:295:53:295:53 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) : | GRDB.swift:295:53:295:53 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:296:53:296:53 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) : | GRDB.swift:296:53:296:53 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:302:50:302:50 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) : | GRDB.swift:302:50:302:50 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:303:50:303:50 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) : | GRDB.swift:303:50:303:50 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:304:50:304:50 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) : | GRDB.swift:304:50:304:50 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:305:50:305:50 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) : | GRDB.swift:305:50:305:50 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:311:50:311:50 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) : | GRDB.swift:311:50:311:50 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:312:50:312:50 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) : | GRDB.swift:312:50:312:50 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:313:50:313:50 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) : | GRDB.swift:313:50:313:50 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:314:50:314:50 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) : | GRDB.swift:314:50:314:50 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:320:50:320:50 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) : | GRDB.swift:320:50:320:50 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:321:50:321:50 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) : | GRDB.swift:321:50:321:50 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:322:50:322:50 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) : | GRDB.swift:322:50:322:50 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:323:50:323:50 | remoteString | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) : | GRDB.swift:323:50:323:50 | remoteString | This query depends on a $@. | GRDB.swift:291:26:291:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:334:57:334:57 | remoteString | GRDB.swift:332:26:332:80 | call to String.init(contentsOf:) : | GRDB.swift:334:57:334:57 | remoteString | This query depends on a $@. | GRDB.swift:332:26:332:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:335:57:335:57 | remoteString | GRDB.swift:332:26:332:80 | call to String.init(contentsOf:) : | GRDB.swift:335:57:335:57 | remoteString | This query depends on a $@. | GRDB.swift:332:26:332:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:344:51:344:51 | remoteString | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) : | GRDB.swift:344:51:344:51 | remoteString | This query depends on a $@. | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:345:51:345:51 | remoteString | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) : | GRDB.swift:345:51:345:51 | remoteString | This query depends on a $@. | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:346:66:346:66 | remoteString | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) : | GRDB.swift:346:66:346:66 | remoteString | This query depends on a $@. | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:347:66:347:66 | remoteString | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) : | GRDB.swift:347:66:347:66 | remoteString | This query depends on a $@. | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:348:69:348:69 | remoteString | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) : | GRDB.swift:348:69:348:69 | remoteString | This query depends on a $@. | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:349:84:349:84 | remoteString | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) : | GRDB.swift:349:84:349:84 | remoteString | This query depends on a $@. | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:350:69:350:69 | remoteString | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) : | GRDB.swift:350:69:350:69 | remoteString | This query depends on a $@. | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| GRDB.swift:351:84:351:84 | remoteString | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) : | GRDB.swift:351:84:351:84 | remoteString | This query depends on a $@. | GRDB.swift:342:26:342:80 | call to String.init(contentsOf:) | user-provided value |
|
||||
| SQLite.swift:73:17:73:17 | unsafeQuery1 | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) : | SQLite.swift:73:17:73:17 | unsafeQuery1 | This query depends on a $@. | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | user-provided value |
|
||||
| SQLite.swift:74:17:74:17 | unsafeQuery2 | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) : | SQLite.swift:74:17:74:17 | unsafeQuery2 | This query depends on a $@. | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | user-provided value |
|
||||
| SQLite.swift:75:17:75:17 | unsafeQuery3 | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) : | SQLite.swift:75:17:75:17 | unsafeQuery3 | This query depends on a $@. | SQLite.swift:62:25:62:79 | call to String.init(contentsOf:) | user-provided value |
|
||||
|
||||
@@ -9,6 +9,57 @@ edges
|
||||
| testCoreData.swift:91:10:91:10 | passwd : | testCoreData.swift:95:15:95:15 | x |
|
||||
| testCoreData.swift:92:10:92:10 | passwd : | testCoreData.swift:96:15:96:15 | y |
|
||||
| testCoreData.swift:93:10:93:10 | passwd : | testCoreData.swift:97:15:97:15 | z |
|
||||
| testGRDB.swift:73:57:73:57 | password : | testGRDB.swift:73:56:73:65 | [...] |
|
||||
| testGRDB.swift:76:43:76:43 | password : | testGRDB.swift:76:42:76:51 | [...] |
|
||||
| testGRDB.swift:81:45:81:45 | password : | testGRDB.swift:81:44:81:53 | [...] |
|
||||
| testGRDB.swift:83:45:83:45 | password : | testGRDB.swift:83:44:83:53 | [...] |
|
||||
| testGRDB.swift:85:45:85:45 | password : | testGRDB.swift:85:44:85:53 | [...] |
|
||||
| testGRDB.swift:87:45:87:45 | password : | testGRDB.swift:87:44:87:53 | [...] |
|
||||
| testGRDB.swift:92:38:92:38 | password : | testGRDB.swift:92:37:92:46 | [...] |
|
||||
| testGRDB.swift:95:37:95:37 | password : | testGRDB.swift:95:36:95:45 | [...] |
|
||||
| testGRDB.swift:100:73:100:73 | password : | testGRDB.swift:100:72:100:81 | [...] |
|
||||
| testGRDB.swift:101:73:101:73 | password : | testGRDB.swift:101:72:101:81 | [...] |
|
||||
| testGRDB.swift:107:53:107:53 | password : | testGRDB.swift:107:52:107:61 | [...] |
|
||||
| testGRDB.swift:109:53:109:53 | password : | testGRDB.swift:109:52:109:61 | [...] |
|
||||
| testGRDB.swift:111:52:111:52 | password : | testGRDB.swift:111:51:111:60 | [...] |
|
||||
| testGRDB.swift:116:48:116:48 | password : | testGRDB.swift:116:47:116:56 | [...] |
|
||||
| testGRDB.swift:118:48:118:48 | password : | testGRDB.swift:118:47:118:56 | [...] |
|
||||
| testGRDB.swift:121:45:121:45 | password : | testGRDB.swift:121:44:121:53 | [...] |
|
||||
| testGRDB.swift:123:45:123:45 | password : | testGRDB.swift:123:44:123:53 | [...] |
|
||||
| testGRDB.swift:126:45:126:45 | password : | testGRDB.swift:126:44:126:53 | [...] |
|
||||
| testGRDB.swift:128:45:128:45 | password : | testGRDB.swift:128:44:128:53 | [...] |
|
||||
| testGRDB.swift:131:45:131:45 | password : | testGRDB.swift:131:44:131:53 | [...] |
|
||||
| testGRDB.swift:133:45:133:45 | password : | testGRDB.swift:133:44:133:53 | [...] |
|
||||
| testGRDB.swift:138:69:138:69 | password : | testGRDB.swift:138:68:138:77 | [...] |
|
||||
| testGRDB.swift:140:69:140:69 | password : | testGRDB.swift:140:68:140:77 | [...] |
|
||||
| testGRDB.swift:143:66:143:66 | password : | testGRDB.swift:143:65:143:74 | [...] |
|
||||
| testGRDB.swift:145:66:145:66 | password : | testGRDB.swift:145:65:145:74 | [...] |
|
||||
| testGRDB.swift:148:66:148:66 | password : | testGRDB.swift:148:65:148:74 | [...] |
|
||||
| testGRDB.swift:150:66:150:66 | password : | testGRDB.swift:150:65:150:74 | [...] |
|
||||
| testGRDB.swift:153:66:153:66 | password : | testGRDB.swift:153:65:153:74 | [...] |
|
||||
| testGRDB.swift:155:66:155:66 | password : | testGRDB.swift:155:65:155:74 | [...] |
|
||||
| testGRDB.swift:160:60:160:60 | password : | testGRDB.swift:160:59:160:68 | [...] |
|
||||
| testGRDB.swift:161:51:161:51 | password : | testGRDB.swift:161:50:161:59 | [...] |
|
||||
| testGRDB.swift:164:60:164:60 | password : | testGRDB.swift:164:59:164:68 | [...] |
|
||||
| testGRDB.swift:165:51:165:51 | password : | testGRDB.swift:165:50:165:59 | [...] |
|
||||
| testGRDB.swift:169:57:169:57 | password : | testGRDB.swift:169:56:169:65 | [...] |
|
||||
| testGRDB.swift:170:48:170:48 | password : | testGRDB.swift:170:47:170:56 | [...] |
|
||||
| testGRDB.swift:173:57:173:57 | password : | testGRDB.swift:173:56:173:65 | [...] |
|
||||
| testGRDB.swift:174:48:174:48 | password : | testGRDB.swift:174:47:174:56 | [...] |
|
||||
| testGRDB.swift:178:57:178:57 | password : | testGRDB.swift:178:56:178:65 | [...] |
|
||||
| testGRDB.swift:179:48:179:48 | password : | testGRDB.swift:179:47:179:56 | [...] |
|
||||
| testGRDB.swift:182:57:182:57 | password : | testGRDB.swift:182:56:182:65 | [...] |
|
||||
| testGRDB.swift:183:48:183:48 | password : | testGRDB.swift:183:47:183:56 | [...] |
|
||||
| testGRDB.swift:187:57:187:57 | password : | testGRDB.swift:187:56:187:65 | [...] |
|
||||
| testGRDB.swift:188:48:188:48 | password : | testGRDB.swift:188:47:188:56 | [...] |
|
||||
| testGRDB.swift:191:57:191:57 | password : | testGRDB.swift:191:56:191:65 | [...] |
|
||||
| testGRDB.swift:192:48:192:48 | password : | testGRDB.swift:192:47:192:56 | [...] |
|
||||
| testGRDB.swift:198:30:198:30 | password : | testGRDB.swift:198:29:198:38 | [...] |
|
||||
| testGRDB.swift:201:24:201:24 | password : | testGRDB.swift:201:23:201:32 | [...] |
|
||||
| testGRDB.swift:206:67:206:67 | password : | testGRDB.swift:206:66:206:75 | [...] |
|
||||
| testGRDB.swift:208:81:208:81 | password : | testGRDB.swift:208:80:208:89 | [...] |
|
||||
| testGRDB.swift:210:85:210:85 | password : | testGRDB.swift:210:84:210:93 | [...] |
|
||||
| testGRDB.swift:212:99:212:99 | password : | testGRDB.swift:212:98:212:107 | [...] |
|
||||
| testRealm.swift:16:6:16:6 | value : | file://:0:0:0:0 | value : |
|
||||
| testRealm.swift:34:2:34:2 | [post] a [data] : | testRealm.swift:34:2:34:2 | [post] a |
|
||||
| testRealm.swift:34:11:34:11 | myPassword : | testRealm.swift:16:6:16:6 | value : |
|
||||
@@ -45,6 +96,108 @@ nodes
|
||||
| testCoreData.swift:95:15:95:15 | x | semmle.label | x |
|
||||
| testCoreData.swift:96:15:96:15 | y | semmle.label | y |
|
||||
| testCoreData.swift:97:15:97:15 | z | semmle.label | z |
|
||||
| testGRDB.swift:73:56:73:65 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:73:57:73:57 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:76:42:76:51 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:76:43:76:43 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:81:44:81:53 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:81:45:81:45 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:83:44:83:53 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:83:45:83:45 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:85:44:85:53 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:85:45:85:45 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:87:44:87:53 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:87:45:87:45 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:92:37:92:46 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:92:38:92:38 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:95:36:95:45 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:95:37:95:37 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:100:72:100:81 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:100:73:100:73 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:101:72:101:81 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:101:73:101:73 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:107:52:107:61 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:107:53:107:53 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:109:52:109:61 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:109:53:109:53 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:111:51:111:60 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:111:52:111:52 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:116:47:116:56 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:116:48:116:48 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:118:47:118:56 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:118:48:118:48 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:121:44:121:53 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:121:45:121:45 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:123:44:123:53 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:123:45:123:45 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:126:44:126:53 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:126:45:126:45 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:128:44:128:53 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:128:45:128:45 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:131:44:131:53 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:131:45:131:45 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:133:44:133:53 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:133:45:133:45 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:138:68:138:77 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:138:69:138:69 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:140:68:140:77 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:140:69:140:69 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:143:65:143:74 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:143:66:143:66 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:145:65:145:74 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:145:66:145:66 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:148:65:148:74 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:148:66:148:66 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:150:65:150:74 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:150:66:150:66 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:153:65:153:74 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:153:66:153:66 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:155:65:155:74 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:155:66:155:66 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:160:59:160:68 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:160:60:160:60 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:161:50:161:59 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:161:51:161:51 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:164:59:164:68 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:164:60:164:60 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:165:50:165:59 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:165:51:165:51 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:169:56:169:65 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:169:57:169:57 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:170:47:170:56 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:170:48:170:48 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:173:56:173:65 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:173:57:173:57 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:174:47:174:56 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:174:48:174:48 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:178:56:178:65 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:178:57:178:57 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:179:47:179:56 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:179:48:179:48 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:182:56:182:65 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:182:57:182:57 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:183:47:183:56 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:183:48:183:48 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:187:56:187:65 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:187:57:187:57 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:188:47:188:56 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:188:48:188:48 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:191:56:191:65 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:191:57:191:57 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:192:47:192:56 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:192:48:192:48 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:198:29:198:38 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:198:30:198:30 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:201:23:201:32 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:201:24:201:24 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:206:66:206:75 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:206:67:206:67 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:208:80:208:89 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:208:81:208:81 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:210:84:210:93 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:210:85:210:85 | password : | semmle.label | password : |
|
||||
| testGRDB.swift:212:98:212:107 | [...] | semmle.label | [...] |
|
||||
| testGRDB.swift:212:99:212:99 | password : | semmle.label | password : |
|
||||
| testRealm.swift:16:6:16:6 | value : | semmle.label | value : |
|
||||
| testRealm.swift:34:2:34:2 | [post] a | semmle.label | [post] a |
|
||||
| testRealm.swift:34:2:34:2 | [post] a [data] : | semmle.label | [post] a [data] : |
|
||||
@@ -75,6 +228,57 @@ subpaths
|
||||
| testCoreData.swift:95:15:95:15 | x | testCoreData.swift:91:10:91:10 | passwd : | testCoreData.swift:95:15:95:15 | x | This operation stores 'x' in a database. It may contain unencrypted sensitive data from $@. | testCoreData.swift:91:10:91:10 | passwd : | passwd |
|
||||
| testCoreData.swift:96:15:96:15 | y | testCoreData.swift:92:10:92:10 | passwd : | testCoreData.swift:96:15:96:15 | y | This operation stores 'y' in a database. It may contain unencrypted sensitive data from $@. | testCoreData.swift:92:10:92:10 | passwd : | passwd |
|
||||
| testCoreData.swift:97:15:97:15 | z | testCoreData.swift:93:10:93:10 | passwd : | testCoreData.swift:97:15:97:15 | z | This operation stores 'z' in a database. It may contain unencrypted sensitive data from $@. | testCoreData.swift:93:10:93:10 | passwd : | passwd |
|
||||
| testGRDB.swift:73:56:73:65 | [...] | testGRDB.swift:73:57:73:57 | password : | testGRDB.swift:73:56:73:65 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:73:57:73:57 | password : | password |
|
||||
| testGRDB.swift:76:42:76:51 | [...] | testGRDB.swift:76:43:76:43 | password : | testGRDB.swift:76:42:76:51 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:76:43:76:43 | password : | password |
|
||||
| testGRDB.swift:81:44:81:53 | [...] | testGRDB.swift:81:45:81:45 | password : | testGRDB.swift:81:44:81:53 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:81:45:81:45 | password : | password |
|
||||
| testGRDB.swift:83:44:83:53 | [...] | testGRDB.swift:83:45:83:45 | password : | testGRDB.swift:83:44:83:53 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:83:45:83:45 | password : | password |
|
||||
| testGRDB.swift:85:44:85:53 | [...] | testGRDB.swift:85:45:85:45 | password : | testGRDB.swift:85:44:85:53 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:85:45:85:45 | password : | password |
|
||||
| testGRDB.swift:87:44:87:53 | [...] | testGRDB.swift:87:45:87:45 | password : | testGRDB.swift:87:44:87:53 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:87:45:87:45 | password : | password |
|
||||
| testGRDB.swift:92:37:92:46 | [...] | testGRDB.swift:92:38:92:38 | password : | testGRDB.swift:92:37:92:46 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:92:38:92:38 | password : | password |
|
||||
| testGRDB.swift:95:36:95:45 | [...] | testGRDB.swift:95:37:95:37 | password : | testGRDB.swift:95:36:95:45 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:95:37:95:37 | password : | password |
|
||||
| testGRDB.swift:100:72:100:81 | [...] | testGRDB.swift:100:73:100:73 | password : | testGRDB.swift:100:72:100:81 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:100:73:100:73 | password : | password |
|
||||
| testGRDB.swift:101:72:101:81 | [...] | testGRDB.swift:101:73:101:73 | password : | testGRDB.swift:101:72:101:81 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:101:73:101:73 | password : | password |
|
||||
| testGRDB.swift:107:52:107:61 | [...] | testGRDB.swift:107:53:107:53 | password : | testGRDB.swift:107:52:107:61 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:107:53:107:53 | password : | password |
|
||||
| testGRDB.swift:109:52:109:61 | [...] | testGRDB.swift:109:53:109:53 | password : | testGRDB.swift:109:52:109:61 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:109:53:109:53 | password : | password |
|
||||
| testGRDB.swift:111:51:111:60 | [...] | testGRDB.swift:111:52:111:52 | password : | testGRDB.swift:111:51:111:60 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:111:52:111:52 | password : | password |
|
||||
| testGRDB.swift:116:47:116:56 | [...] | testGRDB.swift:116:48:116:48 | password : | testGRDB.swift:116:47:116:56 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:116:48:116:48 | password : | password |
|
||||
| testGRDB.swift:118:47:118:56 | [...] | testGRDB.swift:118:48:118:48 | password : | testGRDB.swift:118:47:118:56 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:118:48:118:48 | password : | password |
|
||||
| testGRDB.swift:121:44:121:53 | [...] | testGRDB.swift:121:45:121:45 | password : | testGRDB.swift:121:44:121:53 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:121:45:121:45 | password : | password |
|
||||
| testGRDB.swift:123:44:123:53 | [...] | testGRDB.swift:123:45:123:45 | password : | testGRDB.swift:123:44:123:53 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:123:45:123:45 | password : | password |
|
||||
| testGRDB.swift:126:44:126:53 | [...] | testGRDB.swift:126:45:126:45 | password : | testGRDB.swift:126:44:126:53 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:126:45:126:45 | password : | password |
|
||||
| testGRDB.swift:128:44:128:53 | [...] | testGRDB.swift:128:45:128:45 | password : | testGRDB.swift:128:44:128:53 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:128:45:128:45 | password : | password |
|
||||
| testGRDB.swift:131:44:131:53 | [...] | testGRDB.swift:131:45:131:45 | password : | testGRDB.swift:131:44:131:53 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:131:45:131:45 | password : | password |
|
||||
| testGRDB.swift:133:44:133:53 | [...] | testGRDB.swift:133:45:133:45 | password : | testGRDB.swift:133:44:133:53 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:133:45:133:45 | password : | password |
|
||||
| testGRDB.swift:138:68:138:77 | [...] | testGRDB.swift:138:69:138:69 | password : | testGRDB.swift:138:68:138:77 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:138:69:138:69 | password : | password |
|
||||
| testGRDB.swift:140:68:140:77 | [...] | testGRDB.swift:140:69:140:69 | password : | testGRDB.swift:140:68:140:77 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:140:69:140:69 | password : | password |
|
||||
| testGRDB.swift:143:65:143:74 | [...] | testGRDB.swift:143:66:143:66 | password : | testGRDB.swift:143:65:143:74 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:143:66:143:66 | password : | password |
|
||||
| testGRDB.swift:145:65:145:74 | [...] | testGRDB.swift:145:66:145:66 | password : | testGRDB.swift:145:65:145:74 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:145:66:145:66 | password : | password |
|
||||
| testGRDB.swift:148:65:148:74 | [...] | testGRDB.swift:148:66:148:66 | password : | testGRDB.swift:148:65:148:74 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:148:66:148:66 | password : | password |
|
||||
| testGRDB.swift:150:65:150:74 | [...] | testGRDB.swift:150:66:150:66 | password : | testGRDB.swift:150:65:150:74 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:150:66:150:66 | password : | password |
|
||||
| testGRDB.swift:153:65:153:74 | [...] | testGRDB.swift:153:66:153:66 | password : | testGRDB.swift:153:65:153:74 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:153:66:153:66 | password : | password |
|
||||
| testGRDB.swift:155:65:155:74 | [...] | testGRDB.swift:155:66:155:66 | password : | testGRDB.swift:155:65:155:74 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:155:66:155:66 | password : | password |
|
||||
| testGRDB.swift:160:59:160:68 | [...] | testGRDB.swift:160:60:160:60 | password : | testGRDB.swift:160:59:160:68 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:160:60:160:60 | password : | password |
|
||||
| testGRDB.swift:161:50:161:59 | [...] | testGRDB.swift:161:51:161:51 | password : | testGRDB.swift:161:50:161:59 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:161:51:161:51 | password : | password |
|
||||
| testGRDB.swift:164:59:164:68 | [...] | testGRDB.swift:164:60:164:60 | password : | testGRDB.swift:164:59:164:68 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:164:60:164:60 | password : | password |
|
||||
| testGRDB.swift:165:50:165:59 | [...] | testGRDB.swift:165:51:165:51 | password : | testGRDB.swift:165:50:165:59 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:165:51:165:51 | password : | password |
|
||||
| testGRDB.swift:169:56:169:65 | [...] | testGRDB.swift:169:57:169:57 | password : | testGRDB.swift:169:56:169:65 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:169:57:169:57 | password : | password |
|
||||
| testGRDB.swift:170:47:170:56 | [...] | testGRDB.swift:170:48:170:48 | password : | testGRDB.swift:170:47:170:56 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:170:48:170:48 | password : | password |
|
||||
| testGRDB.swift:173:56:173:65 | [...] | testGRDB.swift:173:57:173:57 | password : | testGRDB.swift:173:56:173:65 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:173:57:173:57 | password : | password |
|
||||
| testGRDB.swift:174:47:174:56 | [...] | testGRDB.swift:174:48:174:48 | password : | testGRDB.swift:174:47:174:56 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:174:48:174:48 | password : | password |
|
||||
| testGRDB.swift:178:56:178:65 | [...] | testGRDB.swift:178:57:178:57 | password : | testGRDB.swift:178:56:178:65 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:178:57:178:57 | password : | password |
|
||||
| testGRDB.swift:179:47:179:56 | [...] | testGRDB.swift:179:48:179:48 | password : | testGRDB.swift:179:47:179:56 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:179:48:179:48 | password : | password |
|
||||
| testGRDB.swift:182:56:182:65 | [...] | testGRDB.swift:182:57:182:57 | password : | testGRDB.swift:182:56:182:65 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:182:57:182:57 | password : | password |
|
||||
| testGRDB.swift:183:47:183:56 | [...] | testGRDB.swift:183:48:183:48 | password : | testGRDB.swift:183:47:183:56 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:183:48:183:48 | password : | password |
|
||||
| testGRDB.swift:187:56:187:65 | [...] | testGRDB.swift:187:57:187:57 | password : | testGRDB.swift:187:56:187:65 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:187:57:187:57 | password : | password |
|
||||
| testGRDB.swift:188:47:188:56 | [...] | testGRDB.swift:188:48:188:48 | password : | testGRDB.swift:188:47:188:56 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:188:48:188:48 | password : | password |
|
||||
| testGRDB.swift:191:56:191:65 | [...] | testGRDB.swift:191:57:191:57 | password : | testGRDB.swift:191:56:191:65 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:191:57:191:57 | password : | password |
|
||||
| testGRDB.swift:192:47:192:56 | [...] | testGRDB.swift:192:48:192:48 | password : | testGRDB.swift:192:47:192:56 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:192:48:192:48 | password : | password |
|
||||
| testGRDB.swift:198:29:198:38 | [...] | testGRDB.swift:198:30:198:30 | password : | testGRDB.swift:198:29:198:38 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:198:30:198:30 | password : | password |
|
||||
| testGRDB.swift:201:23:201:32 | [...] | testGRDB.swift:201:24:201:24 | password : | testGRDB.swift:201:23:201:32 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:201:24:201:24 | password : | password |
|
||||
| testGRDB.swift:206:66:206:75 | [...] | testGRDB.swift:206:67:206:67 | password : | testGRDB.swift:206:66:206:75 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:206:67:206:67 | password : | password |
|
||||
| testGRDB.swift:208:80:208:89 | [...] | testGRDB.swift:208:81:208:81 | password : | testGRDB.swift:208:80:208:89 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:208:81:208:81 | password : | password |
|
||||
| testGRDB.swift:210:84:210:93 | [...] | testGRDB.swift:210:85:210:85 | password : | testGRDB.swift:210:84:210:93 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:210:85:210:85 | password : | password |
|
||||
| testGRDB.swift:212:98:212:107 | [...] | testGRDB.swift:212:99:212:99 | password : | testGRDB.swift:212:98:212:107 | [...] | This operation stores '[...]' in a database. It may contain unencrypted sensitive data from $@. | testGRDB.swift:212:99:212:99 | password : | password |
|
||||
| testRealm.swift:34:2:34:2 | a | testRealm.swift:34:11:34:11 | myPassword : | testRealm.swift:34:2:34:2 | [post] a | This operation stores '[post] a' in a database. It may contain unencrypted sensitive data from $@. | testRealm.swift:34:11:34:11 | myPassword : | myPassword |
|
||||
| testRealm.swift:42:2:42:2 | c | testRealm.swift:42:11:42:11 | myPassword : | testRealm.swift:42:2:42:2 | [post] c | This operation stores '[post] c' in a database. It may contain unencrypted sensitive data from $@. | testRealm.swift:42:11:42:11 | myPassword : | myPassword |
|
||||
| testRealm.swift:52:2:52:3 | ...! | testRealm.swift:52:12:52:12 | myPassword : | testRealm.swift:52:2:52:3 | [post] ...! | This operation stores '[post] ...!' in a database. It may contain unencrypted sensitive data from $@. | testRealm.swift:52:12:52:12 | myPassword : | myPassword |
|
||||
|
||||
@@ -20,6 +20,57 @@
|
||||
| testCoreData.swift:91:10:91:10 | passwd | label:passwd, type:credential |
|
||||
| testCoreData.swift:92:10:92:10 | passwd | label:passwd, type:credential |
|
||||
| testCoreData.swift:93:10:93:10 | passwd | label:passwd, type:credential |
|
||||
| testGRDB.swift:73:57:73:57 | password | label:password, type:credential |
|
||||
| testGRDB.swift:76:43:76:43 | password | label:password, type:credential |
|
||||
| testGRDB.swift:81:45:81:45 | password | label:password, type:credential |
|
||||
| testGRDB.swift:83:45:83:45 | password | label:password, type:credential |
|
||||
| testGRDB.swift:85:45:85:45 | password | label:password, type:credential |
|
||||
| testGRDB.swift:87:45:87:45 | password | label:password, type:credential |
|
||||
| testGRDB.swift:92:38:92:38 | password | label:password, type:credential |
|
||||
| testGRDB.swift:95:37:95:37 | password | label:password, type:credential |
|
||||
| testGRDB.swift:100:73:100:73 | password | label:password, type:credential |
|
||||
| testGRDB.swift:101:73:101:73 | password | label:password, type:credential |
|
||||
| testGRDB.swift:107:53:107:53 | password | label:password, type:credential |
|
||||
| testGRDB.swift:109:53:109:53 | password | label:password, type:credential |
|
||||
| testGRDB.swift:111:52:111:52 | password | label:password, type:credential |
|
||||
| testGRDB.swift:116:48:116:48 | password | label:password, type:credential |
|
||||
| testGRDB.swift:118:48:118:48 | password | label:password, type:credential |
|
||||
| testGRDB.swift:121:45:121:45 | password | label:password, type:credential |
|
||||
| testGRDB.swift:123:45:123:45 | password | label:password, type:credential |
|
||||
| testGRDB.swift:126:45:126:45 | password | label:password, type:credential |
|
||||
| testGRDB.swift:128:45:128:45 | password | label:password, type:credential |
|
||||
| testGRDB.swift:131:45:131:45 | password | label:password, type:credential |
|
||||
| testGRDB.swift:133:45:133:45 | password | label:password, type:credential |
|
||||
| testGRDB.swift:138:69:138:69 | password | label:password, type:credential |
|
||||
| testGRDB.swift:140:69:140:69 | password | label:password, type:credential |
|
||||
| testGRDB.swift:143:66:143:66 | password | label:password, type:credential |
|
||||
| testGRDB.swift:145:66:145:66 | password | label:password, type:credential |
|
||||
| testGRDB.swift:148:66:148:66 | password | label:password, type:credential |
|
||||
| testGRDB.swift:150:66:150:66 | password | label:password, type:credential |
|
||||
| testGRDB.swift:153:66:153:66 | password | label:password, type:credential |
|
||||
| testGRDB.swift:155:66:155:66 | password | label:password, type:credential |
|
||||
| testGRDB.swift:160:60:160:60 | password | label:password, type:credential |
|
||||
| testGRDB.swift:161:51:161:51 | password | label:password, type:credential |
|
||||
| testGRDB.swift:164:60:164:60 | password | label:password, type:credential |
|
||||
| testGRDB.swift:165:51:165:51 | password | label:password, type:credential |
|
||||
| testGRDB.swift:169:57:169:57 | password | label:password, type:credential |
|
||||
| testGRDB.swift:170:48:170:48 | password | label:password, type:credential |
|
||||
| testGRDB.swift:173:57:173:57 | password | label:password, type:credential |
|
||||
| testGRDB.swift:174:48:174:48 | password | label:password, type:credential |
|
||||
| testGRDB.swift:178:57:178:57 | password | label:password, type:credential |
|
||||
| testGRDB.swift:179:48:179:48 | password | label:password, type:credential |
|
||||
| testGRDB.swift:182:57:182:57 | password | label:password, type:credential |
|
||||
| testGRDB.swift:183:48:183:48 | password | label:password, type:credential |
|
||||
| testGRDB.swift:187:57:187:57 | password | label:password, type:credential |
|
||||
| testGRDB.swift:188:48:188:48 | password | label:password, type:credential |
|
||||
| testGRDB.swift:191:57:191:57 | password | label:password, type:credential |
|
||||
| testGRDB.swift:192:48:192:48 | password | label:password, type:credential |
|
||||
| testGRDB.swift:198:30:198:30 | password | label:password, type:credential |
|
||||
| testGRDB.swift:201:24:201:24 | password | label:password, type:credential |
|
||||
| testGRDB.swift:206:67:206:67 | password | label:password, type:credential |
|
||||
| testGRDB.swift:208:81:208:81 | password | label:password, type:credential |
|
||||
| testGRDB.swift:210:85:210:85 | password | label:password, type:credential |
|
||||
| testGRDB.swift:212:99:212:99 | password | label:password, type:credential |
|
||||
| testRealm.swift:34:11:34:11 | myPassword | label:myPassword, type:credential |
|
||||
| testRealm.swift:42:11:42:11 | myPassword | label:myPassword, type:credential |
|
||||
| testRealm.swift:52:12:52:12 | myPassword | label:myPassword, type:credential |
|
||||
|
||||
214
swift/ql/test/query-tests/Security/CWE-311/testGRDB.swift
Normal file
214
swift/ql/test/query-tests/Security/CWE-311/testGRDB.swift
Normal file
@@ -0,0 +1,214 @@
|
||||
// --- stubs ---
|
||||
|
||||
struct StatementArguments : ExpressibleByArrayLiteral {
|
||||
init(arrayLiteral: (String)?...) {}
|
||||
}
|
||||
|
||||
protocol RowAdapter {}
|
||||
|
||||
struct QueryInterfaceRequest<T> {}
|
||||
|
||||
class Database {
|
||||
func allStatements(sql: String, arguments: StatementArguments? = nil) -> SQLStatementCursor { return SQLStatementCursor(database: self, sql: "", arguments: nil) }
|
||||
func execute(sql: String, arguments: StatementArguments = StatementArguments()) {}
|
||||
}
|
||||
|
||||
class SQLRequest {
|
||||
init(sql: String, arguments: StatementArguments = StatementArguments(), adapter: (any RowAdapter)? = nil, cached: Bool = false) {}
|
||||
}
|
||||
|
||||
class SQL {
|
||||
init(sql: String, arguments: StatementArguments = StatementArguments()) {}
|
||||
func append(sql: String, arguments: StatementArguments = StatementArguments()) {}
|
||||
}
|
||||
|
||||
class SQLStatementCursor {
|
||||
init(database: Database, sql: String, arguments: StatementArguments?, prepFlags: CUnsignedInt = 0) {}
|
||||
}
|
||||
|
||||
class TableRecord {
|
||||
static func select(sql: String, arguments: StatementArguments = StatementArguments()) -> QueryInterfaceRequest<TableRecord> { QueryInterfaceRequest<TableRecord>() }
|
||||
static func select<RowDecoder>(sql: String, arguments: StatementArguments = StatementArguments(), as: RowDecoder.Type = RowDecoder.self) -> QueryInterfaceRequest<TableRecord>{ QueryInterfaceRequest<TableRecord>() }
|
||||
static func filter(sql: String, arguments: StatementArguments = StatementArguments()) -> QueryInterfaceRequest<TableRecord> { QueryInterfaceRequest<TableRecord>() }
|
||||
static func order(sql: String, arguments: StatementArguments = StatementArguments()) -> QueryInterfaceRequest<TableRecord> { QueryInterfaceRequest<TableRecord>() }
|
||||
}
|
||||
|
||||
class Row {
|
||||
func fetchCursor(_: Statement, sql: String, arguments: StatementArguments? = nil, adapter: (any RowAdapter)? = nil) {}
|
||||
func fetchAll(_: Statement, sql: String, arguments: StatementArguments? = nil, adapter: (any RowAdapter)? = nil) {}
|
||||
func fetchSet(_: Statement, sql: String, arguments: StatementArguments? = nil, adapter: (any RowAdapter)? = nil) {}
|
||||
func fetchOne(_: Statement, sql: String, arguments: StatementArguments? = nil, adapter: (any RowAdapter)? = nil) {}
|
||||
}
|
||||
|
||||
class DatabaseValueConvertible {
|
||||
func fetchCursor(_: Statement, sql: String, arguments: StatementArguments? = nil, adapter: (any RowAdapter)? = nil) {}
|
||||
func fetchAll(_: Statement, sql: String, arguments: StatementArguments? = nil, adapter: (any RowAdapter)? = nil) {}
|
||||
func fetchSet(_: Statement, sql: String, arguments: StatementArguments? = nil, adapter: (any RowAdapter)? = nil) {}
|
||||
func fetchOne(_: Statement, sql: String, arguments: StatementArguments? = nil, adapter: (any RowAdapter)? = nil) {}
|
||||
}
|
||||
|
||||
class FetchableRecord {
|
||||
func fetchCursor(_: Statement, sql: String, arguments: StatementArguments? = nil, adapter: (any RowAdapter)? = nil) {}
|
||||
func fetchCursor(_: Statement, arguments: StatementArguments? = nil, adapter: (any RowAdapter)? = nil) {}
|
||||
func fetchAll(_: Statement, sql: String, arguments: StatementArguments? = nil, adapter: (any RowAdapter)? = nil) {}
|
||||
func fetchAll(_: Statement, arguments: StatementArguments? = nil, adapter: (any RowAdapter)? = nil) {}
|
||||
func fetchSet(_: Statement, sql: String, arguments: StatementArguments? = nil, adapter: (any RowAdapter)? = nil) {}
|
||||
func fetchSet(_: Statement, arguments: StatementArguments? = nil, adapter: (any RowAdapter)? = nil) {}
|
||||
func fetchOne(_: Statement, sql: String, arguments: StatementArguments? = nil, adapter: (any RowAdapter)? = nil) {}
|
||||
func fetchOne(_: Statement, arguments: StatementArguments? = nil, adapter: (any RowAdapter)? = nil) {}
|
||||
}
|
||||
|
||||
class Statement {
|
||||
func execute(arguments: StatementArguments? = nil) {}
|
||||
func setArguments(_: StatementArguments) {}
|
||||
}
|
||||
|
||||
class CommonTableExpression {
|
||||
init(recursive: Bool = false, named: String, columns: [String]? = nil, sql: String, arguments: StatementArguments = StatementArguments()) {}
|
||||
}
|
||||
|
||||
// --- tests ---
|
||||
|
||||
func test(database: Database, password: String, harmless: String) {
|
||||
let _ = database.allStatements(sql: "", arguments: [password]) // BAD
|
||||
let _ = database.allStatements(sql: "", arguments: [harmless]) // GOOD
|
||||
|
||||
database.execute(sql: "", arguments: [password]) // BAD
|
||||
database.execute(sql: "", arguments: [harmless]) // GOOD
|
||||
}
|
||||
|
||||
func testSqlRequest(password: String, harmless: String) {
|
||||
let _ = SQLRequest(sql: "", arguments: [password]) // BAD
|
||||
let _ = SQLRequest(sql: "", arguments: [harmless]) // GOOD
|
||||
let _ = SQLRequest(sql: "", arguments: [password], adapter: nil) // BAD
|
||||
let _ = SQLRequest(sql: "", arguments: [harmless], adapter: nil) // GOOD
|
||||
let _ = SQLRequest(sql: "", arguments: [password], cached: false) // BAD
|
||||
let _ = SQLRequest(sql: "", arguments: [harmless], cached: false) // GOOD
|
||||
let _ = SQLRequest(sql: "", arguments: [password], adapter: nil, cached: false) // BAD
|
||||
let _ = SQLRequest(sql: "", arguments: [harmless], adapter: nil, cached: false) // GOOD
|
||||
}
|
||||
|
||||
func test(sql: SQL, password: String, harmless: String) {
|
||||
let _ = SQL(sql: "", arguments: [password]) // BAD
|
||||
let _ = SQL(sql: "", arguments: [harmless]) // GOOD
|
||||
|
||||
sql.append(sql: "", arguments: [password]) // BAD
|
||||
sql.append(sql: "", arguments: [harmless]) // GOOD
|
||||
}
|
||||
|
||||
func testSqlStatementCursor(database: Database, password: String, harmless: String) {
|
||||
let _ = SQLStatementCursor(database: database, sql: "", arguments: [password]) // BAD
|
||||
let _ = SQLStatementCursor(database: database, sql: "", arguments: [password], prepFlags: 0) // BAD
|
||||
let _ = SQLStatementCursor(database: database, sql: "", arguments: [harmless]) // GOOD
|
||||
let _ = SQLStatementCursor(database: database, sql: "", arguments: [harmless], prepFlags: 0) // GOOD
|
||||
}
|
||||
|
||||
func testTableRecord(password: String, harmless: String) {
|
||||
let _ = TableRecord.select(sql: "", arguments: [password]) // BAD
|
||||
let _ = TableRecord.select(sql: "", arguments: [harmless]) // GOOD
|
||||
let _ = TableRecord.filter(sql: "", arguments: [password]) // BAD
|
||||
let _ = TableRecord.filter(sql: "", arguments: [harmless]) // GOOD
|
||||
let _ = TableRecord.order(sql: "", arguments: [password]) // BAD
|
||||
let _ = TableRecord.order(sql: "", arguments: [harmless]) // GOOD
|
||||
}
|
||||
|
||||
func test(row: Row, stmt: Statement, password: String, harmless: String) {
|
||||
row.fetchCursor(stmt, sql: "", arguments: [password]) // BAD
|
||||
row.fetchCursor(stmt, sql: "", arguments: [harmless]) // GOOD
|
||||
row.fetchCursor(stmt, sql: "", arguments: [password], adapter: nil) // BAD
|
||||
row.fetchCursor(stmt, sql: "", arguments: [harmless], adapter: nil) // GOOD
|
||||
|
||||
row.fetchAll(stmt, sql: "", arguments: [password]) // BAD
|
||||
row.fetchAll(stmt, sql: "", arguments: [harmless]) // GOOD
|
||||
row.fetchAll(stmt, sql: "", arguments: [password], adapter: nil) // BAD
|
||||
row.fetchAll(stmt, sql: "", arguments: [harmless], adapter: nil) // GOOD
|
||||
|
||||
row.fetchSet(stmt, sql: "", arguments: [password]) // BAD
|
||||
row.fetchSet(stmt, sql: "", arguments: [harmless]) // GOOD
|
||||
row.fetchSet(stmt, sql: "", arguments: [password], adapter: nil) // BAD
|
||||
row.fetchSet(stmt, sql: "", arguments: [harmless], adapter: nil) // GOOD
|
||||
|
||||
row.fetchOne(stmt, sql: "", arguments: [password]) // BAD
|
||||
row.fetchOne(stmt, sql: "", arguments: [harmless]) // GOOD
|
||||
row.fetchOne(stmt, sql: "", arguments: [password], adapter: nil) // BAD
|
||||
row.fetchOne(stmt, sql: "", arguments: [harmless], adapter: nil) // GOOD
|
||||
}
|
||||
|
||||
func test(databaseValueConvertible: DatabaseValueConvertible, stmt: Statement, password: String, harmless: String) {
|
||||
databaseValueConvertible.fetchCursor(stmt, sql: "", arguments: [password]) // BAD
|
||||
databaseValueConvertible.fetchCursor(stmt, sql: "", arguments: [harmless]) // GOOD
|
||||
databaseValueConvertible.fetchCursor(stmt, sql: "", arguments: [password], adapter: nil) // BAD
|
||||
databaseValueConvertible.fetchCursor(stmt, sql: "", arguments: [harmless], adapter: nil) // GOOD
|
||||
|
||||
databaseValueConvertible.fetchAll(stmt, sql: "", arguments: [password]) // BAD
|
||||
databaseValueConvertible.fetchAll(stmt, sql: "", arguments: [harmless]) // GOOD
|
||||
databaseValueConvertible.fetchAll(stmt, sql: "", arguments: [password], adapter: nil) // BAD
|
||||
databaseValueConvertible.fetchAll(stmt, sql: "", arguments: [harmless], adapter: nil) // GOOD
|
||||
|
||||
databaseValueConvertible.fetchSet(stmt, sql: "", arguments: [password]) // BAD
|
||||
databaseValueConvertible.fetchSet(stmt, sql: "", arguments: [harmless]) // GOOD
|
||||
databaseValueConvertible.fetchSet(stmt, sql: "", arguments: [password], adapter: nil) // BAD
|
||||
databaseValueConvertible.fetchSet(stmt, sql: "", arguments: [harmless], adapter: nil) // GOOD
|
||||
|
||||
databaseValueConvertible.fetchOne(stmt, sql: "", arguments: [password]) // BAD
|
||||
databaseValueConvertible.fetchOne(stmt, sql: "", arguments: [harmless]) // GOOD
|
||||
databaseValueConvertible.fetchOne(stmt, sql: "", arguments: [password], adapter: nil) // BAD
|
||||
databaseValueConvertible.fetchOne(stmt, sql: "", arguments: [harmless], adapter: nil) // GOOD
|
||||
}
|
||||
|
||||
func test(fetchableRecord: FetchableRecord, stmt: Statement, password: String, harmless: String) {
|
||||
fetchableRecord.fetchCursor(stmt, sql: "", arguments: [password]) // BAD
|
||||
fetchableRecord.fetchCursor(stmt, arguments: [password]) // BAD
|
||||
fetchableRecord.fetchCursor(stmt, sql: "", arguments: [harmless]) // GOOD
|
||||
fetchableRecord.fetchCursor(stmt, arguments: [harmless]) // GOOD
|
||||
fetchableRecord.fetchCursor(stmt, sql: "", arguments: [password], adapter: nil) // BAD
|
||||
fetchableRecord.fetchCursor(stmt, arguments: [password], adapter: nil) // BAD
|
||||
fetchableRecord.fetchCursor(stmt, sql: "", arguments: [harmless], adapter: nil) // GOOD
|
||||
fetchableRecord.fetchCursor(stmt, arguments: [harmless], adapter: nil) // GOOD
|
||||
|
||||
fetchableRecord.fetchAll(stmt, sql: "", arguments: [password]) // BAD
|
||||
fetchableRecord.fetchAll(stmt, arguments: [password]) // BAD
|
||||
fetchableRecord.fetchAll(stmt, sql: "", arguments: [harmless]) // GOOD
|
||||
fetchableRecord.fetchAll(stmt, arguments: [harmless]) // GOOD
|
||||
fetchableRecord.fetchAll(stmt, sql: "", arguments: [password], adapter: nil) // BAD
|
||||
fetchableRecord.fetchAll(stmt, arguments: [password], adapter: nil) // BAD
|
||||
fetchableRecord.fetchAll(stmt, sql: "", arguments: [harmless], adapter: nil) // GOOD
|
||||
fetchableRecord.fetchAll(stmt, arguments: [harmless], adapter: nil) // GOOD
|
||||
|
||||
fetchableRecord.fetchSet(stmt, sql: "", arguments: [password]) // BAD
|
||||
fetchableRecord.fetchSet(stmt, arguments: [password]) // BAD
|
||||
fetchableRecord.fetchSet(stmt, sql: "", arguments: [harmless]) // GOOD
|
||||
fetchableRecord.fetchSet(stmt, arguments: [harmless]) // GOOD
|
||||
fetchableRecord.fetchSet(stmt, sql: "", arguments: [password], adapter: nil) // BAD
|
||||
fetchableRecord.fetchSet(stmt, arguments: [password], adapter: nil) // BAD
|
||||
fetchableRecord.fetchSet(stmt, sql: "", arguments: [harmless], adapter: nil) // GOOD
|
||||
fetchableRecord.fetchSet(stmt, arguments: [harmless], adapter: nil) // GOOD
|
||||
|
||||
fetchableRecord.fetchOne(stmt, sql: "", arguments: [password]) // BAD
|
||||
fetchableRecord.fetchOne(stmt, arguments: [password]) // BAD
|
||||
fetchableRecord.fetchOne(stmt, sql: "", arguments: [harmless]) // GOOD
|
||||
fetchableRecord.fetchOne(stmt, arguments: [harmless]) // GOOD
|
||||
fetchableRecord.fetchOne(stmt, sql: "", arguments: [password], adapter: nil) // BAD
|
||||
fetchableRecord.fetchOne(stmt, arguments: [password], adapter: nil) // BAD
|
||||
fetchableRecord.fetchOne(stmt, sql: "", arguments: [harmless], adapter: nil) // GOOD
|
||||
fetchableRecord.fetchOne(stmt, arguments: [harmless], adapter: nil) // GOOD
|
||||
}
|
||||
|
||||
func test(stmt: Statement, password: String, harmless: String) {
|
||||
stmt.execute(arguments: [password]) // BAD
|
||||
stmt.execute(arguments: [harmless]) // GOOD
|
||||
|
||||
stmt.setArguments([password]) // BAD
|
||||
stmt.setArguments([harmless]) // GOOD
|
||||
}
|
||||
|
||||
func testCommonTableExpression(password: String, harmless: String) {
|
||||
let _ = CommonTableExpression(named: "", sql: "", arguments: [password]) // BAD
|
||||
let _ = CommonTableExpression(named: "", sql: "", arguments: [harmless]) // GOOD
|
||||
let _ = CommonTableExpression(named: "", columns: nil, sql: "", arguments: [password]) // BAD
|
||||
let _ = CommonTableExpression(named: "", columns: nil, sql: "", arguments: [harmless]) // GOOD
|
||||
let _ = CommonTableExpression(recursive: false, named: "", sql: "", arguments: [password]) // BAD
|
||||
let _ = CommonTableExpression(recursive: false, named: "", sql: "", arguments: [harmless]) // GOOD
|
||||
let _ = CommonTableExpression(recursive: false, named: "", columns: nil, sql: "", arguments: [password]) // BAD
|
||||
let _ = CommonTableExpression(recursive: false, named: "", columns: nil, sql: "", arguments: [harmless]) // GOOD
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import swift
|
||||
import codeql.swift.dataflow.DataFlow
|
||||
import codeql.swift.security.PredicateInjectionQuery
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
|
||||
class PredicateInjectionTest extends InlineExpectationsTest {
|
||||
PredicateInjectionTest() { this = "PredicateInjectionTest" }
|
||||
|
||||
override string getARelevantTag() { result = "hasPredicateInjection" }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(
|
||||
PredicateInjectionConf config, DataFlow::Node source, DataFlow::Node sink, Expr sinkExpr
|
||||
|
|
||||
config.hasFlow(source, sink) and
|
||||
sinkExpr = sink.asExpr() and
|
||||
location = sinkExpr.getLocation() and
|
||||
element = sinkExpr.toString() and
|
||||
tag = "hasPredicateInjection" and
|
||||
value = source.asExpr().getLocation().getStartLine().toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// --- stubs ---
|
||||
struct URL {
|
||||
init?(string: String) {}
|
||||
}
|
||||
|
||||
extension String {
|
||||
init(contentsOf: URL) {
|
||||
let data = ""
|
||||
self.init(data)
|
||||
}
|
||||
}
|
||||
|
||||
class NSPredicate {
|
||||
init(format: String, argumentArray: [Any]?) {}
|
||||
init(format: String, arguments: CVaListPointer) {}
|
||||
init(format: String, _: CVarArg...) {}
|
||||
init?(fromMetadataQueryString: String) {}
|
||||
}
|
||||
|
||||
// --- tests ---
|
||||
|
||||
func test() {
|
||||
let remoteString = String(contentsOf: URL(string: "http://example.com/")!)
|
||||
let safeString = "safe"
|
||||
|
||||
NSPredicate(format: remoteString, argumentArray: []) // $ hasPredicateInjection=23
|
||||
NSPredicate(format: safeString, argumentArray: []) // Safe
|
||||
NSPredicate(format: safeString, argumentArray: [remoteString]) // Safe
|
||||
NSPredicate(format: remoteString, arguments: CVaListPointer(_fromUnsafeMutablePointer: UnsafeMutablePointer(bitPattern: 0)!)) // $ hasPredicateInjection=23
|
||||
NSPredicate(format: safeString, arguments: CVaListPointer(_fromUnsafeMutablePointer: UnsafeMutablePointer(bitPattern: 0)!)) // Safe
|
||||
NSPredicate(format: remoteString) // $ hasPredicateInjection=23
|
||||
NSPredicate(format: safeString) // Safe
|
||||
NSPredicate(format: remoteString, "" as! CVarArg) // $ hasPredicateInjection=23
|
||||
NSPredicate(format: safeString, "" as! CVarArg) // Safe
|
||||
NSPredicate(format: safeString, remoteString as! CVarArg) // Safe
|
||||
NSPredicate(fromMetadataQueryString: remoteString) // $ hasPredicateInjection=23
|
||||
NSPredicate(fromMetadataQueryString: safeString) // Safe
|
||||
}
|
||||
Reference in New Issue
Block a user