Skip to content

Commit a452ead

Browse files
authored
Merge pull request github#16946 from hvitved/csharp/fewer-version-regexps
C#: Perform fewer `regexpCapture`s when matching version numbers
2 parents fd8cda3 + 39b5dbf commit a452ead

File tree

2 files changed

+82
-22
lines changed

2 files changed

+82
-22
lines changed

csharp/ql/lib/semmle/code/csharp/Location.qll

Lines changed: 64 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -110,35 +110,89 @@ class SourceLocation extends Location, @location_default {
110110

111111
bindingset[version]
112112
private int versionField(string version, int field) {
113-
exists(string format |
114-
format = "(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)" or
115-
format = "(\\d+)\\.(\\d+)\\.(\\d+)" or
116-
format = "(\\d+)\\.(\\d+)"
113+
exists(string format, int i |
114+
format = "(\\d+)\\.(\\d+)\\.(\\d+)\\.(\\d+)|" + "(\\d+)\\.(\\d+)\\.(\\d+)|" + "(\\d+)\\.(\\d+)" and
115+
result = version.regexpCapture(format, i).toInt()
117116
|
118-
result = version.regexpCapture(format, field).toInt()
117+
i = [1, 5, 8] and
118+
field = 1
119+
or
120+
i = [2, 6, 9] and
121+
field = 2
122+
or
123+
i = [3, 7] and
124+
field = 3
125+
or
126+
i = 4 and
127+
field = 4
119128
) and
120129
result >= 0 and
121130
result <= 255
122131
}
123132

124133
/** An assembly version, for example `4.0.0.0` or `4.5`. */
125134
class Version extends string {
135+
private int major;
136+
126137
bindingset[this]
127-
Version() { exists(versionField(this, 1)) }
138+
Version() { major = versionField(this, 1) }
139+
140+
bindingset[this]
141+
private int getVersionField(int field) {
142+
field = 1 and
143+
result = major
144+
or
145+
field in [2 .. 4] and
146+
result = versionField(this, field)
147+
}
128148

129149
/**
130150
* Gets field `field` of this version.
131151
* If the field is unspecified in the version string, then the result is `0`.
132152
*/
133153
bindingset[this]
134154
int getField(int field) {
135-
field in [1 .. 4] and
136-
if exists(versionField(this, field)) then result = versionField(this, field) else result = 0
155+
result = this.getVersionField(field)
156+
or
157+
field in [2 .. 4] and
158+
not exists(this.getVersionField(field)) and
159+
result = 0
160+
}
161+
162+
bindingset[this]
163+
private string getCanonicalizedField(int field) {
164+
exists(string s, int length |
165+
s = this.getVersionField(field).toString() and
166+
length = s.length()
167+
|
168+
// make each field consist of 3 digits
169+
result = concat(int i | i in [1 .. 3 - length] | "0") + s
170+
)
171+
}
172+
173+
/**
174+
* Gets a canonicalized version of this string, where lexicographical ordering
175+
* corresponds to version ordering.
176+
*/
177+
bindingset[this]
178+
string getCanonicalizedVersion() {
179+
exists(string res, int length |
180+
res =
181+
strictconcat(int field, string s |
182+
s = this.getCanonicalizedField(field)
183+
|
184+
s, "." order by field
185+
) and
186+
length = res.length()
187+
|
188+
// make each canonicalized version consist of 4 chunks of 3 digits separated by a dot
189+
result = res + concat(int i | i = [1 .. 15 - length] / 4 and i > 0 | ".000")
190+
)
137191
}
138192

139193
/** Gets the major version, for example `1` in `1.2.3.4`. */
140194
bindingset[this]
141-
int getMajor() { result = this.getField(1) }
195+
int getMajor() { result = major }
142196

143197
/** Gets the major revision, for example `2` in `1.2.3.4`. */
144198
bindingset[this]
@@ -164,9 +218,7 @@ class Version extends string {
164218
*/
165219
bindingset[this, other]
166220
predicate isEarlierThan(Version other) {
167-
exists(int i | this.getField(i) < other.getField(i) |
168-
forall(int j | j in [1 .. i - 1] | this.getField(j) = other.getField(j))
169-
)
221+
this.getCanonicalizedVersion() < other.getCanonicalizedVersion()
170222
}
171223

172224
/**

csharp/ql/lib/semmle/code/csharp/security/xml/InsecureXMLQuery.qll

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,19 @@
55
import csharp
66
private import semmle.code.csharp.commons.TargetFramework
77

8+
pragma[nomagic]
9+
private float getAssemblyVersion(Assembly a) {
10+
result = a.getVersion().regexpCapture("([0-9]+\\.[0-9]+).*", 1).toFloat() and
11+
// This method is only accurate when we're looking at versions before 4.0.
12+
result < 4.0
13+
}
14+
15+
pragma[nomagic]
16+
private Version getTargetFrameworkVersion(TargetFrameworkAttribute tfa) {
17+
tfa.isNetFramework() and
18+
result = tfa.getFrameworkVersion()
19+
}
20+
821
/**
922
* Holds if the type `t` is in an assembly that has been compiled against a .NET framework version
1023
* before the given version.
@@ -14,21 +27,16 @@ private predicate isNetFrameworkBefore(Type t, string version) {
1427
// For assemblies compiled against framework versions before 4 the TargetFrameworkAttribute
1528
// will not be present. In this case, we can revert back to the assembly version, which may not
1629
// contain full minor version information.
17-
exists(string assemblyVersion |
18-
assemblyVersion =
19-
t.getALocation().(Assembly).getVersion().regexpCapture("([0-9]+\\.[0-9]+).*", 1)
20-
|
21-
assemblyVersion.toFloat() < version.toFloat() and
22-
// This method is only accurate when we're looking at versions before 4.0.
23-
assemblyVersion.toFloat() < 4.0
30+
exists(float assemblyVersion |
31+
assemblyVersion = getAssemblyVersion(t.getALocation()) and
32+
assemblyVersion < version.toFloat()
2433
)
2534
or
2635
// For 4.0 and above the TargetFrameworkAttribute should be present to provide detailed version
2736
// information.
2837
exists(TargetFrameworkAttribute tfa |
2938
tfa.hasElement(t) and
30-
tfa.isNetFramework() and
31-
tfa.getFrameworkVersion().isEarlierThan(version)
39+
getTargetFrameworkVersion(tfa).isEarlierThan(version)
3240
)
3341
}
3442

@@ -173,7 +181,7 @@ module XmlReader {
173181
reason = "DTD processing is enabled by default in versions < 4.0" and
174182
evidence = this and
175183
not exists(this.getSettings()) and
176-
isNetFrameworkBefore(this.(MethodCall).getTarget().getDeclaringType(), "4.0")
184+
isNetFrameworkBefore(this.getTarget().getDeclaringType(), "4.0")
177185
or
178186
// bad settings flow here
179187
exists(ObjectCreation settings |

0 commit comments

Comments
 (0)