mirror of
https://github.com/github/codeql.git
synced 2026-03-30 12:18:18 +02:00
Merge pull request #21368 from asgerf/browser-sources
JS: Add 'browser' source kinds
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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
|
||||
~~~~~~~~~~
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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" }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 | |
|
||||
@@ -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 |
|
||||
@@ -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
|
||||
|
||||
@@ -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']
|
||||
@@ -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 | |
|
||||
@@ -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 |
|
||||
@@ -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
|
||||
|
||||
@@ -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']
|
||||
@@ -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]
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
|
||||
Reference in New Issue
Block a user