mirror of
https://github.com/github/codeql.git
synced 2026-04-28 10:15:14 +02:00
JS: Refactor DOM libs to use DataFlow more
This commit is contained in:
@@ -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()) }
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user