Skip to content

Commit 0a78927

Browse files
authored
Merge pull request github#8938 from hvitved/ruby/with-without-mad-tokens
Ruby: Introduce `With(out)Element` MaD input tokens
2 parents e8972b8 + 5df87d5 commit 0a78927

File tree

14 files changed

+323
-244
lines changed

14 files changed

+323
-244
lines changed

csharp/ql/lib/semmle/code/csharp/dataflow/internal/AccessPathSyntax.qll

+7
Original file line numberDiff line numberDiff line change
@@ -167,9 +167,16 @@ class AccessPathToken extends string {
167167
/** Gets the `n`th argument to this token, such as `x` or `y` from `Member[x,y]`. */
168168
string getArgument(int n) { result = this.getArgumentList().splitAt(",", n).trim() }
169169

170+
/** Gets the `n`th argument to this `name` token, such as `x` or `y` from `Member[x,y]`. */
171+
pragma[nomagic]
172+
string getArgument(string name, int n) { name = this.getName() and result = this.getArgument(n) }
173+
170174
/** Gets an argument to this token, such as `x` or `y` from `Member[x,y]`. */
171175
string getAnArgument() { result = this.getArgument(_) }
172176

177+
/** Gets an argument to this `name` token, such as `x` or `y` from `Member[x,y]`. */
178+
string getAnArgument(string name) { result = this.getArgument(name, _) }
179+
173180
/** Gets the number of arguments to this token, such as 2 for `Member[x,y]` or zero for `ReturnValue`. */
174181
int getNumArgument() { result = count(int n | exists(this.getArgument(n))) }
175182
}

java/ql/lib/semmle/code/java/dataflow/internal/AccessPathSyntax.qll

+7
Original file line numberDiff line numberDiff line change
@@ -167,9 +167,16 @@ class AccessPathToken extends string {
167167
/** Gets the `n`th argument to this token, such as `x` or `y` from `Member[x,y]`. */
168168
string getArgument(int n) { result = this.getArgumentList().splitAt(",", n).trim() }
169169

170+
/** Gets the `n`th argument to this `name` token, such as `x` or `y` from `Member[x,y]`. */
171+
pragma[nomagic]
172+
string getArgument(string name, int n) { name = this.getName() and result = this.getArgument(n) }
173+
170174
/** Gets an argument to this token, such as `x` or `y` from `Member[x,y]`. */
171175
string getAnArgument() { result = this.getArgument(_) }
172176

177+
/** Gets an argument to this `name` token, such as `x` or `y` from `Member[x,y]`. */
178+
string getAnArgument(string name) { result = this.getArgument(name, _) }
179+
173180
/** Gets the number of arguments to this token, such as 2 for `Member[x,y]` or zero for `ReturnValue`. */
174181
int getNumArgument() { result = count(int n | exists(this.getArgument(n))) }
175182
}

javascript/ql/lib/semmle/javascript/frameworks/data/internal/AccessPathSyntax.qll

+7
Original file line numberDiff line numberDiff line change
@@ -167,9 +167,16 @@ class AccessPathToken extends string {
167167
/** Gets the `n`th argument to this token, such as `x` or `y` from `Member[x,y]`. */
168168
string getArgument(int n) { result = this.getArgumentList().splitAt(",", n).trim() }
169169

170+
/** Gets the `n`th argument to this `name` token, such as `x` or `y` from `Member[x,y]`. */
171+
pragma[nomagic]
172+
string getArgument(string name, int n) { name = this.getName() and result = this.getArgument(n) }
173+
170174
/** Gets an argument to this token, such as `x` or `y` from `Member[x,y]`. */
171175
string getAnArgument() { result = this.getArgument(_) }
172176

177+
/** Gets an argument to this `name` token, such as `x` or `y` from `Member[x,y]`. */
178+
string getAnArgument(string name) { result = this.getArgument(name, _) }
179+
173180
/** Gets the number of arguments to this token, such as 2 for `Member[x,y]` or zero for `ReturnValue`. */
174181
int getNumArgument() { result = count(int n | exists(this.getArgument(n))) }
175182
}

ruby/ql/lib/codeql/ruby/dataflow/FlowSummary.qll

+4-11
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ module SummaryComponent {
2525

2626
predicate content = SC::content/1;
2727

28+
predicate withoutContent = SC::withoutContent/1;
29+
30+
predicate withContent = SC::withContent/1;
31+
2832
/** Gets a summary component that represents a receiver. */
2933
SummaryComponent receiver() { result = argument(any(ParameterPosition pos | pos.isSelf())) }
3034

@@ -124,13 +128,6 @@ abstract class SummarizedCallable extends LibraryCallable {
124128
*/
125129
pragma[nomagic]
126130
predicate propagatesFlowExt(string input, string output, boolean preservesValue) { none() }
127-
128-
/**
129-
* Holds if values stored inside `content` are cleared on objects passed as
130-
* arguments at position `pos` to this callable.
131-
*/
132-
pragma[nomagic]
133-
predicate clearsContent(ParameterPosition pos, DataFlow::ContentSet content) { none() }
134131
}
135132

136133
/**
@@ -156,10 +153,6 @@ private class SummarizedCallableAdapter extends Impl::Public::SummarizedCallable
156153
) {
157154
sc.propagatesFlow(input, output, preservesValue)
158155
}
159-
160-
final override predicate clearsContent(ParameterPosition pos, DataFlow::ContentSet content) {
161-
sc.clearsContent(pos, content)
162-
}
163156
}
164157

165158
class RequiredSummaryComponentStack = Impl::Public::RequiredSummaryComponentStack;

ruby/ql/lib/codeql/ruby/dataflow/internal/AccessPathSyntax.qll

+7
Original file line numberDiff line numberDiff line change
@@ -167,9 +167,16 @@ class AccessPathToken extends string {
167167
/** Gets the `n`th argument to this token, such as `x` or `y` from `Member[x,y]`. */
168168
string getArgument(int n) { result = this.getArgumentList().splitAt(",", n).trim() }
169169

170+
/** Gets the `n`th argument to this `name` token, such as `x` or `y` from `Member[x,y]`. */
171+
pragma[nomagic]
172+
string getArgument(string name, int n) { name = this.getName() and result = this.getArgument(n) }
173+
170174
/** Gets an argument to this token, such as `x` or `y` from `Member[x,y]`. */
171175
string getAnArgument() { result = this.getArgument(_) }
172176

177+
/** Gets an argument to this `name` token, such as `x` or `y` from `Member[x,y]`. */
178+
string getAnArgument(string name) { result = this.getArgument(name, _) }
179+
173180
/** Gets the number of arguments to this token, such as 2 for `Member[x,y]` or zero for `ReturnValue`. */
174181
int getNumArgument() { result = count(int n | exists(this.getArgument(n))) }
175182
}

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

+28-16
Original file line numberDiff line numberDiff line change
@@ -56,15 +56,30 @@ predicate summaryElement(
5656
)
5757
}
5858

59+
bindingset[arg]
60+
private SummaryComponent interpretElementArg(string arg) {
61+
arg = "?" and
62+
result = FlowSummary::SummaryComponent::elementUnknown()
63+
or
64+
arg = "any" and
65+
result = FlowSummary::SummaryComponent::elementAny()
66+
or
67+
exists(ConstantValue cv | result = FlowSummary::SummaryComponent::elementKnown(cv) |
68+
cv.isInt(AccessPath::parseInt(arg))
69+
or
70+
not exists(AccessPath::parseInt(arg)) and
71+
cv.serialize() = arg
72+
)
73+
}
74+
5975
/**
6076
* Gets the summary component for specification component `c`, if any.
6177
*
6278
* This covers all the Ruby-specific components of a flow summary.
6379
*/
6480
SummaryComponent interpretComponentSpecific(AccessPathToken c) {
65-
c.getName() = "Argument" and
6681
exists(string arg, ParameterPosition ppos |
67-
arg = c.getAnArgument() and
82+
arg = c.getAnArgument("Argument") and
6883
result = FlowSummary::SummaryComponent::argument(ppos)
6984
|
7085
arg = "any" and
@@ -73,20 +88,17 @@ SummaryComponent interpretComponentSpecific(AccessPathToken c) {
7388
ppos.isPositionalLowerBound(AccessPath::parseLowerBound(arg))
7489
)
7590
or
76-
c.getName() = "Element" and
77-
exists(string arg | arg = c.getAnArgument() |
78-
arg = "?" and
79-
result = FlowSummary::SummaryComponent::elementUnknown()
80-
or
81-
arg = "any" and
82-
result = FlowSummary::SummaryComponent::elementAny()
83-
or
84-
exists(ConstantValue cv | result = FlowSummary::SummaryComponent::elementKnown(cv) |
85-
cv.isInt(AccessPath::parseInt(arg))
86-
or
87-
not exists(AccessPath::parseInt(arg)) and
88-
cv.serialize() = c.getAnArgument()
89-
)
91+
result = interpretElementArg(c.getAnArgument("Element"))
92+
or
93+
exists(ContentSet cs |
94+
FlowSummary::SummaryComponent::content(cs) = interpretElementArg(c.getAnArgument("WithElement")) and
95+
result = FlowSummary::SummaryComponent::withContent(cs)
96+
)
97+
or
98+
exists(ContentSet cs |
99+
FlowSummary::SummaryComponent::content(cs) =
100+
interpretElementArg(c.getAnArgument("WithoutElement")) and
101+
result = FlowSummary::SummaryComponent::withoutContent(cs)
90102
)
91103
}
92104

ruby/ql/lib/codeql/ruby/frameworks/Core.qll

+2-2
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,11 @@ private class SplatSummary extends SummarizedCallable {
6262
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
6363
(
6464
// *1 = [1]
65-
input = "Argument[self]" and
65+
input = "Argument[self].WithoutElement[any]" and
6666
output = "ReturnValue.Element[0]"
6767
or
6868
// *[1] = [1]
69-
input = "Argument[self]" and
69+
input = "Argument[self].WithElement[any]" and
7070
output = "ReturnValue"
7171
) and
7272
preservesValue = true

0 commit comments

Comments
 (0)