Merge branch 'main' into redsun82/swift-open-redirection

This commit is contained in:
Paolo Tranquilli
2023-01-12 10:54:04 +01:00
790 changed files with 32474 additions and 20143 deletions

View File

@@ -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",
]),
)

View File

@@ -0,0 +1,4 @@
#pragma once
#include <filesystem>
#include <functional>

View File

@@ -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

View File

@@ -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)
)
)
}

View File

@@ -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()
)
}

View File

@@ -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()
}
/**

View File

@@ -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

View File

@@ -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)
}
}

View File

@@ -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)
)
}

View File

@@ -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"
]
}
}

View 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"
]
}
}

View File

@@ -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)
}
}

View File

@@ -0,0 +1,4 @@
- description: Extended and experimental security queries for Swift
- queries: .
- apply: security-experimental-selectors.yml
from: codeql/suite-helpers

View File

@@ -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.
*/

View File

@@ -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
)
}

View File

@@ -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`.

View File

@@ -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>

View 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"

View File

@@ -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)

View File

@@ -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)

View File

@@ -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>

View File

@@ -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() }
}

View File

@@ -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
}

View 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
}

View File

@@ -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 |

View File

@@ -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 |

View File

@@ -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 |

View 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
}

View File

@@ -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()
)
}
}

View File

@@ -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
}