Skip to content

Commit f7026c2

Browse files
authored
Merge pull request #19091 from Napalys/js/package_got
JS: `got` package modeling
2 parents 7208604 + cb14b43 commit f7026c2

File tree

4 files changed

+100
-3
lines changed

4 files changed

+100
-3
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* Improved support for `got` package with `Options`, `paginate()` and `extend()`

javascript/ql/lib/semmle/javascript/frameworks/ClientRequests.qll

+57-3
Original file line numberDiff line numberDiff line change
@@ -414,20 +414,74 @@ module ClientRequest {
414414
}
415415
}
416416

417+
/**
418+
* Represents an instance of the `got` HTTP client library.
419+
*/
420+
abstract private class GotInstance extends API::Node {
421+
/**
422+
* Gets the options object associated with this instance of `got`.
423+
*/
424+
API::Node getOptions() { none() }
425+
}
426+
427+
/**
428+
* Represents the root `got` module import.
429+
* For example: `const got = require('got')`.
430+
*/
431+
private class RootGotInstance extends GotInstance {
432+
RootGotInstance() { this = API::moduleImport("got") }
433+
}
434+
435+
/**
436+
* Represents an instance of `got` created by calling the `extend()` method.
437+
* It may also be chained with multiple calls to `extend()`.
438+
*
439+
* For example: `const client = got.extend({ prefixUrl: 'https://example.com' })`.
440+
*/
441+
private class ExtendGotInstance extends GotInstance {
442+
private GotInstance base;
443+
private API::CallNode extendCall;
444+
445+
ExtendGotInstance() {
446+
extendCall = base.getMember("extend").getACall() and
447+
this = extendCall.getReturn()
448+
}
449+
450+
override API::Node getOptions() {
451+
result = extendCall.getParameter(0) or result = base.getOptions()
452+
}
453+
}
454+
417455
/**
418456
* A model of a URL request made using the `got` library.
419457
*/
420458
class GotUrlRequest extends ClientRequest::Range {
459+
GotInstance got;
460+
421461
GotUrlRequest() {
422-
exists(API::Node callee, API::Node got | this = callee.getACall() |
423-
got = [API::moduleImport("got"), API::moduleImport("got").getMember("extend").getReturn()] and
424-
callee = [got, got.getMember(["stream", "get", "post", "put", "patch", "head", "delete"])]
462+
exists(API::Node callee | this = callee.getACall() |
463+
callee =
464+
[
465+
got,
466+
got.getMember(["stream", "get", "post", "put", "patch", "head", "delete", "paginate"])
467+
]
425468
)
426469
}
427470

428471
override DataFlow::Node getUrl() {
429472
result = this.getArgument(0) and
430473
not exists(this.getOptionArgument(1, "baseUrl"))
474+
or
475+
// Handle URL from options passed to extend()
476+
result = got.getOptions().getMember("url").asSink() and
477+
not exists(this.getArgument(0))
478+
or
479+
// Handle URL from options passed as third argument when first arg is undefined/missing
480+
exists(API::InvokeNode optionsCall |
481+
optionsCall = API::moduleImport("got").getMember("Options").getAnInvocation() and
482+
optionsCall.getReturn().getAValueReachableFromSource() = this.getAnArgument() and
483+
result = optionsCall.getParameter(0).getMember("url").asSink()
484+
)
431485
}
432486

433487
override DataFlow::Node getHost() {

javascript/ql/test/library-tests/frameworks/ClientRequests/ClientRequests.expected

+20
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,12 @@ test_ClientRequest
9797
| tst.js:319:5:319:26 | superag ... ', url) |
9898
| tst.js:320:5:320:23 | superagent.del(url) |
9999
| tst.js:321:5:321:32 | superag ... st(url) |
100+
| tst.js:328:5:328:38 | got(und ... ptions) |
101+
| tst.js:329:5:329:49 | got(und ... {url})) |
102+
| tst.js:332:5:332:46 | got.ext ... ).get() |
103+
| tst.js:334:5:334:25 | got.pag ... rl, {}) |
104+
| tst.js:337:5:337:20 | jsonClient.get() |
105+
| tst.js:340:5:340:21 | jsonClient2.get() |
100106
test_getADataNode
101107
| axiosTest.js:12:5:17:6 | axios({ ... \\n }) | axiosTest.js:15:18:15:55 | { 'Cont ... json' } |
102108
| axiosTest.js:12:5:17:6 | axios({ ... \\n }) | axiosTest.js:16:15:16:35 | {x: 'te ... 'test'} |
@@ -254,6 +260,14 @@ test_getUrl
254260
| tst.js:319:5:319:26 | superag ... ', url) | tst.js:319:23:319:25 | url |
255261
| tst.js:320:5:320:23 | superagent.del(url) | tst.js:320:20:320:22 | url |
256262
| tst.js:321:5:321:32 | superag ... st(url) | tst.js:321:29:321:31 | url |
263+
| tst.js:328:5:328:38 | got(und ... ptions) | tst.js:327:34:327:36 | url |
264+
| tst.js:328:5:328:38 | got(und ... ptions) | tst.js:328:9:328:17 | undefined |
265+
| tst.js:329:5:329:49 | got(und ... {url})) | tst.js:329:9:329:17 | undefined |
266+
| tst.js:329:5:329:49 | got(und ... {url})) | tst.js:329:44:329:46 | url |
267+
| tst.js:334:5:334:25 | got.pag ... rl, {}) | tst.js:334:18:334:20 | url |
268+
| tst.js:337:5:337:20 | jsonClient.get() | tst.js:336:41:336:43 | url |
269+
| tst.js:340:5:340:21 | jsonClient2.get() | tst.js:339:42:339:44 | url |
270+
| tst.js:340:5:340:21 | jsonClient2.get() | tst.js:339:61:339:63 | url |
257271
test_getAResponseDataNode
258272
| axiosTest.js:4:5:7:6 | axios({ ... \\n }) | axiosTest.js:4:5:7:6 | axios({ ... \\n }) | json | true |
259273
| axiosTest.js:12:5:17:6 | axios({ ... \\n }) | axiosTest.js:12:5:17:6 | axios({ ... \\n }) | json | true |
@@ -334,3 +348,9 @@ test_getAResponseDataNode
334348
| tst.js:319:5:319:26 | superag ... ', url) | tst.js:319:5:319:26 | superag ... ', url) | stream | true |
335349
| tst.js:320:5:320:23 | superagent.del(url) | tst.js:320:5:320:23 | superagent.del(url) | stream | true |
336350
| tst.js:321:5:321:32 | superag ... st(url) | tst.js:321:5:321:32 | superag ... st(url) | stream | true |
351+
| tst.js:328:5:328:38 | got(und ... ptions) | tst.js:328:5:328:38 | got(und ... ptions) | text | true |
352+
| tst.js:329:5:329:49 | got(und ... {url})) | tst.js:329:5:329:49 | got(und ... {url})) | text | true |
353+
| tst.js:332:5:332:46 | got.ext ... ).get() | tst.js:332:5:332:46 | got.ext ... ).get() | text | true |
354+
| tst.js:334:5:334:25 | got.pag ... rl, {}) | tst.js:334:5:334:25 | got.pag ... rl, {}) | text | true |
355+
| tst.js:337:5:337:20 | jsonClient.get() | tst.js:337:5:337:20 | jsonClient.get() | text | true |
356+
| tst.js:340:5:340:21 | jsonClient2.get() | tst.js:340:5:340:21 | jsonClient2.get() | text | true |

javascript/ql/test/library-tests/frameworks/ClientRequests/tst.js

+19
Original file line numberDiff line numberDiff line change
@@ -320,3 +320,22 @@ function useSuperagent(url){
320320
superagent.del(url);
321321
superagent.agent().post(url).send(data);
322322
}
323+
324+
import { Options } from 'got';
325+
326+
function gotTests(url){
327+
const options = new Options({url});
328+
got(undefined, undefined, options);
329+
got(undefined, undefined, new Options({url}));
330+
331+
const options2 = new Options({url});
332+
got.extend(options2).extend(options).get();
333+
334+
got.paginate(url, {});
335+
336+
const jsonClient = got.extend({url: url});
337+
jsonClient.get();
338+
339+
const jsonClient2 = got.extend({url: url}).extend({url: url});
340+
jsonClient2.get();
341+
}

0 commit comments

Comments
 (0)