Skip to content

Commit bba70a7

Browse files
committed
Swift: Support selecting fields in Swift MaD.
1 parent 2593120 commit bba70a7

File tree

5 files changed

+101
-31
lines changed

5 files changed

+101
-31
lines changed

swift/ql/lib/codeql/swift/dataflow/ExternalFlow.qll

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -410,8 +410,10 @@ pragma[nomagic]
410410
private Element interpretElement0(
411411
string namespace, string type, boolean subtypes, string name, string signature
412412
) {
413+
elementSpec(namespace, type, subtypes, name, signature, _) and
413414
namespace = "" and // TODO: Fill out when we properly extract modules.
414415
(
416+
// Non-member functions
415417
exists(AbstractFunctionDecl func |
416418
func.getName() = name and
417419
type = "" and
@@ -421,10 +423,11 @@ private Element interpretElement0(
421423
result = func
422424
)
423425
or
426+
// Member functions
424427
exists(NominalType nomType, IterableDeclContext decl, MethodDecl method |
425428
method.getName() = name and
426429
method = decl.getAMember() and
427-
nomType.getName() = type and
430+
nomType.getFullName() = type and
428431
matchesSignature(method, signature) and
429432
result = method
430433
|
@@ -434,6 +437,20 @@ private Element interpretElement0(
434437
subtypes = false and
435438
getDeclType(decl) = nomType
436439
)
440+
or
441+
signature = "" and
442+
exists(NominalType nomType, IterableDeclContext decl, FieldDecl field |
443+
field.getName() = name and
444+
field = decl.getAMember() and
445+
nomType.getFullName() = type and
446+
result = field
447+
|
448+
subtypes = true and
449+
getDeclType(decl) = nomType.getADerivedType*()
450+
or
451+
subtypes = false and
452+
getDeclType(decl) = nomType
453+
)
437454
)
438455
}
439456

@@ -447,11 +464,18 @@ Element interpretElement(
447464
)
448465
}
449466

450-
/**
451-
* Holds if `c` has a `generated` summary.
452-
*/
453-
predicate hasSummary(SummarizedCallable c, boolean generated) {
454-
summaryElement(c, _, _, _, generated)
467+
private predicate parseField(AccessPathToken c, Content::FieldContent f) {
468+
exists(string fieldRegex, string name |
469+
c.getName() = "Field" and
470+
fieldRegex = "^([^.]+)$" and
471+
name = c.getAnArgument().regexpCapture(fieldRegex, 1) and
472+
f.getField().getName() = name
473+
)
474+
}
475+
476+
/** Holds if the specification component parses as a `Content`. */
477+
predicate parseContent(AccessPathToken component, Content content) {
478+
parseField(component, content)
455479
}
456480

457481
cached

swift/ql/lib/codeql/swift/dataflow/internal/FlowSummaryImplSpecific.qll

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -105,12 +105,19 @@ predicate sinkElement(Element e, string input, string kind, boolean generated) {
105105
/** Gets the summary component for specification component `c`, if any. */
106106
bindingset[c]
107107
SummaryComponent interpretComponentSpecific(AccessPathToken c) {
108-
none() // TODO once we have field flow
108+
exists(ContentSet cs, Content content |
109+
cs.isSingleton(content) and
110+
parseContent(c, content) and
111+
result = SummaryComponent::content(cs)
112+
)
109113
}
110114

111115
/** Gets the textual representation of the content in the format used for flow summaries. */
112-
private string getContentSpecificCsv(ContentSet c) {
113-
none() // TODO once we have field flow
116+
private string getContentSpecificCsv(ContentSet cs) {
117+
exists(Content::FieldContent c |
118+
cs.isSingleton(c) and
119+
result = "Field[" + c.getField().getName() + "]"
120+
)
114121
}
115122

116123
/** Gets the textual representation of a summary component in the format used for flow summaries. */
@@ -182,10 +189,17 @@ class InterpretNode extends TInterpretNode {
182189
}
183190
}
184191

185-
/** Provides additional sink specification logic required for attributes. */
186-
predicate interpretOutputSpecific(string c, InterpretNode mid, InterpretNode node) { none() }
192+
predicate interpretOutputSpecific(string c, InterpretNode mid, InterpretNode node) {
193+
// Allow fields to be picked as output nodes.
194+
exists(Node n, AstNode ast |
195+
n = node.asNode() and
196+
ast = mid.asElement()
197+
|
198+
c = "" and
199+
n.asExpr().(MemberRefExpr).getMember() = ast
200+
)
201+
}
187202

188-
/** Provides additional sink specification logic required for attributes. */
189203
predicate interpretInputSpecific(string c, InterpretNode mid, InterpretNode n) { none() }
190204

191205
/** Gets the argument position obtained by parsing `X` in `Parameter[X]`. */

swift/ql/lib/codeql/swift/elements/decl/TypeDecl.qll

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
private import codeql.swift.generated.decl.TypeDecl
22
private import codeql.swift.generated.type.Type
33
private import codeql.swift.elements.type.AnyGenericType
4+
private import swift
45

56
class TypeDecl extends TypeDeclBase {
67
override string toString() { result = this.getName() }
@@ -12,4 +13,23 @@ class TypeDecl extends TypeDeclBase {
1213
TypeDecl getDerivedTypeDecl(int i) { result.getBaseTypeDecl(i) = this }
1314

1415
TypeDecl getADerivedTypeDecl() { result = this.getDerivedTypeDecl(_) }
16+
17+
/**
18+
* Gets the full name of this `TypeDecl`. For example in:
19+
* ```swift
20+
* struct A {
21+
* struct B {
22+
* // ...
23+
* }
24+
* }
25+
* ```
26+
* The name and full name of `A` is `A`. The name of `B` is `B`, but the
27+
* full name of `B` is `A.B`.
28+
*/
29+
string getFullName() {
30+
not this.getEnclosingDecl() instanceof TypeDecl and
31+
result = this.getName()
32+
or
33+
result = this.getEnclosingDecl().(TypeDecl).getFullName() + "." + this.getName()
34+
}
1535
}

swift/ql/lib/codeql/swift/elements/type/NominalType.qll

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,18 @@ class NominalType extends NominalTypeBase {
55
NominalType getABaseType() { result = this.getDeclaration().(NominalTypeDecl).getABaseType() }
66

77
NominalType getADerivedType() { result.getABaseType() = this }
8+
9+
/**
10+
* Gets the full name of this `TypeDecl`. For example in:
11+
* ```swift
12+
* struct A {
13+
* struct B {
14+
* // ...
15+
* }
16+
* }
17+
* ```
18+
* The name and full name of `A` is `A`. The name of `B` is `B`, but the
19+
* full name of `B` is `A.B`.
20+
*/
21+
string getFullName() { result = this.getDeclaration().(NominalTypeDecl).getFullName() }
822
}
Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,25 @@
11
import swift
2-
private import codeql.swift.dataflow.FlowSources
2+
private import codeql.swift.dataflow.ExternalFlow
33

44
/**
55
* A model for `URL` members that are sources of remote flow.
66
*/
7-
class UrlRemoteFlowSource extends RemoteFlowSource {
8-
UrlRemoteFlowSource() {
9-
exists(StructDecl urlClass, ConcreteVarDecl memberDecl |
10-
urlClass.getName() = "URL" and
11-
(
12-
urlClass.getAMember() = memberDecl and
13-
memberDecl.getName() = ["resourceBytes", "lines"]
14-
or
15-
exists(StructDecl asyncBytesClass |
16-
urlClass.getAMember() = asyncBytesClass and
17-
asyncBytesClass.getName() = "AsyncBytes" and
18-
asyncBytesClass.getAMember() = memberDecl and
19-
memberDecl.getName() = "lines"
20-
)
21-
) and
22-
this.asExpr().(MemberRefExpr).getMember() = memberDecl
23-
)
7+
private class UrlRemoteFlowSource extends SourceModelCsv {
8+
override predicate row(string row) {
9+
row =
10+
[
11+
";URL;true;resourceBytes;;;;remote", ";URL;true;lines;;;;remote",
12+
";URL.AsyncBytes;true;lines;;;;remote"
13+
]
2414
}
15+
}
2516

26-
override string getSourceType() { result = "external" }
17+
private class UrlSummaries extends SummaryModelCsv {
18+
override predicate row(string row) {
19+
row =
20+
[
21+
";URL;true;init(string:);(String);;Argument[0];ReturnValue;taint",
22+
";URL;true;init(string:relativeTo:);(String,URL?);;Argument[0,1];ReturnValue;taint"
23+
]
24+
}
2725
}

0 commit comments

Comments
 (0)