Skip to content

Commit e05dd35

Browse files
authored
Merge pull request #2768 from asger-semmle/js/protopol-packages
Approved by esbena
2 parents 9c3fed7 + 91a5385 commit e05dd35

File tree

6 files changed

+349
-1
lines changed

6 files changed

+349
-1
lines changed

change-notes/1.24/analysis-javascript.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
- [ws](https://github.com/websockets/ws)
2222
- [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API)
2323
- [Koa](https://www.npmjs.com/package/koa)
24+
- [lazy-cache](https://www.npmjs.com/package/lazy-cache)
25+
- [for-in](https://www.npmjs.com/package/for-in)
26+
- [for-own](https://www.npmjs.com/package/for-own)
2427

2528
## New queries
2629

javascript/ql/src/Security/CWE-400/PrototypePollutionUtility.ql

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ SourceNode getAnEnumeratedArrayElement(SourceNode array) {
4242
*/
4343
abstract class EnumeratedPropName extends DataFlow::Node {
4444
/**
45-
* Gets the object whose properties are being enumerated.
45+
* Gets the data flow node holding the object whose properties are being enumerated.
4646
*
4747
* For example, gets `src` in `for (var key in src)`.
4848
*/
@@ -120,6 +120,52 @@ class EntriesEnumeratedPropName extends EnumeratedPropName {
120120
}
121121
}
122122

123+
/**
124+
* Gets a function that enumerates object properties when invoked.
125+
*
126+
* Invocations takes the following form:
127+
* ```js
128+
* fn(obj, (value, key, o) => { ... })
129+
* ```
130+
*/
131+
SourceNode propertyEnumerator() {
132+
result = moduleImport("for-own") or
133+
result = moduleImport("for-in") or
134+
result = moduleMember("ramda", "forEachObjIndexed") or
135+
result = LodashUnderscore::member("forEach") or
136+
result = LodashUnderscore::member("each")
137+
}
138+
139+
/**
140+
* Property enumeration through a library function taking a callback.
141+
*/
142+
class LibraryCallbackEnumeratedPropName extends EnumeratedPropName {
143+
CallNode call;
144+
FunctionNode callback;
145+
146+
LibraryCallbackEnumeratedPropName() {
147+
call = propertyEnumerator().getACall() and
148+
callback = call.getCallback(1) and
149+
this = callback.getParameter(1)
150+
}
151+
152+
override Node getSourceObject() {
153+
result = call.getArgument(0)
154+
}
155+
156+
override SourceNode getASourceObjectRef() {
157+
result = super.getASourceObjectRef()
158+
or
159+
result = callback.getParameter(2)
160+
}
161+
162+
override SourceNode getASourceProp() {
163+
result = super.getASourceProp()
164+
or
165+
result = callback.getParameter(0)
166+
}
167+
}
168+
123169
/**
124170
* Holds if the properties of `node` are enumerated locally.
125171
*/

javascript/ql/src/javascript.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ import semmle.javascript.frameworks.Files
7878
import semmle.javascript.frameworks.Firebase
7979
import semmle.javascript.frameworks.jQuery
8080
import semmle.javascript.frameworks.Handlebars
81+
import semmle.javascript.frameworks.LazyCache
8182
import semmle.javascript.frameworks.LodashUnderscore
8283
import semmle.javascript.frameworks.Logging
8384
import semmle.javascript.frameworks.HttpFrameworks
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/**
2+
* Models imports through the NPM `lazy-cache` package.
3+
*/
4+
5+
import javascript
6+
7+
module LazyCache {
8+
/**
9+
* A lazy-cache object, usually created through an expression of form `require('lazy-cache')(require)`.
10+
*/
11+
class LazyCacheObject extends DataFlow::SourceNode {
12+
LazyCacheObject() {
13+
// Use `require` directly instead of `moduleImport` to avoid recursion.
14+
// For the same reason, avoid `Import.getImportedPath`.
15+
exists(Require req |
16+
req.getArgument(0).getStringValue() = "lazy-cache" and
17+
this = req.flow().(DataFlow::SourceNode).getAnInvocation()
18+
)
19+
}
20+
}
21+
22+
/**
23+
* An import through `lazy-cache`.
24+
*/
25+
class LazyCacheImport extends CallExpr, Import {
26+
LazyCacheObject cache;
27+
28+
LazyCacheImport() { this = cache.getACall().asExpr() }
29+
30+
/** Gets the name of the package as it's exposed on the lazy-cache object. */
31+
string getLocalAlias() {
32+
result = getArgument(1).getStringValue()
33+
or
34+
not exists(getArgument(1)) and
35+
result = getArgument(0).getStringValue()
36+
}
37+
38+
override Module getEnclosingModule() { result = getTopLevel() }
39+
40+
override PathExpr getImportedPath() { result = getArgument(0) }
41+
42+
override DataFlow::Node getImportedModuleNode() {
43+
result = this.flow()
44+
or
45+
result = cache.getAPropertyRead(getLocalAlias())
46+
}
47+
}
48+
49+
/** A constant path element appearing in a call to a lazy-cache object. */
50+
private class LazyCachePathExpr extends PathExprInModule, ConstantString {
51+
LazyCachePathExpr() { this = any(LazyCacheImport rp).getArgument(0) }
52+
53+
override string getValue() { result = getStringValue() }
54+
}
55+
}

0 commit comments

Comments
 (0)