mirror of
https://github.com/github/codeql.git
synced 2026-05-01 19:55:15 +02:00
Merge pull request #629 from esben-semmle/js/persistent-read-taint
JS: add persistent storage taint steps
This commit is contained in:
@@ -60,6 +60,7 @@ import semmle.javascript.frameworks.Azure
|
||||
import semmle.javascript.frameworks.Babel
|
||||
import semmle.javascript.frameworks.ComposedFunctions
|
||||
import semmle.javascript.frameworks.ClientRequests
|
||||
import semmle.javascript.frameworks.CookieLibraries
|
||||
import semmle.javascript.frameworks.Credentials
|
||||
import semmle.javascript.frameworks.CryptoLibraries
|
||||
import semmle.javascript.frameworks.DigitalOcean
|
||||
|
||||
@@ -65,3 +65,27 @@ abstract class DatabaseAccess extends DataFlow::Node {
|
||||
/** Gets an argument to this database access that is interpreted as a query. */
|
||||
abstract DataFlow::Node getAQueryArgument();
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow node that reads persistent data.
|
||||
*/
|
||||
abstract class PersistentReadAccess extends DataFlow::Node {
|
||||
|
||||
/**
|
||||
* Gets a corresponding persistent write, if any.
|
||||
*/
|
||||
abstract PersistentWriteAccess getAWrite();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow node that writes persistent data.
|
||||
*/
|
||||
abstract class PersistentWriteAccess extends DataFlow::Node {
|
||||
|
||||
/**
|
||||
* Gets the data flow node corresponding to the written value.
|
||||
*/
|
||||
abstract DataFlow::Node getValue();
|
||||
|
||||
}
|
||||
|
||||
@@ -232,6 +232,24 @@ module TaintTracking {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint propagating data flow edge through persistent storage.
|
||||
*/
|
||||
private class StorageTaintStep extends AdditionalTaintStep {
|
||||
|
||||
PersistentReadAccess read;
|
||||
|
||||
StorageTaintStep() {
|
||||
this = read
|
||||
}
|
||||
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
pred = read.getAWrite().getValue() and
|
||||
succ = read
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint propagating data flow edge caused by the builtin array functions.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
|
||||
/**
|
||||
* Provides classes for reasoning about cookies.
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
/**
|
||||
* A model of the `js-cookie` library (https://github.com/js-cookie/js-cookie).
|
||||
*/
|
||||
private module JsCookie {
|
||||
/**
|
||||
* Gets a function call that invokes method `name` of the `js-cookie` library.
|
||||
*/
|
||||
DataFlow::CallNode libMemberCall(string name) {
|
||||
result = DataFlow::globalVarRef("Cookie").getAMemberCall(name) or
|
||||
result = DataFlow::globalVarRef("Cookie").getAMemberCall("noConflict").getAMemberCall(name) or
|
||||
result = DataFlow::moduleMember("js-cookie", name).getACall()
|
||||
}
|
||||
|
||||
class ReadAccess extends PersistentReadAccess, DataFlow::CallNode {
|
||||
ReadAccess() { this = libMemberCall("get") }
|
||||
|
||||
override PersistentWriteAccess getAWrite() {
|
||||
getArgument(0).mayHaveStringValue(result.(WriteAccess).getKey())
|
||||
}
|
||||
}
|
||||
|
||||
class WriteAccess extends PersistentWriteAccess, DataFlow::CallNode {
|
||||
WriteAccess() { this = libMemberCall("set") }
|
||||
|
||||
string getKey() { getArgument(0).mayHaveStringValue(result) }
|
||||
|
||||
override DataFlow::Node getValue() { result = getArgument(1) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A model of the `browser-cookies` library (https://github.com/voltace/browser-cookies).
|
||||
*/
|
||||
private module BrowserCookies {
|
||||
/**
|
||||
* Gets a function call that invokes method `name` of the `browser-cookies` library.
|
||||
*/
|
||||
DataFlow::CallNode libMemberCall(string name) {
|
||||
result = DataFlow::moduleMember("browser-cookies", name).getACall()
|
||||
}
|
||||
|
||||
class ReadAccess extends PersistentReadAccess, DataFlow::CallNode {
|
||||
ReadAccess() { this = libMemberCall("get") }
|
||||
|
||||
override PersistentWriteAccess getAWrite() {
|
||||
getArgument(0).mayHaveStringValue(result.(WriteAccess).getKey())
|
||||
}
|
||||
}
|
||||
|
||||
class WriteAccess extends PersistentWriteAccess, DataFlow::CallNode {
|
||||
WriteAccess() { this = libMemberCall("set") }
|
||||
|
||||
string getKey() { getArgument(0).mayHaveStringValue(result) }
|
||||
|
||||
override DataFlow::Node getValue() { result = getArgument(1) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A model of the `cookie` library (https://github.com/jshttp/cookie).
|
||||
*/
|
||||
private module LibCookie {
|
||||
/**
|
||||
* Gets a function call that invokes method `name` of the `cookie` library.
|
||||
*/
|
||||
DataFlow::CallNode libMemberCall(string name) {
|
||||
result = DataFlow::moduleMember("cookie", name).getACall()
|
||||
}
|
||||
|
||||
class ReadAccess extends PersistentReadAccess {
|
||||
string key;
|
||||
ReadAccess() { this = libMemberCall("parse").getAPropertyRead(key) }
|
||||
|
||||
override PersistentWriteAccess getAWrite() {
|
||||
key = result.(WriteAccess).getKey()
|
||||
}
|
||||
}
|
||||
|
||||
class WriteAccess extends PersistentWriteAccess, DataFlow::CallNode {
|
||||
WriteAccess() { this = libMemberCall("serialize") }
|
||||
|
||||
string getKey() { getArgument(0).mayHaveStringValue(result) }
|
||||
|
||||
override DataFlow::Node getValue() { result = getArgument(1) }
|
||||
}
|
||||
}
|
||||
@@ -167,6 +167,44 @@ class WebStorageWrite extends Expr {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Persistent storage through web storage such as `localStorage` or `sessionStorage`.
|
||||
*/
|
||||
private module PersistentWebStorage {
|
||||
private DataFlow::SourceNode webStorage(string kind) {
|
||||
(kind = "localStorage" or kind = "sessionStorage") and
|
||||
result = DataFlow::globalVarRef(kind)
|
||||
}
|
||||
|
||||
/**
|
||||
* A read access.
|
||||
*/
|
||||
class ReadAccess extends PersistentReadAccess, DataFlow::CallNode {
|
||||
string kind;
|
||||
|
||||
ReadAccess() { this = webStorage(kind).getAMethodCall("getItem") }
|
||||
|
||||
override PersistentWriteAccess getAWrite() {
|
||||
getArgument(0).mayHaveStringValue(result.(WriteAccess).getKey()) and
|
||||
result.(WriteAccess).getKind() = kind
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A write access.
|
||||
*/
|
||||
class WriteAccess extends PersistentWriteAccess, DataFlow::CallNode {
|
||||
string kind;
|
||||
|
||||
WriteAccess() { this = webStorage(kind).getAMethodCall("setItem") }
|
||||
|
||||
string getKey() { getArgument(0).mayHaveStringValue(result) }
|
||||
|
||||
string getKind() { result = kind }
|
||||
|
||||
override DataFlow::Node getValue() { result = getArgument(1) }
|
||||
}
|
||||
}
|
||||
/**
|
||||
* An event handler that handles `postMessage` events.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user