Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@ Adds a new taint source. Most taint-tracking queries will use the new source.

- **type**: Name of a type from which to evaluate **path**.
- **path**: Access path leading to the source.
- **kind**: Kind of source to add. Currently only **remote** is used.
- **kind**: Kind of source to add. See the section on source kinds for a list of supported kinds.

Example:

Expand Down Expand Up @@ -553,7 +553,16 @@ Kinds
Source kinds
~~~~~~~~~~~~

See documentation below for :ref:`Threat models <threat-models-javascript>`.
- **remote**: A general source of remote flow.
- **browser**: A source in the browser environment that does not fit a more specific browser kind.
- **browser-url-query**: A source derived from the query parameters of the browser URL, such as ``location.search``.
- **browser-url-fragment**: A source derived from the fragment part of the browser URL, such as ``location.hash``.
- **browser-url-path**: A source derived from the pathname of the browser URL, such as ``location.pathname``.
- **browser-url**: A source derived from the browser URL, where the untrusted part is prefixed by trusted data such as the scheme and hostname.
- **browser-window-name**: A source derived from the window name, such as ``window.name``.
- **browser-message-event**: A source derived from cross-window message passing, such as ``event`` in ``window.onmessage = event => {...}``.

See also :ref:`Threat models <threat-models-javascript>`.

Sink kinds
~~~~~~~~~~
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Added support for browser-specific source kinds (`browser`, `browser-url-query`, `browser-url-fragment`, `browser-url-path`, `browser-url`, `browser-window-name`, `browser-message-event`) that can be used in data extensions to model sources in browser environments.
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,18 @@ private class RemoteFlowSourceFromMaD extends RemoteFlowSource {
override string getSourceType() { result = "Remote flow" }
}

private class ClientSideRemoteFlowSourceFromMaD extends ClientSideRemoteFlowSource {
private ClientSideRemoteFlowKind kind;

ClientSideRemoteFlowSourceFromMaD() { ModelOutput::sourceNode(this, kind) }

override ClientSideRemoteFlowKind getKind() { result = kind }

override string getSourceType() {
result = "Source node (" + this.getThreatModel() + ") [from data-extension]"
}
}

/**
* A threat-model flow source originating from a data extension.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,53 +43,65 @@ import Cached

/**
* A type of remote flow source that is specific to the browser environment.
*
* The underlying string also corresponds to a source kind.
*/
class ClientSideRemoteFlowKind extends string {
ClientSideRemoteFlowKind() {
this = ["query", "fragment", "path", "url", "name", "message-event"]
this =
[
"browser", "browser-url-query", "browser-url-fragment", "browser-url-path", "browser-url",
"browser-window-name", "browser-message-event"
]
}

/**
* Holds if this is the `query` kind, describing sources derived from the query parameters of the browser URL,
* Holds if this is the `browser` kind, indicating a remote source in a browser context, that does not fit into one
* of the more specific kinds.
*/
predicate isGenericBrowserSourceKind() { this = "browser" }

/**
* Holds if this is the `browser-url-query` kind, describing sources derived from the query parameters of the browser URL,
* such as `location.search`.
*/
predicate isQuery() { this = "query" }
predicate isQuery() { this = "browser-url-query" }

/**
* Holds if this is the `frgament` kind, describing sources derived from the fragment part of the browser URL,
* Holds if this is the `browser-url-fragment` kind, describing sources derived from the fragment part of the browser URL,
* such as `location.hash`.
*/
predicate isFragment() { this = "fragment" }
predicate isFragment() { this = "browser-url-fragment" }

/**
* Holds if this is the `path` kind, describing sources derived from the pathname of the browser URL,
* Holds if this is the `browser-url-path` kind, describing sources derived from the pathname of the browser URL,
* such as `location.pathname`.
*/
predicate isPath() { this = "path" }
predicate isPath() { this = "browser-url-path" }

/**
* Holds if this is the `url` kind, describing sources derived from the browser URL,
* Holds if this is the `browser-url` kind, describing sources derived from the browser URL,
* where the untrusted part of the URL is prefixed by trusted data, such as the scheme and hostname.
*/
predicate isUrl() { this = "url" }
predicate isUrl() { this = "browser-url" }

/** Holds if this is the `query` or `fragment` kind. */
/** Holds if this is the `browser-url-query` or `browser-url-fragment` kind. */
predicate isQueryOrFragment() { this.isQuery() or this.isFragment() }

/** Holds if this is the `path`, `query`, or `fragment` kind. */
/** Holds if this is the `browser-url-path`, `browser-url-query`, or `browser-url-fragment` kind. */
predicate isPathOrQueryOrFragment() { this.isPath() or this.isQuery() or this.isFragment() }

/** Holds if this is the `path` or `url` kind. */
/** Holds if this is the `browser-url-path` or `browser-url` kind. */
predicate isPathOrUrl() { this.isPath() or this.isUrl() }

/** Holds if this is the `name` kind, describing sources derived from the window name, such as `window.name`. */
predicate isWindowName() { this = "name" }
/** Holds if this is the `browser-window-name` kind, describing sources derived from the window name, such as `window.name`. */
predicate isWindowName() { this = "browser-window-name" }

/**
* Holds if this is the `message-event` kind, describing sources derived from cross-window message passing,
* Holds if this is the `browser-message-event` kind, describing sources derived from cross-window message passing,
* such as `event` in `window.onmessage = event => {...}`.
*/
predicate isMessageEvent() { this = "message-event" }
predicate isMessageEvent() { this = "browser-message-event" }
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
| clientSide.js:14:5:14:64 | request ... search) | clientSide.js:14:42:14:63 | window. ... .search | clientSide.js:14:13:14:63 | 'https: ... .search | The $@ of this request depends on a $@. | clientSide.js:14:13:14:63 | 'https: ... .search | URL | clientSide.js:14:42:14:63 | window. ... .search | user-provided value |
| clientSide.js:17:5:17:58 | request ... '/id') | clientSide.js:16:22:16:41 | window.location.hash | clientSide.js:17:13:17:57 | 'https: ... + '/id' | The $@ of this request depends on a $@. | clientSide.js:17:13:17:57 | 'https: ... + '/id' | URL | clientSide.js:16:22:16:41 | window.location.hash | user-provided value |
| clientSide.js:21:5:21:54 | request ... '/id') | clientSide.js:20:18:20:28 | window.name | clientSide.js:21:13:21:53 | 'https: ... + '/id' | The $@ of this request depends on a $@. | clientSide.js:21:13:21:53 | 'https: ... + '/id' | URL | clientSide.js:20:18:20:28 | window.name | user-provided value |
| clientSide.js:27:5:27:19 | request(custom) | clientSide.js:26:20:26:56 | require ... ource() | clientSide.js:27:13:27:18 | custom | The $@ of this request depends on a $@. | clientSide.js:27:13:27:18 | custom | URL | clientSide.js:26:20:26:56 | require ... ource() | user-provided value |
edges
| clientSide.js:11:11:11:15 | query | clientSide.js:12:42:12:46 | query | provenance | |
| clientSide.js:11:19:11:40 | window. ... .search | clientSide.js:11:19:11:53 | window. ... ring(1) | provenance | |
Expand All @@ -16,6 +17,8 @@ edges
| clientSide.js:20:11:20:14 | name | clientSide.js:21:42:21:45 | name | provenance | |
| clientSide.js:20:18:20:28 | window.name | clientSide.js:20:11:20:14 | name | provenance | |
| clientSide.js:21:42:21:45 | name | clientSide.js:21:13:21:53 | 'https: ... + '/id' | provenance | |
| clientSide.js:26:11:26:16 | custom | clientSide.js:27:13:27:18 | custom | provenance | |
| clientSide.js:26:20:26:56 | require ... ource() | clientSide.js:26:11:26:16 | custom | provenance | |
nodes
| clientSide.js:11:11:11:15 | query | semmle.label | query |
| clientSide.js:11:19:11:40 | window. ... .search | semmle.label | window. ... .search |
Expand All @@ -33,4 +36,7 @@ nodes
| clientSide.js:20:18:20:28 | window.name | semmle.label | window.name |
| clientSide.js:21:13:21:53 | 'https: ... + '/id' | semmle.label | 'https: ... + '/id' |
| clientSide.js:21:42:21:45 | name | semmle.label | name |
| clientSide.js:26:11:26:16 | custom | semmle.label | custom |
| clientSide.js:26:20:26:56 | require ... ource() | semmle.label | require ... ource() |
| clientSide.js:27:13:27:18 | custom | semmle.label | custom |
subpaths
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
extensions:
- addsTo:
pack: codeql/javascript-all
extensible: sourceModel
data:
- ['testlib', 'Member[getBrowserSource].ReturnValue', 'browser-url-query']
- ['testlib', 'Member[getServerSource].ReturnValue', 'remote']
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
| serverSide.js:143:5:143:26 | axios.g ... t.href) | serverSide.js:139:19:139:31 | req.query.url | serverSide.js:143:15:143:25 | target.href | The $@ of this request depends on a $@. | serverSide.js:143:15:143:25 | target.href | URL | serverSide.js:139:19:139:31 | req.query.url | user-provided value |
| serverSide.js:145:5:145:25 | axios.g ... dedUrl) | serverSide.js:139:19:139:31 | req.query.url | serverSide.js:145:15:145:24 | encodedUrl | The $@ of this request depends on a $@. | serverSide.js:145:15:145:24 | encodedUrl | URL | serverSide.js:139:19:139:31 | req.query.url | user-provided value |
| serverSide.js:147:5:147:25 | axios.g ... pedUrl) | serverSide.js:139:19:139:31 | req.query.url | serverSide.js:147:15:147:24 | escapedUrl | The $@ of this request depends on a $@. | serverSide.js:147:15:147:24 | escapedUrl | URL | serverSide.js:139:19:139:31 | req.query.url | user-provided value |
| serverSide.js:151:1:151:15 | request(custom) | serverSide.js:150:16:150:51 | require ... ource() | serverSide.js:151:9:151:14 | custom | The $@ of this request depends on a $@. | serverSide.js:151:9:151:14 | custom | URL | serverSide.js:150:16:150:51 | require ... ource() | user-provided value |
edges
| Request/app/api/proxy/route2.serverSide.ts:4:9:4:15 | { url } | Request/app/api/proxy/route2.serverSide.ts:4:11:4:13 | url | provenance | |
| Request/app/api/proxy/route2.serverSide.ts:4:11:4:13 | url | Request/app/api/proxy/route2.serverSide.ts:5:27:5:29 | url | provenance | |
Expand Down Expand Up @@ -144,6 +145,8 @@ edges
| serverSide.js:146:11:146:20 | escapedUrl | serverSide.js:147:15:147:24 | escapedUrl | provenance | |
| serverSide.js:146:24:146:36 | escape(input) | serverSide.js:146:11:146:20 | escapedUrl | provenance | |
| serverSide.js:146:31:146:35 | input | serverSide.js:146:24:146:36 | escape(input) | provenance | |
| serverSide.js:150:7:150:12 | custom | serverSide.js:151:9:151:14 | custom | provenance | |
| serverSide.js:150:16:150:51 | require ... ource() | serverSide.js:150:7:150:12 | custom | provenance | |
nodes
| Request/app/api/proxy/route2.serverSide.ts:4:9:4:15 | { url } | semmle.label | { url } |
| Request/app/api/proxy/route2.serverSide.ts:4:11:4:13 | url | semmle.label | url |
Expand Down Expand Up @@ -271,4 +274,7 @@ nodes
| serverSide.js:146:24:146:36 | escape(input) | semmle.label | escape(input) |
| serverSide.js:146:31:146:35 | input | semmle.label | input |
| serverSide.js:147:15:147:24 | escapedUrl | semmle.label | escapedUrl |
| serverSide.js:150:7:150:12 | custom | semmle.label | custom |
| serverSide.js:150:16:150:51 | require ... ource() | semmle.label | require ... ource() |
| serverSide.js:151:9:151:14 | custom | semmle.label | custom |
subpaths
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
extensions:
- addsTo:
pack: codeql/javascript-all
extensible: sourceModel
data:
- ['testlib', 'Member[getBrowserSource].ReturnValue', 'browser-url-query']
- ['testlib', 'Member[getServerSource].ReturnValue', 'remote']
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,7 @@ export function MyComponent() {
request('https://example.com/api?q=' + name);

request(window.location.href + '?q=123');

const custom = require('testlib').getBrowserSource(); // $ Source[js/client-side-request-forgery]
request(custom); // $ Alert[js/client-side-request-forgery]
}
Original file line number Diff line number Diff line change
Expand Up @@ -146,3 +146,6 @@ var server2 = http.createServer(function (req, res) {
const escapedUrl = escape(input);
axios.get(escapedUrl); // $ Alert[js/request-forgery]
});

const custom = require('testlib').getServerSource(); // $ Source[js/request-forgery]
request(custom); // $ Alert[js/request-forgery]
Loading