Skip to content

Commit 788c5ba

Browse files
committed
add support for the prettier API
1 parent 348fab8 commit 788c5ba

File tree

7 files changed

+131
-0
lines changed

7 files changed

+131
-0
lines changed

javascript/ql/src/javascript.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ import semmle.javascript.frameworks.Nest
104104
import semmle.javascript.frameworks.Next
105105
import semmle.javascript.frameworks.NoSQL
106106
import semmle.javascript.frameworks.PkgCloud
107+
import semmle.javascript.frameworks.Prettier
107108
import semmle.javascript.frameworks.PropertyProjection
108109
import semmle.javascript.frameworks.Puppeteer
109110
import semmle.javascript.frameworks.React
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/**
2+
* Provides classes and predicates for working with the [prettier](https://www.npmjs.com/package/prettier) library.
3+
*/
4+
5+
import javascript
6+
7+
/** Provides classes and predicates modelling aspects of the [prettier](https://www.npmjs.com/package/prettier) library. */
8+
private module Prettier {
9+
/**
10+
* A taint step from the [prettier API](https://prettier.io/docs/en/api.html).
11+
*/
12+
private class PrettierTaintStep extends TaintTracking::SharedTaintStep {
13+
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
14+
exists(API::CallNode call |
15+
call = API::moduleImport("prettier").getMember("format").getACall()
16+
|
17+
pred = call.getArgument(0) and
18+
succ = call
19+
)
20+
or
21+
exists(API::CallNode call |
22+
call = API::moduleImport("prettier").getMember("formatWithCursor").getACall()
23+
|
24+
pred = call.getArgument(0) and
25+
succ = call.getReturn().getMember("formatted").getAnImmediateUse()
26+
)
27+
}
28+
}
29+
30+
private import semmle.javascript.security.dataflow.TaintedPathCustomizations::TaintedPath as TaintedPath
31+
32+
/**
33+
* An argument given to the `prettier` library specificing the location of a config file.
34+
*/
35+
private class PrettierFileSink extends TaintedPath::Sink {
36+
PrettierFileSink() {
37+
this =
38+
API::moduleImport("prettier")
39+
.getMember(["resolveConfig", "resolveConfigFile", "getFileInfo"])
40+
.getACall()
41+
.getArgument(0)
42+
or
43+
this =
44+
API::moduleImport("prettier")
45+
.getMember("resolveConfig")
46+
.getACall()
47+
.getParameter(1)
48+
.getMember("config")
49+
.getARhs()
50+
}
51+
}
52+
}

javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2271,6 +2271,25 @@ nodes
22712271
| other-fs-libraries.js:52:24:52:27 | path |
22722272
| other-fs-libraries.js:52:24:52:27 | path |
22732273
| other-fs-libraries.js:52:24:52:27 | path |
2274+
| prettier.js:6:11:6:28 | p |
2275+
| prettier.js:6:11:6:28 | p |
2276+
| prettier.js:6:11:6:28 | p |
2277+
| prettier.js:6:11:6:28 | p |
2278+
| prettier.js:6:13:6:13 | p |
2279+
| prettier.js:6:13:6:13 | p |
2280+
| prettier.js:6:13:6:13 | p |
2281+
| prettier.js:6:13:6:13 | p |
2282+
| prettier.js:6:13:6:13 | p |
2283+
| prettier.js:7:28:7:28 | p |
2284+
| prettier.js:7:28:7:28 | p |
2285+
| prettier.js:7:28:7:28 | p |
2286+
| prettier.js:7:28:7:28 | p |
2287+
| prettier.js:7:28:7:28 | p |
2288+
| prettier.js:11:44:11:44 | p |
2289+
| prettier.js:11:44:11:44 | p |
2290+
| prettier.js:11:44:11:44 | p |
2291+
| prettier.js:11:44:11:44 | p |
2292+
| prettier.js:11:44:11:44 | p |
22742293
| pupeteer.js:5:9:5:71 | tainted |
22752294
| pupeteer.js:5:9:5:71 | tainted |
22762295
| pupeteer.js:5:9:5:71 | tainted |
@@ -6668,6 +6687,30 @@ edges
66686687
| other-fs-libraries.js:49:24:49:30 | req.url | other-fs-libraries.js:49:14:49:37 | url.par ... , true) |
66696688
| other-fs-libraries.js:49:24:49:30 | req.url | other-fs-libraries.js:49:14:49:37 | url.par ... , true) |
66706689
| other-fs-libraries.js:49:24:49:30 | req.url | other-fs-libraries.js:49:14:49:37 | url.par ... , true) |
6690+
| prettier.js:6:11:6:28 | p | prettier.js:7:28:7:28 | p |
6691+
| prettier.js:6:11:6:28 | p | prettier.js:7:28:7:28 | p |
6692+
| prettier.js:6:11:6:28 | p | prettier.js:7:28:7:28 | p |
6693+
| prettier.js:6:11:6:28 | p | prettier.js:7:28:7:28 | p |
6694+
| prettier.js:6:11:6:28 | p | prettier.js:7:28:7:28 | p |
6695+
| prettier.js:6:11:6:28 | p | prettier.js:7:28:7:28 | p |
6696+
| prettier.js:6:11:6:28 | p | prettier.js:7:28:7:28 | p |
6697+
| prettier.js:6:11:6:28 | p | prettier.js:7:28:7:28 | p |
6698+
| prettier.js:6:11:6:28 | p | prettier.js:11:44:11:44 | p |
6699+
| prettier.js:6:11:6:28 | p | prettier.js:11:44:11:44 | p |
6700+
| prettier.js:6:11:6:28 | p | prettier.js:11:44:11:44 | p |
6701+
| prettier.js:6:11:6:28 | p | prettier.js:11:44:11:44 | p |
6702+
| prettier.js:6:11:6:28 | p | prettier.js:11:44:11:44 | p |
6703+
| prettier.js:6:11:6:28 | p | prettier.js:11:44:11:44 | p |
6704+
| prettier.js:6:11:6:28 | p | prettier.js:11:44:11:44 | p |
6705+
| prettier.js:6:11:6:28 | p | prettier.js:11:44:11:44 | p |
6706+
| prettier.js:6:13:6:13 | p | prettier.js:6:11:6:28 | p |
6707+
| prettier.js:6:13:6:13 | p | prettier.js:6:11:6:28 | p |
6708+
| prettier.js:6:13:6:13 | p | prettier.js:6:11:6:28 | p |
6709+
| prettier.js:6:13:6:13 | p | prettier.js:6:11:6:28 | p |
6710+
| prettier.js:6:13:6:13 | p | prettier.js:6:11:6:28 | p |
6711+
| prettier.js:6:13:6:13 | p | prettier.js:6:11:6:28 | p |
6712+
| prettier.js:6:13:6:13 | p | prettier.js:6:11:6:28 | p |
6713+
| prettier.js:6:13:6:13 | p | prettier.js:6:11:6:28 | p |
66716714
| pupeteer.js:5:9:5:71 | tainted | pupeteer.js:9:28:9:34 | tainted |
66726715
| pupeteer.js:5:9:5:71 | tainted | pupeteer.js:9:28:9:34 | tainted |
66736716
| pupeteer.js:5:9:5:71 | tainted | pupeteer.js:9:28:9:34 | tainted |
@@ -8295,6 +8338,8 @@ edges
82958338
| other-fs-libraries.js:42:53:42:56 | path | other-fs-libraries.js:38:24:38:30 | req.url | other-fs-libraries.js:42:53:42:56 | path | This path depends on $@. | other-fs-libraries.js:38:24:38:30 | req.url | a user-provided value |
82968339
| other-fs-libraries.js:51:19:51:22 | path | other-fs-libraries.js:49:24:49:30 | req.url | other-fs-libraries.js:51:19:51:22 | path | This path depends on $@. | other-fs-libraries.js:49:24:49:30 | req.url | a user-provided value |
82978340
| other-fs-libraries.js:52:24:52:27 | path | other-fs-libraries.js:49:24:49:30 | req.url | other-fs-libraries.js:52:24:52:27 | path | This path depends on $@. | other-fs-libraries.js:49:24:49:30 | req.url | a user-provided value |
8341+
| prettier.js:7:28:7:28 | p | prettier.js:6:13:6:13 | p | prettier.js:7:28:7:28 | p | This path depends on $@. | prettier.js:6:13:6:13 | p | a user-provided value |
8342+
| prettier.js:11:44:11:44 | p | prettier.js:6:13:6:13 | p | prettier.js:11:44:11:44 | p | This path depends on $@. | prettier.js:6:13:6:13 | p | a user-provided value |
82988343
| pupeteer.js:9:28:9:34 | tainted | pupeteer.js:5:28:5:53 | parseTo ... t).name | pupeteer.js:9:28:9:34 | tainted | This path depends on $@. | pupeteer.js:5:28:5:53 | parseTo ... t).name | a user-provided value |
82998344
| pupeteer.js:13:37:13:43 | tainted | pupeteer.js:5:28:5:53 | parseTo ... t).name | pupeteer.js:13:37:13:43 | tainted | This path depends on $@. | pupeteer.js:5:28:5:53 | parseTo ... t).name | a user-provided value |
83008345
| tainted-access-paths.js:8:19:8:22 | path | tainted-access-paths.js:6:24:6:30 | req.url | tainted-access-paths.js:8:19:8:22 | path | This path depends on $@. | tainted-access-paths.js:6:24:6:30 | req.url | a user-provided value |
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
const express = require('express');
2+
const prettier = require("prettier");
3+
4+
const app = express();
5+
app.get('/some/path', function (req, res) {
6+
const { p } = req.params;
7+
prettier.resolveConfig(p).then((options) => { // NOT OK
8+
const formatted = prettier.format("foo", options);
9+
});
10+
11+
prettier.resolveConfig("foo", {config: p}).then((options) => { // NOT OK
12+
const formatted = prettier.format("bar", options);
13+
});
14+
});

javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ReflectedXss.expected

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,12 @@ nodes
187187
| tst3.js:5:9:5:9 | p |
188188
| tst3.js:6:12:6:12 | p |
189189
| tst3.js:6:12:6:12 | p |
190+
| tst3.js:11:9:11:74 | code |
191+
| tst3.js:11:16:11:74 | prettie ... bel" }) |
192+
| tst3.js:11:32:11:39 | reg.body |
193+
| tst3.js:11:32:11:39 | reg.body |
194+
| tst3.js:12:12:12:15 | code |
195+
| tst3.js:12:12:12:15 | code |
190196
edges
191197
| ReflectedXss.js:8:33:8:45 | req.params.id | ReflectedXss.js:8:14:8:45 | "Unknow ... rams.id |
192198
| ReflectedXss.js:8:33:8:45 | req.params.id | ReflectedXss.js:8:14:8:45 | "Unknow ... rams.id |
@@ -342,6 +348,11 @@ edges
342348
| tst3.js:5:7:5:24 | p | tst3.js:6:12:6:12 | p |
343349
| tst3.js:5:9:5:9 | p | tst3.js:5:7:5:24 | p |
344350
| tst3.js:5:9:5:9 | p | tst3.js:5:7:5:24 | p |
351+
| tst3.js:11:9:11:74 | code | tst3.js:12:12:12:15 | code |
352+
| tst3.js:11:9:11:74 | code | tst3.js:12:12:12:15 | code |
353+
| tst3.js:11:16:11:74 | prettie ... bel" }) | tst3.js:11:9:11:74 | code |
354+
| tst3.js:11:32:11:39 | reg.body | tst3.js:11:16:11:74 | prettie ... bel" }) |
355+
| tst3.js:11:32:11:39 | reg.body | tst3.js:11:16:11:74 | prettie ... bel" }) |
345356
#select
346357
| ReflectedXss.js:8:14:8:45 | "Unknow ... rams.id | ReflectedXss.js:8:33:8:45 | req.params.id | ReflectedXss.js:8:14:8:45 | "Unknow ... rams.id | Cross-site scripting vulnerability due to $@. | ReflectedXss.js:8:33:8:45 | req.params.id | user-provided value |
347358
| ReflectedXss.js:17:12:17:39 | "Unknow ... rams.id | ReflectedXss.js:17:31:17:39 | params.id | ReflectedXss.js:17:12:17:39 | "Unknow ... rams.id | Cross-site scripting vulnerability due to $@. | ReflectedXss.js:17:31:17:39 | params.id | user-provided value |
@@ -386,3 +397,4 @@ edges
386397
| tst2.js:36:12:36:12 | p | tst2.js:30:9:30:9 | p | tst2.js:36:12:36:12 | p | Cross-site scripting vulnerability due to $@. | tst2.js:30:9:30:9 | p | user-provided value |
387398
| tst2.js:37:12:37:18 | other.p | tst2.js:30:9:30:9 | p | tst2.js:37:12:37:18 | other.p | Cross-site scripting vulnerability due to $@. | tst2.js:30:9:30:9 | p | user-provided value |
388399
| tst3.js:6:12:6:12 | p | tst3.js:5:9:5:9 | p | tst3.js:6:12:6:12 | p | Cross-site scripting vulnerability due to $@. | tst3.js:5:9:5:9 | p | user-provided value |
400+
| tst3.js:12:12:12:15 | code | tst3.js:11:32:11:39 | reg.body | tst3.js:12:12:12:15 | code | Cross-site scripting vulnerability due to $@. | tst3.js:11:32:11:39 | reg.body | user-provided value |

javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/ReflectedXssWithCustomSanitizer.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,4 @@
4040
| tst2.js:36:12:36:12 | p | Cross-site scripting vulnerability due to $@. | tst2.js:30:9:30:9 | p | user-provided value |
4141
| tst2.js:37:12:37:18 | other.p | Cross-site scripting vulnerability due to $@. | tst2.js:30:9:30:9 | p | user-provided value |
4242
| tst3.js:6:12:6:12 | p | Cross-site scripting vulnerability due to $@. | tst3.js:5:9:5:9 | p | user-provided value |
43+
| tst3.js:12:12:12:15 | code | Cross-site scripting vulnerability due to $@. | tst3.js:11:32:11:39 | reg.body | user-provided value |

javascript/ql/test/query-tests/Security/CWE-079/ReflectedXss/tst3.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,9 @@ app.enable('x-powered-by').disable('x-powered-by').get('/', function (req, res)
55
let { p } = req.params;
66
res.send(p); // NOT OK
77
});
8+
9+
const prettier = require("prettier");
10+
app.post("foobar", function (reg, res) {
11+
const code = prettier.format(reg.body, { semi: false, parser: "babel" });
12+
res.send(code); // NOT OK
13+
});

0 commit comments

Comments
 (0)