mirror of
https://github.com/github/codeql.git
synced 2026-05-02 20:25:13 +02:00
refactor the history library model, add support for the global variable
This commit is contained in:
@@ -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
|
||||
|
||||
54
javascript/ql/src/semmle/javascript/frameworks/History.qll
Normal file
54
javascript/ql/src/semmle/javascript/frameworks/History.qll
Normal file
@@ -0,0 +1,54 @@
|
||||
/** Provides classes and predicates modelling aspects of the [`history`](https://npmjs.org/package/history) library. */
|
||||
|
||||
import javascript
|
||||
|
||||
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 }
|
||||
}
|
||||
}
|
||||
@@ -196,13 +196,7 @@ module ClientSideUrlRedirect {
|
||||
*/
|
||||
class HistoryWriteUrlSink extends ScriptUrlSink {
|
||||
HistoryWriteUrlSink() {
|
||||
this =
|
||||
API::moduleImport("history")
|
||||
.getMember("createBrowserHistory")
|
||||
.getReturn()
|
||||
.getMember(["push", "replace"])
|
||||
.getACall()
|
||||
.getArgument(0)
|
||||
this = History::getBrowserHistory().getMember(["push", "replace"]).getACall().getArgument(0)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -272,30 +272,3 @@ private class WindowLocationFlowSource extends ClientSideRemoteFlowSource {
|
||||
|
||||
override ClientSideRemoteFlowKind getKind() { result = kind }
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 =
|
||||
API::moduleImport("history")
|
||||
.getMember(["createBrowserHistory", "createHashHistory"])
|
||||
.getReturn()
|
||||
.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 }
|
||||
}
|
||||
|
||||
@@ -141,6 +141,12 @@ nodes
|
||||
| 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] |
|
||||
@@ -328,6 +334,11 @@ edges
|
||||
| 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 |
|
||||
@@ -421,6 +432,7 @@ edges
|
||||
| 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 |
|
||||
|
||||
@@ -64,5 +64,12 @@ 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
|
||||
}
|
||||
Reference in New Issue
Block a user