Skip to content

Commit c058b91

Browse files
author
Esben Sparre Andreasen
committed
JS: extract PrintfStyleCall out of TaintedFormatString
1 parent e1f3637 commit c058b91

File tree

3 files changed

+98
-50
lines changed

3 files changed

+98
-50
lines changed

javascript/ql/src/javascript.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ import semmle.javascript.frameworks.React
6666
import semmle.javascript.frameworks.ReactNative
6767
import semmle.javascript.frameworks.Request
6868
import semmle.javascript.frameworks.SQL
69+
import semmle.javascript.frameworks.StringFormatters
6970
import semmle.javascript.frameworks.UriLibraries
7071
import semmle.javascript.frameworks.XmlParsers
7172
import semmle.javascript.frameworks.xUnit
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/**
2+
* Provides classes for modeling string formatting libraries.
3+
*/
4+
5+
import javascript
6+
7+
/**
8+
* A printf-style call that substitutes the embedded format specifiers of a format string for the format arguments.
9+
*/
10+
abstract class PrintfStyleCall extends DataFlow::CallNode {
11+
/**
12+
* Gets the format string.
13+
*/
14+
abstract DataFlow::Node getFormatString();
15+
16+
/**
17+
* Gets the ith argument to the format string.
18+
*/
19+
abstract DataFlow::Node getFormatArgument(int i);
20+
}
21+
22+
private class LibraryFormatter extends PrintfStyleCall {
23+
24+
int formatIndex;
25+
26+
LibraryFormatter() {
27+
// built-in Node.js functions
28+
exists (string mod, string meth |
29+
mod = "console" and (
30+
(
31+
meth = "debug" or
32+
meth = "error" or
33+
meth = "info" or
34+
meth = "log" or
35+
meth = "trace" or
36+
meth = "warn"
37+
) and
38+
formatIndex = 0
39+
or
40+
meth = "assert" and formatIndex = 1
41+
)
42+
or
43+
mod = "util" and (
44+
(meth = "format" or meth = "log") and formatIndex = 0
45+
or
46+
meth = "formatWithOptions" and formatIndex = 1
47+
)
48+
|
49+
// `console` and `util` are available both as modules...
50+
this = DataFlow::moduleMember(mod, meth).getACall()
51+
or
52+
// ...and as globals
53+
this = DataFlow::globalVarRef(mod).getAMemberCall(meth)
54+
)
55+
or
56+
(
57+
// https://www.npmjs.com/package/printf
58+
this = DataFlow::moduleImport("printf").getACall() and
59+
formatIndex in [0..1]
60+
or
61+
// https://www.npmjs.com/package/printj
62+
exists (string fn | fn = "sprintf" or fn = "vsprintf" |
63+
this = DataFlow::moduleMember("printj", fn).getACall() and
64+
formatIndex = 0
65+
)
66+
or
67+
// https://www.npmjs.com/package/format-util
68+
this = DataFlow::moduleImport("format-util").getACall() and
69+
formatIndex = 0
70+
or
71+
// https://www.npmjs.com/package/string-template
72+
this = DataFlow::moduleImport("string-template").getACall() and
73+
formatIndex = 0
74+
or
75+
this = DataFlow::moduleImport("string-template/compile").getACall() and
76+
formatIndex = 0
77+
or
78+
// https://www.npmjs.com/package/sprintf-js
79+
exists (string meth | meth = "sprintf" or meth = "vsprintf" |
80+
this = DataFlow::moduleMember("sprintf-js", meth).getACall() and
81+
formatIndex = 0
82+
)
83+
)
84+
}
85+
86+
override DataFlow::Node getFormatString() {
87+
result = getArgument(formatIndex)
88+
}
89+
90+
override DataFlow::Node getFormatArgument(int i) {
91+
i >= 0 and
92+
result = getArgument(formatIndex + 1 + i)
93+
}
94+
}

javascript/ql/src/semmle/javascript/security/dataflow/TaintedFormatString.qll

Lines changed: 3 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -58,57 +58,10 @@ module TaintedFormatString {
5858
*/
5959
class FormatSink extends Sink {
6060
FormatSink() {
61-
exists (DataFlow::CallNode call, int argIdx |
62-
// built-in Node.js functions
63-
exists (string mod, string meth |
64-
mod = "console" and
65-
(meth = "debug" or meth = "error" or meth = "info" or
66-
meth = "log" or meth = "trace" or meth = "warn") and
67-
argIdx = 0
68-
or
69-
mod = "console" and meth = "assert" and argIdx = 1
70-
or
71-
mod = "util" and (meth = "format" or meth = "log") and argIdx = 0
72-
or
73-
mod = "util" and meth = "formatWithOptions" and argIdx = 1
74-
|
75-
// `console` and `util` are available both as modules...
76-
call = DataFlow::moduleMember(mod, meth).getACall()
77-
or
78-
// ...and as globals
79-
call = DataFlow::globalVarRef(mod).getAMemberCall(meth)
80-
)
81-
or
82-
// https://www.npmjs.com/package/printf
83-
call = DataFlow::moduleImport("printf").getACall() and
84-
argIdx in [0..1]
85-
or
86-
// https://www.npmjs.com/package/printj
87-
exists (string fn | fn = "sprintf" or fn = "vsprintf" |
88-
call = DataFlow::moduleMember("printj", fn).getACall() and
89-
argIdx = 0
90-
)
91-
or
92-
// https://www.npmjs.com/package/format-util
93-
call = DataFlow::moduleImport("format-util").getACall() and
94-
argIdx = 0
95-
or
96-
// https://www.npmjs.com/package/string-template
97-
call = DataFlow::moduleImport("string-template").getACall() and
98-
argIdx = 0
99-
or
100-
call = DataFlow::moduleImport("string-template/compile").getACall() and
101-
argIdx = 0
102-
or
103-
// https://www.npmjs.com/package/sprintf-js
104-
exists (string meth | meth = "sprintf" or meth = "vsprintf" |
105-
call = DataFlow::moduleMember("sprintf-js", meth).getACall() and
106-
argIdx = 0
107-
)
108-
|
109-
this = call.getArgument(argIdx) and
61+
exists(PrintfStyleCall printf |
62+
this = printf.getFormatString() and
11063
// exclude trivial case where there are no arguments to interpolate
111-
exists(call.getArgument(argIdx+1))
64+
exists(printf.getFormatArgument(_))
11265
)
11366
}
11467
}

0 commit comments

Comments
 (0)