Skip to content

Commit e167b87

Browse files
authored
Merge pull request github#3932 from max-schaefer/portals-additions
Approved by esbena
2 parents 777dc63 + 7a1410e commit e167b87

File tree

5 files changed

+92
-2
lines changed

5 files changed

+92
-2
lines changed

javascript/ql/src/semmle/javascript/dataflow/Portals.qll

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import javascript
1616

1717
private newtype TPortal =
18+
MkGlobalObjectPortal() or
1819
MkNpmPackagePortal(string pkgName) {
1920
NpmPackagePortal::imports(_, pkgName) or
2021
NpmPackagePortal::imports(_, pkgName, _) or
@@ -96,6 +97,20 @@ class Portal extends TPortal {
9697
cached
9798
ReturnPortal getReturn() { result.getBasePortal() = this }
9899

100+
/**
101+
* Gets the `i`th base portal of this portal.
102+
*
103+
* The `0`th base portal is the portal itself, the `n+1`st base portal is the `n`th base portal
104+
* of the portal `p` of which this is a member, instance, parameter, or return portal.
105+
*/
106+
cached
107+
Portal getBasePortal(int i) {
108+
i = 0 and
109+
result = this
110+
or
111+
result = this.(CompoundPortal).getBasePortal().getBasePortal(i - 1)
112+
}
113+
99114
/**
100115
* Gets a textual representation of this portal.
101116
*
@@ -115,6 +130,22 @@ class Portal extends TPortal {
115130
abstract int depth();
116131
}
117132

133+
/**
134+
* A portal representing the global object.
135+
*/
136+
private class GlobalObjectPortal extends Portal, MkGlobalObjectPortal {
137+
override DataFlow::SourceNode getAnExitNode(boolean isRemote) {
138+
result = DataFlow::globalObjectRef() and
139+
isRemote = true
140+
}
141+
142+
override DataFlow::Node getAnEntryNode(boolean escapes) { none() }
143+
144+
override string toString() { result = "(global)" }
145+
146+
override int depth() { result = 1 }
147+
}
148+
118149
/**
119150
* A portal representing the exports value of the main module of an npm
120151
* package (that is, a value of `module.exports` for CommonJS modules, or
@@ -167,15 +198,15 @@ private module NpmPackagePortal {
167198
predicate imports(DataFlow::SourceNode imp, string pkgName) {
168199
exists(NPMPackage pkg |
169200
imp = getAModuleImport(pkg, pkgName) and
170-
pkg.declaresDependency(pkgName, _)
201+
pkgName.regexpMatch("[^./].*")
171202
)
172203
}
173204

174205
/** Holds if `imp` imports `member` from package `pkgName`. */
175206
predicate imports(DataFlow::SourceNode imp, string pkgName, string member) {
176207
exists(NPMPackage pkg |
177208
imp = getAModuleMemberImport(pkg, pkgName, member) and
178-
pkg.declaresDependency(pkgName, _)
209+
pkgName.regexpMatch("[^./].*")
179210
)
180211
}
181212

@@ -275,6 +306,11 @@ private module MemberPortal {
275306
base = MkNpmPackagePortal(pkg) and
276307
isRemote = false
277308
)
309+
or
310+
// global variable reads are a kind of property read
311+
base instanceof GlobalObjectPortal and
312+
read = DataFlow::globalVarRef(prop) and
313+
isRemote = true
278314
}
279315

280316
/** Holds if the main module of `pkgName` exports `rhs` under the name `prop`. */
@@ -300,6 +336,14 @@ private module MemberPortal {
300336
base = MkNpmPackagePortal(pkgName) and
301337
escapes = true
302338
)
339+
or
340+
// global variable writes are a kind of property write
341+
base instanceof GlobalObjectPortal and
342+
exists(AssignExpr assgn |
343+
assgn.getLhs() = DataFlow::globalVarRef(prop).asExpr() and
344+
rhs = assgn.getRhs().flow()
345+
) and
346+
escapes = true
303347
}
304348
}
305349

javascript/ql/test/library-tests/Portals/PortalEntry.expected

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -707,16 +707,22 @@
707707
| (member x (parameter 0 (member foo (root https://www.npmjs.com/package/m2)))) | src/m3/tst2.js:5:10:5:10 | o | false |
708708
| (member y (member x (parameter 0 (member foo (root https://www.npmjs.com/package/m2))))) | src/m3/tst2.js:3:6:3:8 | "?" | false |
709709
| (member z (parameter 0 (member foo (root https://www.npmjs.com/package/m2)))) | src/m2/main.js:3:9:3:12 | "hi" | true |
710+
| (parameter 0 (member String (global))) | src/m5/index.js:5:33:5:50 | fs.readFileSync(f) | true |
710711
| (parameter 0 (member default (root https://www.npmjs.com/package/m2))) | src/m3/tst3.js:4:7:4:10 | "me" | false |
711712
| (parameter 0 (member default (root https://www.npmjs.com/package/m2))) | src/m3/tst3.js:5:7:5:10 | "me" | false |
713+
| (parameter 0 (member encode (root https://www.npmjs.com/package/base-64/base64.js))) | src/m5/index.js:5:26:5:51 | String( ... ync(f)) | false |
712714
| (parameter 0 (member foo (root https://www.npmjs.com/package/m2))) | src/m3/tst2.js:5:5:5:12 | { x: o } | false |
715+
| (parameter 0 (member log (member console (global)))) | src/m2/main.js:2:15:2:19 | p.x.y | true |
716+
| (parameter 0 (member log (member console (global)))) | src/m2/main.js:12:17:12:35 | x + " " + this.name | true |
717+
| (parameter 0 (member log (member console (global)))) | src/m3/index.js:3:43:3:61 | m1("Hello, world!") | true |
713718
| (parameter 0 (member m (instance (member default (root https://www.npmjs.com/package/m2))))) | src/m3/tst3.js:4:15:4:18 | "hi" | false |
714719
| (parameter 0 (member m (instance (member default (root https://www.npmjs.com/package/m2))))) | src/m3/tst3.js:4:15:4:18 | "hi" | true |
715720
| (parameter 0 (member m (instance (root https://www.npmjs.com/package/m2)))) | src/m3/tst3.js:4:15:4:18 | "hi" | false |
716721
| (parameter 0 (member m (member default (root https://www.npmjs.com/package/m2)))) | src/m3/tst3.js:2:5:2:8 | "hi" | false |
717722
| (parameter 0 (member m (return (member default (root https://www.npmjs.com/package/m2))))) | src/m3/tst3.js:4:15:4:18 | "hi" | false |
718723
| (parameter 0 (member m (return (root https://www.npmjs.com/package/m2)))) | src/m3/tst3.js:4:15:4:18 | "hi" | false |
719724
| (parameter 0 (member m (root https://www.npmjs.com/package/m2))) | src/m3/tst3.js:2:5:2:8 | "hi" | false |
725+
| (parameter 0 (member readFileSync (root https://www.npmjs.com/package/fs))) | src/m5/index.js:5:49:5:49 | f | false |
720726
| (parameter 0 (member s (instance (member default (root https://www.npmjs.com/package/m2))))) | src/m3/tst3.js:5:15:5:21 | "there" | false |
721727
| (parameter 0 (member s (instance (member default (root https://www.npmjs.com/package/m2))))) | src/m3/tst3.js:5:15:5:21 | "there" | true |
722728
| (parameter 0 (member s (instance (root https://www.npmjs.com/package/m2)))) | src/m3/tst3.js:5:15:5:21 | "there" | false |

javascript/ql/test/library-tests/Portals/PortalExit.expected

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
| (global) | src/bluebird/index.js:1:1:1:0 | this | true |
2+
| (global) | src/bluebird/tst.js:1:1:1:0 | this | true |
3+
| (global) | src/cyclic/index.js:1:1:1:0 | this | true |
4+
| (global) | src/m1/index.js:1:1:1:0 | this | true |
5+
| (global) | src/m2/main.js:1:1:1:0 | this | true |
6+
| (global) | src/m3/index.js:1:1:1:0 | this | true |
7+
| (global) | src/m3/tst2.js:1:1:1:0 | this | true |
8+
| (global) | src/m3/tst3.js:1:1:1:0 | this | true |
9+
| (global) | src/m3/tst.js:1:1:1:0 | this | true |
10+
| (global) | src/m4/index.js:1:1:1:0 | this | true |
11+
| (global) | src/m5/index.js:1:1:1:0 | this | true |
112
| (instance (member Promise (root https://www.npmjs.com/package/bluebird))) | src/bluebird/index.js:1:1:1:0 | this | true |
213
| (instance (member Promise (root https://www.npmjs.com/package/bluebird))) | src/bluebird/index.js:5:1:5:17 | Promise.prototype | true |
314
| (instance (member Promise (root https://www.npmjs.com/package/bluebird))) | src/bluebird/index.js:5:26:5:25 | this | true |
@@ -11,8 +22,16 @@
1122
| (instance (member default (root https://www.npmjs.com/package/m2))) | src/m3/tst3.js:5:1:5:11 | new A("me") | true |
1223
| (instance (root https://www.npmjs.com/package/m2)) | src/m3/tst3.js:4:1:4:11 | new A("me") | false |
1324
| (instance (root https://www.npmjs.com/package/m2)) | src/m3/tst3.js:5:1:5:11 | new A("me") | false |
25+
| (member String (global)) | src/m5/index.js:5:26:5:31 | String | true |
26+
| (member console (global)) | src/m2/main.js:2:3:2:9 | console | true |
27+
| (member console (global)) | src/m2/main.js:12:5:12:11 | console | true |
28+
| (member console (global)) | src/m3/index.js:3:31:3:37 | console | true |
1429
| (member default (root https://www.npmjs.com/package/m2)) | src/m3/tst3.js:1:8:1:8 | A | false |
30+
| (member encode (root https://www.npmjs.com/package/base-64/base64.js)) | src/m5/index.js:5:12:5:24 | base64.encode | false |
1531
| (member foo (root https://www.npmjs.com/package/m2)) | src/m3/tst2.js:1:10:1:12 | foo | false |
32+
| (member log (member console (global))) | src/m2/main.js:2:3:2:13 | console.log | true |
33+
| (member log (member console (global))) | src/m2/main.js:12:5:12:15 | console.log | true |
34+
| (member log (member console (global))) | src/m3/index.js:3:31:3:41 | console.log | true |
1635
| (member m (instance (member default (root https://www.npmjs.com/package/m2)))) | src/m3/tst3.js:4:1:4:13 | new A("me").m | false |
1736
| (member m (instance (member default (root https://www.npmjs.com/package/m2)))) | src/m3/tst3.js:4:1:4:13 | new A("me").m | true |
1837
| (member m (instance (root https://www.npmjs.com/package/m2))) | src/m3/tst3.js:4:1:4:13 | new A("me").m | false |
@@ -21,6 +40,7 @@
2140
| (member m (return (root https://www.npmjs.com/package/m2))) | src/m3/tst3.js:4:1:4:13 | new A("me").m | false |
2241
| (member m (root https://www.npmjs.com/package/m2)) | src/m3/tst3.js:2:1:2:3 | A.m | false |
2342
| (member name (instance (member default (root https://www.npmjs.com/package/m2)))) | src/m2/main.js:12:27:12:35 | this.name | true |
43+
| (member readFileSync (root https://www.npmjs.com/package/fs)) | src/m5/index.js:5:33:5:47 | fs.readFileSync | false |
2444
| (member s (instance (member default (root https://www.npmjs.com/package/m2)))) | src/m3/tst3.js:5:1:5:13 | new A("me").s | false |
2545
| (member s (instance (member default (root https://www.npmjs.com/package/m2)))) | src/m3/tst3.js:5:1:5:13 | new A("me").s | true |
2646
| (member s (instance (root https://www.npmjs.com/package/m2))) | src/m3/tst3.js:5:1:5:13 | new A("me").s | false |
@@ -734,16 +754,22 @@
734754
| (parameter 0 (return (return (return (return (return (return (return (member foo (root https://www.npmjs.com/package/cyclic)))))))))) | src/cyclic/index.js:1:14:1:15 | cb | true |
735755
| (parameter 0 (root https://www.npmjs.com/package/m1)) | src/m1/index.js:1:19:1:19 | x | true |
736756
| (parameter 1 (member then (instance (member Promise (root https://www.npmjs.com/package/bluebird))))) | src/bluebird/index.js:5:46:5:53 | rejected | true |
757+
| (return (member String (global))) | src/m5/index.js:5:26:5:51 | String( ... ync(f)) | true |
737758
| (return (member default (root https://www.npmjs.com/package/m2))) | src/m3/tst3.js:4:1:4:11 | new A("me") | false |
738759
| (return (member default (root https://www.npmjs.com/package/m2))) | src/m3/tst3.js:5:1:5:11 | new A("me") | false |
760+
| (return (member encode (root https://www.npmjs.com/package/base-64/base64.js))) | src/m5/index.js:5:12:5:52 | base64. ... nc(f))) | false |
739761
| (return (member foo (root https://www.npmjs.com/package/m2))) | src/m3/tst2.js:5:1:5:13 | foo({ x: o }) | false |
762+
| (return (member log (member console (global)))) | src/m2/main.js:2:3:2:20 | console.log(p.x.y) | true |
763+
| (return (member log (member console (global)))) | src/m2/main.js:12:5:12:36 | console ... s.name) | true |
764+
| (return (member log (member console (global)))) | src/m3/index.js:3:31:3:62 | console ... rld!")) | true |
740765
| (return (member m (instance (member default (root https://www.npmjs.com/package/m2))))) | src/m3/tst3.js:4:1:4:19 | new A("me").m("hi") | false |
741766
| (return (member m (instance (member default (root https://www.npmjs.com/package/m2))))) | src/m3/tst3.js:4:1:4:19 | new A("me").m("hi") | true |
742767
| (return (member m (instance (root https://www.npmjs.com/package/m2)))) | src/m3/tst3.js:4:1:4:19 | new A("me").m("hi") | false |
743768
| (return (member m (member default (root https://www.npmjs.com/package/m2)))) | src/m3/tst3.js:2:1:2:9 | A.m("hi") | false |
744769
| (return (member m (return (member default (root https://www.npmjs.com/package/m2))))) | src/m3/tst3.js:4:1:4:19 | new A("me").m("hi") | false |
745770
| (return (member m (return (root https://www.npmjs.com/package/m2)))) | src/m3/tst3.js:4:1:4:19 | new A("me").m("hi") | false |
746771
| (return (member m (root https://www.npmjs.com/package/m2))) | src/m3/tst3.js:2:1:2:9 | A.m("hi") | false |
772+
| (return (member readFileSync (root https://www.npmjs.com/package/fs))) | src/m5/index.js:5:33:5:50 | fs.readFileSync(f) | false |
747773
| (return (member s (instance (member default (root https://www.npmjs.com/package/m2))))) | src/m3/tst3.js:5:1:5:22 | new A(" ... there") | false |
748774
| (return (member s (instance (member default (root https://www.npmjs.com/package/m2))))) | src/m3/tst3.js:5:1:5:22 | new A(" ... there") | true |
749775
| (return (member s (instance (root https://www.npmjs.com/package/m2)))) | src/m3/tst3.js:5:1:5:22 | new A(" ... there") | false |
@@ -1043,6 +1069,8 @@
10431069
| (return (root https://www.npmjs.com/package/m1)) | src/m3/index.js:3:43:3:61 | m1("Hello, world!") | false |
10441070
| (return (root https://www.npmjs.com/package/m2)) | src/m3/tst3.js:4:1:4:11 | new A("me") | false |
10451071
| (return (root https://www.npmjs.com/package/m2)) | src/m3/tst3.js:5:1:5:11 | new A("me") | false |
1072+
| (root https://www.npmjs.com/package/base-64/base64.js) | src/m5/index.js:2:14:2:41 | require ... 64.js") | false |
1073+
| (root https://www.npmjs.com/package/fs) | src/m5/index.js:1:12:1:24 | require("fs") | false |
10461074
| (root https://www.npmjs.com/package/m1) | src/m3/index.js:1:10:1:22 | require("m1") | false |
10471075
| (root https://www.npmjs.com/package/m2) | src/m3/tst2.js:1:1:1:25 | import ... m "m2"; | false |
10481076
| (root https://www.npmjs.com/package/m2) | src/m3/tst3.js:1:1:1:19 | import A from "m2"; | false |
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
const fs = require("fs"),
2+
base64 = require("base-64/base64.js");
3+
4+
module.exports.readBase64 = function (f) {
5+
return base64.encode(String(fs.readFileSync(f)));
6+
};
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"name": "m5",
3+
"dependencies": {
4+
"base-64": "*"
5+
}
6+
}

0 commit comments

Comments
 (0)