JS: Refactor DOM libs to use DataFlow more

This commit is contained in:
Asger F
2019-03-28 15:37:17 +00:00
parent 4908902f47
commit e90a889f76
5 changed files with 102 additions and 99 deletions

View File

@@ -253,4 +253,86 @@ module DOM {
reason = "must not contain any space characters"
)
}
/** Gets a call that queries the DOM for a collection of DOM nodes. */
private DataFlow::SourceNode domElementCollection() {
exists(string collectionName |
collectionName = "getElementsByClassName" or
collectionName = "getElementsByName" or
collectionName = "getElementsByTagName" or
collectionName = "getElementsByTagNameNS" or
collectionName = "querySelectorAll"
|
(
result = documentRef().getAMethodCall(collectionName) or
result = DataFlow::globalVarRef(collectionName).getACall()
)
)
}
/** Gets a call that creates a DOM node or queries the DOM for a DOM node. */
private DataFlow::SourceNode domElementCreationOrQuery() {
exists(string methodName |
methodName = "createElement" or
methodName = "createElementNS" or
methodName = "createRange" or
methodName = "getElementById" or
methodName = "querySelector"
|
result = documentRef().getAMethodCall(methodName) or
result = DataFlow::globalVarRef(methodName).getACall()
)
}
/** Gets a data flow node that refer directly to a value from the DOM. */
DataFlow::SourceNode domValueSource() {
result.asExpr().(VarAccess).getVariable() instanceof DOMGlobalVariable or
result = domValueRef().getAPropertyRead(_) or
result = domElementCreationOrQuery() or
result = domElementCollection()
}
/** Gets a data flow node that may refer to a value from the DOM. */
private DataFlow::SourceNode domValueRef(DataFlow::TypeTracker t) {
t.start() and
result = domValueSource()
or
exists(DataFlow::TypeTracker t2 | result = domValueRef(t2).track(t2, t))
}
/** Gets a data flow node that may refer to a value from the DOM. */
DataFlow::SourceNode domValueRef() { result = domValueRef(DataFlow::TypeTracker::end()) }
/** Gets a data flow node that directly refers to the DOM `location` object. */
DataFlow::SourceNode locationSource() {
result = domValueRef().getAPropertyRead("location")
or
result = DataFlow::globalVarRef("location")
}
/** Gets a reference to the DOM `location` object. */
private DataFlow::SourceNode locationRef(DataFlow::TypeTracker t) {
t.start() and
result = locationSource()
or
exists(DataFlow::TypeTracker t2 | result = locationRef(t2).track(t2, t))
}
/** Gets a reference to the DOM `location` object. */
DataFlow::SourceNode locationRef() { result = locationRef(DataFlow::TypeTracker::end()) }
/**
* Gets a reference to the 'document' object.
*/
private DataFlow::SourceNode documentRef(DataFlow::TypeTracker t) {
t.start() and
result = DataFlow::globalVarRef("document")
or
exists(DataFlow::TypeTracker t2 | result = documentRef(t2).track(t2, t))
}
/**
* Gets a reference to the 'document' object.
*/
DataFlow::SourceNode documentRef() { result = documentRef(DataFlow::TypeTracker::end()) }
}

View File

@@ -121,10 +121,9 @@ module ClientSideUrlRedirect {
)
or
// A call to `location.replace` or `location.assign`
exists(MethodCallExpr locationCall, string name |
isLocation(locationCall.getReceiver()) and
name = locationCall.getMethodName() and
astNode = locationCall.getArgument(0)
exists(DataFlow::MethodCallNode locationCall, string name |
locationCall = DOM::locationRef().getAMethodCall(name) and
this = locationCall.getArgument(0)
|
name = "replace" or name = "assign"
)
@@ -134,8 +133,7 @@ module ClientSideUrlRedirect {
or
// An assignment to `location.href`, `location.protocol` or `location.hostname`
exists(DataFlow::PropWrite pw, string propName |
isLocation(pw.getBase().asExpr()) and
propName = pw.getPropertyName() and
pw = DOM::locationRef().getAPropertyWrite(propName) and
this = pw.getRhs()
|
propName = "href" or propName = "protocol" or propName = "hostname"

View File

@@ -18,113 +18,36 @@ class DOMGlobalVariable extends GlobalVariable {
}
}
private DataFlow::SourceNode domElementCollection() {
exists(string collectionName |
collectionName = "getElementsByClassName" or
collectionName = "getElementsByName" or
collectionName = "getElementsByTagName" or
collectionName = "getElementsByTagNameNS" or
collectionName = "querySelectorAll"
|
(
result = document().getAMethodCall(collectionName) or
result = DataFlow::globalVarRef(collectionName).getACall()
)
)
}
private DataFlow::SourceNode domElementCreationOrQuery() {
exists(string methodName |
methodName = "createElement" or
methodName = "createElementNS" or
methodName = "createRange" or
methodName = "getElementById" or
methodName = "querySelector"
|
result = document().getAMethodCall(methodName) or
result = DataFlow::globalVarRef(methodName).getACall()
)
}
private DataFlow::SourceNode domValueSource(DataFlow::TypeTracker t) {
t.start() and
(
result.asExpr().(VarAccess).getVariable() instanceof DOMGlobalVariable or
result = domValueSource().getAPropertyRead(_) or
result = domElementCreationOrQuery() or
result = domElementCollection()
)
or
exists(DataFlow::TypeTracker t2 |
result = domValueSource(t2).track(t2, t)
)
}
private DataFlow::SourceNode domValueSource() {
result = domValueSource(DataFlow::TypeTracker::end())
}
/** Holds if `e` could hold a value that comes from the DOM. */
predicate isDomValue(Expr e) { domValueSource().flowsToExpr(e) }
/** Gets a reference to the DOM `location` object. */
private DataFlow::SourceNode locationRef(DataFlow::TypeTracker t) {
t.start() and
exists(Expr e | result.asExpr() = e |
e = domValueSource().getAPropertyReference("location").asExpr() or
e.accessesGlobal("location")
)
or
exists(DataFlow::TypeTracker t2 |
result = locationRef(t2).track(t2, t)
)
}
/** Gets a reference to the DOM `location` object. */
private DataFlow::SourceNode locationRef() {
result = locationRef(DataFlow::TypeTracker::end())
}
predicate isDomValue(Expr e) { DOM::domValueRef().flowsToExpr(e) }
/** Holds if `e` could refer to the `location` property of a DOM node. */
predicate isLocation(Expr e) {
locationRef().flowsToExpr(e)
}
/**
* Gets a reference to the 'document' object.
*/
private DataFlow::SourceNode document(DataFlow::TypeTracker t) {
t.start() and
result = DataFlow::globalVarRef("document")
e = DOM::domValueRef().getAPropertyReference("location").asExpr()
or
exists(DataFlow::TypeTracker t2 |
result = document(t2).track(t2, t)
)
e.accessesGlobal("location")
}
/**
* Gets a reference to the 'document' object.
*/
DataFlow::SourceNode document() { result = document(DataFlow::TypeTracker::end()) }
DataFlow::SourceNode document() { result = DOM::documentRef() }
/** Holds if `e` could refer to the `document` object. */
predicate isDocument(Expr e) { document().flowsToExpr(e) }
predicate isDocument(Expr e) { DOM::documentRef().flowsToExpr(e) }
/** Holds if `e` could refer to the document URL. */
predicate isDocumentURL(Expr e) {
exists(Expr base, string propName | e.(PropAccess).accesses(base, propName) |
isDocument(base) and
(
propName = "documentURI" or
propName = "documentURIObject" or
propName = "location" or
propName = "referrer" or
propName = "URL"
)
or
isDomValue(base) and propName = "baseUri"
exists(string propName | e = DOM::documentRef().getAPropertyRead(propName).asExpr() |
propName = "documentURI" or
propName = "documentURIObject" or
propName = "location" or
propName = "referrer" or
propName = "URL"
)
or
e = DOM::domValueRef().getAPropertyRead("baseUri").asExpr()
or
e.accessesGlobal("location")
}
@@ -134,7 +57,7 @@ predicate isDocumentURL(Expr e) {
* `href`, `hash` and `search`.
*/
predicate isSafeLocationProperty(PropAccess pacc) {
exists(Expr loc, string prop | isLocation(loc) and pacc.accesses(loc, prop) |
exists(string prop | pacc = DOM::locationRef().getAPropertyRead(prop).asExpr() |
prop != "href" and prop != "hash" and prop != "search"
)
}

View File

@@ -14,7 +14,7 @@ module PropertyInjection {
node = DataFlow::globalObjectRef()
or
// document.write can be accessed
isDocument(node.asExpr())
node = DOM::documentRef()
or
// 'constructor' property leads to the Function constructor.
node.analyze().getAValue() instanceof AbstractCallable

View File

@@ -75,7 +75,7 @@ module XpathInjection {
class DomXpathSink extends Sink {
DomXpathSink() {
exists(string m | m = "evaluate" or m = "createExpression" |
this = document().getAMethodCall(m).getArgument(0)
this = DOM::documentRef().getAMethodCall(m).getArgument(0)
)
}
}