Skip to content

Commit 4da9d6d

Browse files
author
Robert Marsh
committed
JavaScript: add support for Electron http client
1 parent 4d97570 commit 4da9d6d

26 files changed

+417
-37
lines changed

javascript/ql/src/semmle/javascript/frameworks/Electron.qll

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,54 @@ module Electron {
3333
this = DataFlow::moduleMember("electron", "BrowserView").getAnInstantiation()
3434
}
3535
}
36+
37+
/**
38+
* A Node.js-style HTTP or HTTPS request made using an Electron module.
39+
*/
40+
abstract class ClientRequest extends NodeJSLib::ClientRequest {}
41+
42+
/**
43+
* A Node.js-style HTTP or HTTPS request made using `electron.net`, for example `net.request(url)`.
44+
*/
45+
private class NetRequest extends ClientRequest {
46+
NetRequest() {
47+
this = DataFlow::moduleMember("electron", "net").getAMemberCall("request")
48+
}
49+
50+
override DataFlow::Node getOptions() {
51+
result = this.(DataFlow::MethodCallNode).getArgument(0)
52+
}
53+
}
54+
55+
56+
/**
57+
* A Node.js-style HTTP or HTTPS request made using `electron.client`, for example `new client(url)`.
58+
*/
59+
private class NewClientRequest extends ClientRequest {
60+
NewClientRequest() {
61+
this = DataFlow::moduleMember("electron", "ClientRequest").getAnInstantiation()
62+
}
63+
64+
override DataFlow::Node getOptions() {
65+
result = this.(DataFlow::NewNode).getArgument(0)
66+
}
67+
}
68+
69+
70+
/**
71+
* A data flow node that is the parameter of a redirect callback for an HTTP or HTTPS request made by a Node.js process, for example `res` in `net.request(url).on('redirect', (res) => {})`.
72+
*/
73+
private class ClientRequestRedirectEvent extends RemoteFlowSource {
74+
ClientRequestRedirectEvent() {
75+
exists(NodeJSLib::ClientRequestHandler handler |
76+
this = handler.getParameter(0) and
77+
handler.getAHandledEvent() = "redirect" and
78+
handler.getClientRequest() instanceof ClientRequest
79+
)
80+
}
81+
82+
override string getSourceType() {
83+
result = "Electron ClientRequest redirect event"
84+
}
85+
}
3686
}

javascript/ql/src/semmle/javascript/frameworks/NodeJSLib.qll

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,4 +506,231 @@ module NodeJSLib {
506506
}
507507
}
508508

509+
/**
510+
* A data flow node that is an HTTP or HTTPS client request made by a Node.js server, for example `http.request(url)`.
511+
*/
512+
abstract class ClientRequest extends DataFlow::DefaultSourceNode {
513+
/**
514+
* Gets the options object or string URL used to make the request.
515+
*/
516+
abstract DataFlow::Node getOptions();
517+
}
518+
519+
/**
520+
* A data flow node that is an HTTP or HTTPS client request made by a Node.js server, for example `http.request(url)`.
521+
*/
522+
private class HttpRequest extends ClientRequest {
523+
HttpRequest() {
524+
exists(string protocol |
525+
(
526+
protocol = "http" or
527+
protocol = "https"
528+
)
529+
and
530+
this = DataFlow::moduleImport(protocol).getAMemberCall("request")
531+
)
532+
}
533+
534+
override DataFlow::Node getOptions() {
535+
result = this.(DataFlow::MethodCallNode).getArgument(0)
536+
}
537+
}
538+
539+
/**
540+
* A data flow node that is an HTTP or HTTPS client request made by a Node.js process, for example `https.get(url)`.
541+
*/
542+
private class HttpGet extends ClientRequest {
543+
HttpGet() {
544+
exists(string protocol |
545+
(
546+
protocol = "http" or
547+
protocol = "https"
548+
)
549+
and
550+
this = DataFlow::moduleImport(protocol).getAMemberCall("get")
551+
)
552+
}
553+
554+
override DataFlow::Node getOptions() {
555+
result = this.(DataFlow::MethodCallNode).getArgument(0)
556+
}
557+
}
558+
559+
/**
560+
* A data flow node that is the parameter of a result callback for an HTTP or HTTPS request made by a Node.js process, for example `res` in `https.request(url, (res) => {})`.
561+
*/
562+
private class ClientRequestCallbackParam extends DataFlow::ParameterNode, RemoteFlowSource {
563+
ClientRequestCallbackParam() {
564+
exists(ClientRequest req |
565+
this = req.(DataFlow::MethodCallNode).getCallback(1).getParameter(0)
566+
)
567+
}
568+
569+
override string getSourceType() {
570+
result = "ClientRequest callback parameter"
571+
}
572+
}
573+
574+
/**
575+
* A data flow node that is the parameter of a data callback for an HTTP or HTTPS request made by a Node.js process, for example `body` in `http.request(url, (res) => {res.on('data', (body) => {})})`.
576+
*/
577+
private class ClientRequestCallbackData extends RemoteFlowSource {
578+
ClientRequestCallbackData() {
579+
exists(ClientRequestCallbackParam rcp, DataFlow::MethodCallNode mcn |
580+
rcp.getAMethodCall("on") = mcn and
581+
mcn.getArgument(0).mayHaveStringValue("data") and
582+
this = mcn.getCallback(1).getParameter(0)
583+
)
584+
}
585+
586+
override string getSourceType() {
587+
result = "http.request data parameter"
588+
}
589+
}
590+
591+
592+
/**
593+
* A data flow node that is registered as a callback for an HTTP or HTTPS request made by a Node.js process, for example the function `handler` in `http.request(url).on(message, handler)`.
594+
*/
595+
class ClientRequestHandler extends DataFlow::FunctionNode {
596+
string handledEvent;
597+
ClientRequest clientRequest;
598+
599+
ClientRequestHandler() {
600+
exists(DataFlow::MethodCallNode mcn |
601+
clientRequest.getAMethodCall("on") = mcn and
602+
mcn.getArgument(0).mayHaveStringValue(handledEvent) and
603+
flowsTo(mcn.getArgument(1))
604+
)
605+
}
606+
607+
/**
608+
* Gets the name of an event this callback is registered for.
609+
*/
610+
string getAHandledEvent() {
611+
result = handledEvent
612+
}
613+
614+
/**
615+
* Gets a request this callback is registered for.
616+
*/
617+
ClientRequest getClientRequest() {
618+
result = clientRequest
619+
}
620+
}
621+
622+
/**
623+
* A data flow node that is the parameter of a response callback for an HTTP or HTTPS request made by a Node.js process, for example `res` in `http.request(url).on('response', (res) => {})`.
624+
*/
625+
private class ClientRequestResponseEvent extends RemoteFlowSource, DataFlow::ParameterNode {
626+
ClientRequestResponseEvent() {
627+
exists(ClientRequestHandler handler |
628+
this = handler.getParameter(0) and
629+
handler.getAHandledEvent() = "response"
630+
)
631+
}
632+
633+
override string getSourceType() {
634+
result = "ClientRequest response event"
635+
}
636+
}
637+
638+
/**
639+
* A data flow node that is the parameter of a data callback for an HTTP or HTTPS request made by a Node.js process, for example `chunk` in `http.request(url).on('response', (res) => {res.on('data', (chunk) => {})})`.
640+
*/
641+
private class ClientRequestDataEvent extends RemoteFlowSource {
642+
ClientRequestDataEvent() {
643+
exists(DataFlow::MethodCallNode mcn, ClientRequestResponseEvent cr |
644+
cr.getAMethodCall("on") = mcn and
645+
mcn.getArgument(0).mayHaveStringValue("data") and
646+
this = mcn.getCallback(1).getParameter(0)
647+
)
648+
}
649+
650+
override string getSourceType() {
651+
result = "ClientRequest data event"
652+
}
653+
}
654+
655+
/**
656+
* A data flow node that is a login callback for an HTTP or HTTPS request made by a Node.js process.
657+
*/
658+
private class ClientRequestLoginHandler extends ClientRequestHandler {
659+
ClientRequestLoginHandler() {
660+
getAHandledEvent() = "login"
661+
}
662+
}
663+
664+
/**
665+
* A data flow node that is a parameter of a login callback for an HTTP or HTTPS request made by a Node.js process, for example `res` in `http.request(url).on('login', (res, callback) => {})`.
666+
*/
667+
private class ClientRequestLoginEvent extends RemoteFlowSource {
668+
ClientRequestLoginEvent() {
669+
exists(ClientRequestLoginHandler handler |
670+
this = handler.getParameter(0)
671+
)
672+
}
673+
674+
override string getSourceType() {
675+
result = "ClientRequest login event"
676+
}
677+
}
678+
679+
/**
680+
* A data flow node that is the login callback provided by an HTTP or HTTPS request made by a Node.js process, for example `callback` in `http.request(url).on('login', (res, callback) => {})`.
681+
*/
682+
class ClientRequestLoginCallback extends DataFlow::ParameterNode {
683+
ClientRequestLoginCallback() {
684+
exists(ClientRequestLoginHandler handler |
685+
this = handler.getParameter(1)
686+
)
687+
}
688+
}
689+
690+
/**
691+
* A data flow node that is the username passed to the login callback provided by an HTTP or HTTPS request made by a Node.js process, for example `username` in `http.request(url).on('login', (res, cb) => {cb(username, password)})`.
692+
*/
693+
private class ClientRequestLoginUsername extends CredentialsExpr {
694+
ClientRequestLoginUsername() {
695+
exists(ClientRequestLoginCallback callback |
696+
this = callback.getACall().getArgument(0).asExpr()
697+
)
698+
}
699+
700+
override string getCredentialsKind() {
701+
result = "Node.js http(s) client login username"
702+
}
703+
}
704+
705+
/**
706+
* A data flow node that is the password passed to the login callback provided by an HTTP or HTTPS request made by a Node.js process, for example `password` in `http.request(url).on('login', (res, cb) => {cb(username, password)})`.
707+
*/
708+
private class ClientRequestLoginPassword extends CredentialsExpr {
709+
ClientRequestLoginPassword() {
710+
exists(ClientRequestLoginCallback callback |
711+
this = callback.getACall().getArgument(1).asExpr()
712+
)
713+
}
714+
715+
override string getCredentialsKind() {
716+
result = "Node.js http(s) client login password"
717+
}
718+
}
719+
720+
721+
/**
722+
* A data flow node that is the parameter of an error callback for an HTTP or HTTPS request made by a Node.js process, for example `err` in `http.request(url).on('error', (err) => {})`.
723+
*/
724+
private class ClientRequestErrorEvent extends RemoteFlowSource {
725+
ClientRequestErrorEvent() {
726+
exists(ClientRequestHandler handler |
727+
this = handler.getParameter(0) and
728+
handler.getAHandledEvent() = "error"
729+
)
730+
}
731+
732+
override string getSourceType() {
733+
result = "ClientRequest error event"
734+
}
735+
}
509736
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
| electron.js:7:5:7:38 | net.req ... e.com') |
2+
| electron.js:8:16:8:78 | new Cli ... POST'}) |
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import javascript
2+
3+
from NodeJSLib::ClientRequest cr
4+
select cr
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
| electron.js:10:26:10:33 | response |
2+
| electron.js:11:28:11:32 | chunk |
3+
| electron.js:16:26:16:33 | redirect |
4+
| electron.js:21:23:21:30 | authInfo |
5+
| electron.js:26:23:26:27 | error |
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import javascript
2+
3+
from RemoteFlowSource source
4+
select source
Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,33 @@
1-
const {BrowserView, BrowserWindow} = require('electron')
1+
const {BrowserView, BrowserWindow, ClientRequest, net} = require('electron')
22

33
new BrowserWindow({webPreferences: {}})
4-
new BrowserView({webPreferences: {}})
4+
new BrowserView({webPreferences: {}})
5+
6+
function makeClientRequests() {
7+
net.request('https://example.com').end();
8+
var post = new ClientRequest({url: 'https://example.com', method: 'POST'});
9+
10+
post.on('response', (response) => {
11+
response.on('data', (chunk) => {
12+
chunk[0];
13+
});
14+
});
15+
16+
post.on('redirect', (redirect) => {
17+
redirect.statusCode;
18+
post.followRedirect();
19+
});
20+
21+
post.on('login', (authInfo, callback) => {
22+
authInfo.host;
23+
callback('username', 'password');
24+
});
25+
26+
post.on('error', (error) => {
27+
error.something;
28+
});
29+
30+
post.setHeader('referer', 'https://example.com');
31+
post.write('stuff');
32+
post.end('more stuff');
33+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
| src/http.js:18:1:18:30 | http.re ... uth" }) |
2+
| src/http.js:21:15:26:6 | http.re ... \\n }) |
3+
| src/http.js:27:16:27:73 | http.re ... POST'}) |
4+
| src/https.js:18:1:18:31 | https.r ... uth" }) |
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import javascript
2+
3+
from NodeJSLib::ClientRequest cr
4+
select cr
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
| src/http.js:7:3:7:42 | res.wri ... rget }) | src/http.js:4:32:10:1 | functio ... .foo;\\n} |
22
| src/http.js:13:3:13:44 | res.set ... /html') | src/http.js:12:19:16:1 | functio ... ar");\\n} |
3-
| src/http.js:29:3:29:40 | res.set ... , "23") | src/http.js:28:19:31:1 | functio ... r2");\\n} |
3+
| src/http.js:63:3:63:40 | res.set ... , "23") | src/http.js:62:19:65:1 | functio ... r2");\\n} |
44
| src/https.js:7:3:7:42 | res.wri ... rget }) | src/https.js:4:33:10:1 | functio ... .foo;\\n} |
55
| src/https.js:13:3:13:44 | res.set ... /html') | src/https.js:12:20:16:1 | functio ... ar");\\n} |

0 commit comments

Comments
 (0)