Skip to content

Commit 106ee5b

Browse files
authored
Merge pull request #696 from asgerf/asgerf/dot-separated-access-paths
Go: Switch to dot-separated access paths in summary specs
2 parents 980c274 + cb38df5 commit 106ee5b

9 files changed

Lines changed: 186 additions & 118 deletions

File tree

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ build/testdb/go.dbscheme: ql/lib/upgrades/initial/go.dbscheme
136136

137137
.PHONY: sync-dataflow-libraries
138138
sync-dataflow-libraries:
139-
for f in DataFlowImpl.qll DataFlowImpl2.qll DataFlowImplCommon.qll DataFlowImplConsistency.qll tainttracking1/TaintTrackingImpl.qll tainttracking2/TaintTrackingImpl.qll FlowSummaryImpl.qll;\
139+
for f in DataFlowImpl.qll DataFlowImpl2.qll DataFlowImplCommon.qll DataFlowImplConsistency.qll tainttracking1/TaintTrackingImpl.qll tainttracking2/TaintTrackingImpl.qll FlowSummaryImpl.qll AccessPathSyntax.qll;\
140140
do\
141141
curl -s -o ./ql/lib/semmle/go/dataflow/internal/$$f https://raw.githubusercontent.com/github/codeql/$(DATAFLOW_BRANCH)/java/ql/lib/semmle/code/java/dataflow/internal/$$f;\
142142
done

ql/lib/semmle/go/dataflow/ExternalFlow.qll

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ private import go
6464
private import internal.DataFlowPrivate
6565
private import internal.FlowSummaryImpl::Private::External
6666
private import internal.FlowSummaryImplSpecific
67+
private import internal.AccessPathSyntax
6768
private import FlowSummary
6869

6970
/**
@@ -78,8 +79,8 @@ private class BuiltinModel extends SummaryModelCsv {
7879
override predicate row(string row) {
7980
row =
8081
[
81-
";;false;append;;;ArrayElement of Argument[0];ArrayElement of ReturnValue;value",
82-
";;false;append;;;Argument[1];ArrayElement of ReturnValue;value"
82+
";;false;append;;;Argument[0].ArrayElement;ReturnValue.ArrayElement;value",
83+
";;false;append;;;Argument[1];ReturnValue.ArrayElement;value"
8384
]
8485
}
8586
}
@@ -295,7 +296,7 @@ module CsvValidation {
295296
msg = "Unrecognized extra API graph element \"" + ext + "\" in " + pred + " model."
296297
)
297298
or
298-
exists(string pred, string input, string part |
299+
exists(string pred, AccessPath input, string part |
299300
sinkModel(_, _, _, _, _, _, input, _) and pred = "sink"
300301
or
301302
summaryModel(_, _, _, _, _, _, input, _, _) and pred = "summary"
@@ -305,7 +306,7 @@ module CsvValidation {
305306
not part = "" and
306307
not parseArg(part, _)
307308
or
308-
specSplit(input, part, _) and
309+
part = input.getToken(_) and
309310
parseParam(part, _)
310311
) and
311312
msg = "Unrecognized input specification \"" + part + "\" in " + pred + " model."
@@ -403,8 +404,7 @@ predicate hasExternalSpecification(Function f) {
403404
exists(SourceOrSinkElement e | f = e.asEntity() | sourceElement(e, _, _) or sinkElement(e, _, _))
404405
}
405406

406-
private predicate parseField(string c, DataFlow::FieldContent f) {
407-
specSplit(_, c, _) and
407+
private predicate parseField(AccessPathToken c, DataFlow::FieldContent f) {
408408
exists(string fieldRegex, string package, string className, string fieldName |
409409
fieldRegex = "^Field\\[(.*)\\.([^.]+)\\.([^.]+)\\]$" and
410410
package = c.regexpCapture(fieldRegex, 1) and
@@ -425,8 +425,7 @@ class SyntheticField extends string {
425425
Type getType() { result instanceof EmptyInterfaceType }
426426
}
427427

428-
private predicate parseSynthField(string c, string f) {
429-
specSplit(_, c, _) and
428+
private predicate parseSynthField(AccessPathToken c, string f) {
430429
c.regexpCapture("SyntheticField\\[([.a-zA-Z0-9]+)\\]", 1) = f
431430
}
432431

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/**
2+
* Module for parsing access paths from CSV models, both the identifying access path used
3+
* by dynamic languages, and the input/output specifications for summary steps.
4+
*
5+
* This file is used by the shared data flow library and by the JavaScript libraries
6+
* (which does not use the shared data flow libraries).
7+
*/
8+
9+
/** Companion module to the `AccessPath` class. */
10+
module AccessPath {
11+
/** A string that should be parsed as an access path. */
12+
abstract class Range extends string {
13+
bindingset[this]
14+
Range() { any() }
15+
}
16+
}
17+
18+
/** Gets the `n`th token on the access path as a string. */
19+
private string getRawToken(AccessPath path, int n) {
20+
// Avoid splitting by '.' since tokens may contain dots, e.g. `Field[foo.Bar.x]`.
21+
// Instead use regexpFind to match valid tokens, and supplement with a final length
22+
// check (in `AccessPath.hasSyntaxError`) to ensure all characters were included in a token.
23+
result = path.regexpFind("\\w+(?:\\[[^\\]]*\\])?(?=\\.|$)", n, _)
24+
}
25+
26+
/**
27+
* A string that occurs as an access path (either identifying or input/output spec)
28+
* which might be relevant for this database.
29+
*/
30+
class AccessPath extends string instanceof AccessPath::Range {
31+
/** Holds if this string is not a syntactically valid access path. */
32+
predicate hasSyntaxError() {
33+
// If the lengths match, all characters must haven been included in a token
34+
// or seen by the `.` lookahead pattern.
35+
this != "" and
36+
not this.length() = sum(int n | | getRawToken(this, n).length() + 1) - 1
37+
}
38+
39+
/** Gets the `n`th token on the access path (if there are no syntax errors). */
40+
AccessPathToken getToken(int n) {
41+
result = getRawToken(this, n) and
42+
not this.hasSyntaxError()
43+
}
44+
45+
/** Gets the number of tokens on the path (if there are no syntax errors). */
46+
int getNumToken() {
47+
result = count(int n | exists(getRawToken(this, n))) and
48+
not this.hasSyntaxError()
49+
}
50+
}
51+
52+
/**
53+
* An access part token such as `Argument[1]` or `ReturnValue`, appearing in one or more access paths.
54+
*/
55+
class AccessPathToken extends string {
56+
AccessPathToken() { this = getRawToken(any(AccessPath path), _) }
57+
58+
private string getPart(int part) {
59+
result = this.regexpCapture("([^\\[]+)(?:\\[([^\\]]*)\\])?", part)
60+
}
61+
62+
/** Gets the name of the token, such as `Member` from `Member[x]` */
63+
string getName() { result = this.getPart(1) }
64+
65+
/**
66+
* Gets the argument list, such as `1,2` from `Member[1,2]`,
67+
* or has no result if there are no arguments.
68+
*/
69+
string getArgumentList() { result = this.getPart(2) }
70+
71+
/** Gets the `n`th argument to this token, such as `x` or `y` from `Member[x,y]`. */
72+
string getArgument(int n) { result = this.getArgumentList().splitAt(",", n).trim() }
73+
74+
/** Gets an argument to this token, such as `x` or `y` from `Member[x,y]`. */
75+
string getAnArgument() { result = this.getArgument(_) }
76+
77+
/** Gets the number of arguments to this token, such as 2 for `Member[x,y]` or zero for `ReturnValue`. */
78+
int getNumArgument() { result = count(int n | exists(this.getArgument(n))) }
79+
}

0 commit comments

Comments
 (0)