mirror of
https://github.com/github/codeql.git
synced 2025-12-17 01:03:14 +01:00
JS: Port example queries
This commit is contained in:
@@ -9,42 +9,42 @@
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import DataFlow
|
||||
import DataFlow::PathGraph
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration that tracks user-controlled values into a 'userId' property sent to a backend service.
|
||||
*/
|
||||
class IdorTaint extends TaintTracking::Configuration {
|
||||
IdorTaint() { this = "IdorTaint" }
|
||||
module IdorTaintConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node node) { node instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSource(Node node) { node instanceof RemoteFlowSource }
|
||||
predicate isSink(DataFlow::Node node) { exists(ClientRequest req | node = req.getADataNode()) }
|
||||
|
||||
override predicate isSink(Node node) { exists(ClientRequest req | node = req.getADataNode()) }
|
||||
|
||||
override predicate isAdditionalTaintStep(Node pred, Node succ) {
|
||||
predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
// Step from x -> { userId: x }
|
||||
succ.(SourceNode).getAPropertyWrite("userId").getRhs() = pred
|
||||
succ.(DataFlow::SourceNode).getAPropertyWrite("userId").getRhs() = pred
|
||||
}
|
||||
|
||||
override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode node) {
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
// After a check like `if (userId === session.user.id)`, the userId is considered safe.
|
||||
node instanceof EqualityGuard
|
||||
node = DataFlow::MakeBarrierGuard<EqualityGuard>::getABarrierNode()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A sanitizer for values that have successfully been compared to another value.
|
||||
*/
|
||||
class EqualityGuard extends TaintTracking::SanitizerGuardNode, ValueNode {
|
||||
class EqualityGuard extends DataFlow::ValueNode {
|
||||
override EqualityTest astNode;
|
||||
|
||||
override predicate sanitizes(boolean outcome, Expr e) {
|
||||
predicate blocksExpr(boolean outcome, Expr e) {
|
||||
e = astNode.getAnOperand() and
|
||||
outcome = astNode.getPolarity()
|
||||
}
|
||||
}
|
||||
|
||||
from IdorTaint cfg, PathNode source, PathNode sink
|
||||
where cfg.hasFlowPath(source, sink)
|
||||
module IdorTaintFlow = TaintTracking::Global<IdorTaintConfig>;
|
||||
|
||||
import IdorTaintFlow::PathGraph
|
||||
|
||||
from IdorTaintFlow::PathNode source, IdorTaintFlow::PathNode sink
|
||||
where IdorTaintFlow::flowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "Unauthenticated user ID from $@.", source.getNode(), "here"
|
||||
|
||||
@@ -9,23 +9,25 @@
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import DataFlow
|
||||
import DataFlow::PathGraph
|
||||
|
||||
class DecodingAfterSanitization extends TaintTracking::Configuration {
|
||||
DecodingAfterSanitization() { this = "DecodingAfterSanitization" }
|
||||
module DecodingAfterSanitizationConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node node) {
|
||||
node.(DataFlow::CallNode).getCalleeName() = "escapeHtml"
|
||||
}
|
||||
|
||||
override predicate isSource(Node node) { node.(CallNode).getCalleeName() = "escapeHtml" }
|
||||
|
||||
override predicate isSink(Node node) {
|
||||
exists(CallNode call |
|
||||
predicate isSink(DataFlow::Node node) {
|
||||
exists(DataFlow::CallNode call |
|
||||
call.getCalleeName().matches("decodeURI%") and
|
||||
node = call.getArgument(0)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from DecodingAfterSanitization cfg, PathNode source, PathNode sink
|
||||
where cfg.hasFlowPath(source, sink)
|
||||
module DecodingAfterSanitizationFlow = TaintTracking::Global<DecodingAfterSanitizationConfig>;
|
||||
|
||||
import DecodingAfterSanitizationFlow::PathGraph
|
||||
|
||||
from DecodingAfterSanitizationFlow::PathNode source, DecodingAfterSanitizationFlow::PathNode sink
|
||||
where DecodingAfterSanitizationFlow::flowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "URI decoding invalidates the HTML sanitization performed $@.",
|
||||
source.getNode(), "here"
|
||||
|
||||
@@ -9,16 +9,14 @@
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import DataFlow
|
||||
import DataFlow::PathGraph
|
||||
|
||||
/**
|
||||
* A call to a function that may introduce HTML meta-characters by
|
||||
* replacing `%3C` or `\u003C` with `<`.
|
||||
*/
|
||||
class DecodingCall extends CallNode {
|
||||
class DecodingCall extends DataFlow::CallNode {
|
||||
string kind;
|
||||
Node input;
|
||||
DataFlow::Node input;
|
||||
|
||||
DecodingCall() {
|
||||
this.getCalleeName().matches("decodeURI%") and
|
||||
@@ -33,20 +31,24 @@ class DecodingCall extends CallNode {
|
||||
string getKind() { result = kind }
|
||||
|
||||
/** Gets the input being decoded. */
|
||||
Node getInput() { result = input }
|
||||
DataFlow::Node getInput() { result = input }
|
||||
}
|
||||
|
||||
class DecodingAfterSanitization extends TaintTracking::Configuration {
|
||||
DecodingAfterSanitization() { this = "DecodingAfterSanitization" }
|
||||
module DecodingAfterSanitizationConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node node) { node instanceof HtmlSanitizerCall }
|
||||
|
||||
override predicate isSource(Node node) { node instanceof HtmlSanitizerCall }
|
||||
|
||||
override predicate isSink(Node node) { node = any(DecodingCall c).getInput() }
|
||||
predicate isSink(DataFlow::Node node) { node = any(DecodingCall c).getInput() }
|
||||
}
|
||||
|
||||
from DecodingAfterSanitization cfg, PathNode source, PathNode sink, DecodingCall decoder
|
||||
module DecodingAfterSanitizationFlow = TaintTracking::Global<DecodingAfterSanitizationConfig>;
|
||||
|
||||
import DecodingAfterSanitizationFlow::PathGraph
|
||||
|
||||
from
|
||||
DecodingAfterSanitizationFlow::PathNode source, DecodingAfterSanitizationFlow::PathNode sink,
|
||||
DecodingCall decoder
|
||||
where
|
||||
cfg.hasFlowPath(source, sink) and
|
||||
DecodingAfterSanitizationFlow::flowPath(source, sink) and
|
||||
decoder.getInput() = sink.getNode()
|
||||
select sink.getNode(), source, sink, decoder.getKind() + " invalidates $@.", source.getNode(),
|
||||
"this HTML sanitization"
|
||||
|
||||
@@ -8,16 +8,17 @@
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import DataFlow
|
||||
|
||||
class EvalTaint extends TaintTracking::Configuration {
|
||||
EvalTaint() { this = "EvalTaint" }
|
||||
module EvalTaintConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node node) { node instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSource(Node node) { node instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(Node node) { node = globalVarRef("eval").getACall().getArgument(0) }
|
||||
predicate isSink(DataFlow::Node node) {
|
||||
node = DataFlow::globalVarRef("eval").getACall().getArgument(0)
|
||||
}
|
||||
}
|
||||
|
||||
from EvalTaint cfg, Node source, Node sink
|
||||
where cfg.hasFlow(source, sink)
|
||||
module EvalTaintFlow = TaintTracking::Global<EvalTaintConfig>;
|
||||
|
||||
from DataFlow::Node source, DataFlow::Node sink
|
||||
where EvalTaintFlow::flow(source, sink)
|
||||
select sink, "Eval with user-controlled input from $@.", source, "here"
|
||||
|
||||
@@ -9,18 +9,20 @@
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import DataFlow
|
||||
import DataFlow::PathGraph
|
||||
|
||||
class EvalTaint extends TaintTracking::Configuration {
|
||||
EvalTaint() { this = "EvalTaint" }
|
||||
module EvalTaintConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node node) { node instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSource(Node node) { node instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(Node node) { node = globalVarRef("eval").getACall().getArgument(0) }
|
||||
predicate isSink(DataFlow::Node node) {
|
||||
node = DataFlow::globalVarRef("eval").getACall().getArgument(0)
|
||||
}
|
||||
}
|
||||
|
||||
from EvalTaint cfg, PathNode source, PathNode sink
|
||||
where cfg.hasFlowPath(source, sink)
|
||||
module EvalTaintFlow = TaintTracking::Global<EvalTaintConfig>;
|
||||
|
||||
import EvalTaintFlow::PathGraph
|
||||
|
||||
from EvalTaintFlow::PathNode source, EvalTaintFlow::PathNode sink
|
||||
where EvalTaintFlow::flowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "Eval with user-controlled input from $@.", source.getNode(),
|
||||
"here"
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import DataFlow
|
||||
import DataFlow::PathGraph
|
||||
|
||||
/**
|
||||
* A dataflow configuration that tracks authentication tokens ("authKey")
|
||||
@@ -26,33 +24,37 @@ import DataFlow::PathGraph
|
||||
* }), '*');
|
||||
* ```
|
||||
*/
|
||||
class AuthKeyTracking extends DataFlow::Configuration {
|
||||
AuthKeyTracking() { this = "AuthKeyTracking" }
|
||||
module AuthKeyTrackingConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node node) {
|
||||
node.(DataFlow::PropRead).getPropertyName() = "authKey"
|
||||
}
|
||||
|
||||
override predicate isSource(Node node) { node.(PropRead).getPropertyName() = "authKey" }
|
||||
|
||||
override predicate isSink(Node node) {
|
||||
exists(MethodCallNode call |
|
||||
predicate isSink(DataFlow::Node node) {
|
||||
exists(DataFlow::MethodCallNode call |
|
||||
call.getMethodName() = "postMessage" and
|
||||
call.getArgument(1).getStringValue() = "*" and // no restriction on target origin
|
||||
call.getArgument(0) = node
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isAdditionalFlowStep(Node pred, Node succ) {
|
||||
predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
// Step into objects: x -> { f: x }
|
||||
succ.(SourceNode).getAPropertyWrite().getRhs() = pred
|
||||
succ.(DataFlow::SourceNode).getAPropertyWrite().getRhs() = pred
|
||||
or
|
||||
// Step through JSON serialization: x -> JSON.stringify(x)
|
||||
// Note: TaintTracking::Configuration includes this step by default, but not DataFlow::Configuration
|
||||
exists(CallNode call |
|
||||
call = globalVarRef("JSON").getAMethodCall("stringify") and
|
||||
exists(DataFlow::CallNode call |
|
||||
call = DataFlow::globalVarRef("JSON").getAMethodCall("stringify") and
|
||||
pred = call.getArgument(0) and
|
||||
succ = call
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from AuthKeyTracking cfg, PathNode source, PathNode sink
|
||||
where cfg.hasFlowPath(source, sink)
|
||||
module AuthKeyTracking = DataFlow::Global<AuthKeyTrackingConfig>;
|
||||
|
||||
import AuthKeyTracking::PathGraph
|
||||
|
||||
from AuthKeyTracking::PathNode source, AuthKeyTracking::PathNode sink
|
||||
where AuthKeyTracking::flowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "Message leaks the authKey from $@.", source.getNode(), "here"
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
import javascript
|
||||
import semmle.javascript.security.dataflow.StoredXssQuery
|
||||
import DataFlow::PathGraph
|
||||
import StoredXssFlow::PathGraph
|
||||
|
||||
/**
|
||||
* The data returned from a MySQL query, such as the `data` parameter in this example:
|
||||
@@ -31,6 +31,6 @@ class MysqlSource extends Source {
|
||||
}
|
||||
}
|
||||
|
||||
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where cfg.hasFlowPath(source, sink)
|
||||
from StoredXssFlow::PathNode source, StoredXssFlow::PathNode sink
|
||||
where StoredXssFlow::flowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "Stored XSS from $@.", source.getNode(), "database value."
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
import javascript
|
||||
import semmle.javascript.security.dataflow.StoredXssQuery
|
||||
import DataFlow::PathGraph
|
||||
import StoredXssFlow::PathGraph
|
||||
|
||||
/**
|
||||
* Gets an instance of `mysql.createConnection()`, tracked globally.
|
||||
@@ -45,6 +45,6 @@ class MysqlSource extends Source {
|
||||
MysqlSource() { this = mysqlConnection().getAMethodCall("query").getCallback(1).getParameter(1) }
|
||||
}
|
||||
|
||||
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where cfg.hasFlowPath(source, sink)
|
||||
from StoredXssFlow::PathNode source, StoredXssFlow::PathNode sink
|
||||
where StoredXssFlow::flowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "Stored XSS from $@.", source.getNode(), "database value."
|
||||
|
||||
@@ -8,8 +8,6 @@
|
||||
*/
|
||||
|
||||
import javascript
|
||||
import DataFlow
|
||||
import DataFlow::PathGraph
|
||||
|
||||
/**
|
||||
* Gets the name of an unescaped placeholder in a lodash template.
|
||||
@@ -21,13 +19,11 @@ string getAPlaceholderInString(string s) {
|
||||
result = s.regexpCapture(".*<%=\\s*([a-zA-Z0-9_]+)\\s*%>.*", 1)
|
||||
}
|
||||
|
||||
class TemplateInjection extends TaintTracking::Configuration {
|
||||
TemplateInjection() { this = "TemplateInjection" }
|
||||
module TemplateInjectionConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node node) { node instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSource(Node node) { node instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(Node node) {
|
||||
exists(CallNode call, string placeholder |
|
||||
predicate isSink(DataFlow::Node node) {
|
||||
exists(DataFlow::CallNode call, string placeholder |
|
||||
call = LodashUnderscore::member("template").getACall() and
|
||||
placeholder = getAPlaceholderInString(call.getArgument(0).getStringValue()) and
|
||||
node = call.getOptionArgument(1, placeholder)
|
||||
@@ -35,7 +31,11 @@ class TemplateInjection extends TaintTracking::Configuration {
|
||||
}
|
||||
}
|
||||
|
||||
from TemplateInjection cfg, PathNode source, PathNode sink
|
||||
where cfg.hasFlowPath(source, sink)
|
||||
module TemplateInjectionFlow = TaintTracking::Global<TemplateInjectionConfig>;
|
||||
|
||||
import TemplateInjectionFlow::PathGraph
|
||||
|
||||
from TemplateInjectionFlow::PathNode source, TemplateInjectionFlow::PathNode sink
|
||||
where TemplateInjectionFlow::flowPath(source, sink)
|
||||
select sink.getNode(), source, sink,
|
||||
"User-controlled value from $@ occurs unescaped in a lodash template.", source.getNode(), "here."
|
||||
|
||||
Reference in New Issue
Block a user