Merge pull request #629 from esben-semmle/js/persistent-read-taint

JS: add persistent storage taint steps
This commit is contained in:
Max Schaefer
2018-12-13 08:24:42 +00:00
committed by GitHub
23 changed files with 284 additions and 0 deletions

View File

@@ -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

View File

@@ -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();
}

View File

@@ -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.
*/

View File

@@ -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) }
}
}

View File

@@ -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.
*/