Merge pull request #6002 from erik-krogh/history

Approved by asgerf
This commit is contained in:
CodeQL CI
2021-06-08 13:17:38 -07:00
committed by GitHub
6 changed files with 125 additions and 0 deletions

View File

@@ -0,0 +1,4 @@
lgtm,codescanning
* Taint sources and sinks from the [history](https://npmjs.com/package/history) library are now recognized.
Affected packages are
[history](https://www.npmjs.com/package/history)

View File

@@ -93,6 +93,7 @@ import semmle.javascript.frameworks.FormParsers
import semmle.javascript.frameworks.jQuery
import semmle.javascript.frameworks.JWT
import semmle.javascript.frameworks.Handlebars
import semmle.javascript.frameworks.History
import semmle.javascript.frameworks.Immutable
import semmle.javascript.frameworks.LazyCache
import semmle.javascript.frameworks.LodashUnderscore

View File

@@ -0,0 +1,55 @@
/** Provides classes and predicates modelling aspects of the [`history`](https://npmjs.org/package/history) library. */
import javascript
/** Provides classes modelling the [`history`](https://npmjs.org/package/history) library. */
module History {
/** The global variable `HistoryLibrary` as an entry point for API graphs. */
private class HistoryGlobalEntry extends API::EntryPoint {
HistoryGlobalEntry() { this = "HistoryLibrary" }
override DataFlow::SourceNode getAUse() { result = DataFlow::globalVarRef("HistoryLibrary") }
override DataFlow::Node getARhs() { none() }
}
/**
* Gets a reference to the [`history`](https://npmjs.org/package/history) library.
*/
private API::Node history() {
result = [API::moduleImport("history"), API::root().getASuccessor(any(HistoryGlobalEntry h))]
}
/**
* Gets a browser history instance.
* This history instance uses the native browser history API.
*/
API::Node getBrowserHistory() { result = history().getMember("createBrowserHistory").getReturn() }
/**
* Gets a hash history instance.
* This history instance only manipulates the URL hash, which cannot cause XSS.
*/
API::Node getHashHistory() { result = history().getMember("createHashHistory").getReturn() }
/**
* A user-controlled location value read from the [history](http://npmjs.org/package/history) library.
*/
private class HistoryLibaryRemoteFlow extends ClientSideRemoteFlowSource {
ClientSideRemoteFlowKind kind;
HistoryLibaryRemoteFlow() {
exists(API::Node loc | loc = [getBrowserHistory(), getHashHistory()].getMember("location") |
this = loc.getMember("hash").getAnImmediateUse() and kind.isFragment()
or
this = loc.getMember("pathname").getAnImmediateUse() and kind.isPath()
or
this = loc.getMember("search").getAnImmediateUse() and kind.isQuery()
)
}
override string getSourceType() { result = "Window location" }
override ClientSideRemoteFlowKind getKind() { result = kind }
}
}

View File

@@ -191,6 +191,15 @@ module ClientSideUrlRedirect {
}
}
/**
* A write to the location using the [history](https://npmjs.com/package/history) library
*/
class HistoryWriteUrlSink extends ScriptUrlSink {
HistoryWriteUrlSink() {
this = History::getBrowserHistory().getMember(["push", "replace"]).getACall().getArgument(0)
}
}
/**
* A call to change the current url with a Next.js router.
*/

View File

@@ -129,6 +129,24 @@ nodes
| tst13.js:52:34:52:34 | e |
| tst13.js:53:28:53:28 | e |
| tst13.js:53:28:53:28 | e |
| tst13.js:59:9:59:52 | payload |
| tst13.js:59:19:59:42 | documen ... .search |
| tst13.js:59:19:59:42 | documen ... .search |
| tst13.js:59:19:59:52 | documen ... bstr(1) |
| tst13.js:61:18:61:24 | payload |
| tst13.js:61:18:61:24 | payload |
| tst13.js:65:9:65:49 | payload |
| tst13.js:65:19:65:39 | history ... on.hash |
| tst13.js:65:19:65:39 | history ... on.hash |
| tst13.js:65:19:65:49 | history ... bstr(1) |
| tst13.js:67:21:67:27 | payload |
| tst13.js:67:21:67:27 | payload |
| tst13.js:72:9:72:49 | payload |
| tst13.js:72:19:72:39 | history ... on.hash |
| tst13.js:72:19:72:39 | history ... on.hash |
| tst13.js:72:19:72:49 | history ... bstr(1) |
| tst13.js:74:21:74:27 | payload |
| tst13.js:74:21:74:27 | payload |
| tst.js:2:19:2:69 | /.*redi ... n.href) |
| tst.js:2:19:2:72 | /.*redi ... ref)[1] |
| tst.js:2:19:2:72 | /.*redi ... ref)[1] |
@@ -306,6 +324,21 @@ edges
| tst13.js:52:34:52:34 | e | tst13.js:53:28:53:28 | e |
| tst13.js:52:34:52:34 | e | tst13.js:53:28:53:28 | e |
| tst13.js:52:34:52:34 | e | tst13.js:53:28:53:28 | e |
| tst13.js:59:9:59:52 | payload | tst13.js:61:18:61:24 | payload |
| tst13.js:59:9:59:52 | payload | tst13.js:61:18:61:24 | payload |
| tst13.js:59:19:59:42 | documen ... .search | tst13.js:59:19:59:52 | documen ... bstr(1) |
| tst13.js:59:19:59:42 | documen ... .search | tst13.js:59:19:59:52 | documen ... bstr(1) |
| tst13.js:59:19:59:52 | documen ... bstr(1) | tst13.js:59:9:59:52 | payload |
| tst13.js:65:9:65:49 | payload | tst13.js:67:21:67:27 | payload |
| tst13.js:65:9:65:49 | payload | tst13.js:67:21:67:27 | payload |
| tst13.js:65:19:65:39 | history ... on.hash | tst13.js:65:19:65:49 | history ... bstr(1) |
| tst13.js:65:19:65:39 | history ... on.hash | tst13.js:65:19:65:49 | history ... bstr(1) |
| tst13.js:65:19:65:49 | history ... bstr(1) | tst13.js:65:9:65:49 | payload |
| tst13.js:72:9:72:49 | payload | tst13.js:74:21:74:27 | payload |
| tst13.js:72:9:72:49 | payload | tst13.js:74:21:74:27 | payload |
| tst13.js:72:19:72:39 | history ... on.hash | tst13.js:72:19:72:49 | history ... bstr(1) |
| tst13.js:72:19:72:39 | history ... on.hash | tst13.js:72:19:72:49 | history ... bstr(1) |
| tst13.js:72:19:72:49 | history ... bstr(1) | tst13.js:72:9:72:49 | payload |
| tst.js:2:19:2:69 | /.*redi ... n.href) | tst.js:2:19:2:72 | /.*redi ... ref)[1] |
| tst.js:2:19:2:69 | /.*redi ... n.href) | tst.js:2:19:2:72 | /.*redi ... ref)[1] |
| tst.js:2:47:2:63 | document.location | tst.js:2:47:2:68 | documen ... on.href |
@@ -397,6 +430,9 @@ edges
| tst13.js:44:14:44:20 | payload | tst13.js:2:19:2:42 | documen ... .search | tst13.js:44:14:44:20 | payload | Untrusted URL redirection due to $@. | tst13.js:2:19:2:42 | documen ... .search | user-provided value |
| tst13.js:50:23:50:23 | e | tst13.js:49:32:49:32 | e | tst13.js:50:23:50:23 | e | Untrusted URL redirection due to $@. | tst13.js:49:32:49:32 | e | user-provided value |
| tst13.js:53:28:53:28 | e | tst13.js:52:34:52:34 | e | tst13.js:53:28:53:28 | e | Untrusted URL redirection due to $@. | tst13.js:52:34:52:34 | e | user-provided value |
| tst13.js:61:18:61:24 | payload | tst13.js:59:19:59:42 | documen ... .search | tst13.js:61:18:61:24 | payload | Untrusted URL redirection due to $@. | tst13.js:59:19:59:42 | documen ... .search | user-provided value |
| tst13.js:67:21:67:27 | payload | tst13.js:65:19:65:39 | history ... on.hash | tst13.js:67:21:67:27 | payload | Untrusted URL redirection due to $@. | tst13.js:65:19:65:39 | history ... on.hash | user-provided value |
| tst13.js:74:21:74:27 | payload | tst13.js:72:19:72:39 | history ... on.hash | tst13.js:74:21:74:27 | payload | Untrusted URL redirection due to $@. | tst13.js:72:19:72:39 | history ... on.hash | user-provided value |
| tst.js:2:19:2:72 | /.*redi ... ref)[1] | tst.js:2:47:2:63 | document.location | tst.js:2:19:2:72 | /.*redi ... ref)[1] | Untrusted URL redirection due to $@. | tst.js:2:47:2:63 | document.location | user-provided value |
| tst.js:2:19:2:72 | /.*redi ... ref)[1] | tst.js:2:47:2:68 | documen ... on.href | tst.js:2:19:2:72 | /.*redi ... ref)[1] | Untrusted URL redirection due to $@. | tst.js:2:47:2:68 | documen ... on.href | user-provided value |
| tst.js:6:20:6:59 | indirec ... ref)[1] | tst.js:6:34:6:50 | document.location | tst.js:6:20:6:59 | indirec ... ref)[1] | Untrusted URL redirection due to $@. | tst.js:6:34:6:50 | document.location | user-provided value |

View File

@@ -53,3 +53,23 @@ function foo() {
self.importScripts(e); // NOT OK
}
})();
function bar() {
const history = require('history').createBrowserHistory();
var payload = document.location.search.substr(1);
history.push(payload); // NOT OK
}
function baz() {
const history = require('history').createBrowserHistory();
var payload = history.location.hash.substr(1);
history.replace(payload); // NOT OK
}
function quz() {
const history = HistoryLibrary.createBrowserHistory();
var payload = history.location.hash.substr(1);
history.replace(payload); // NOT OK
}