mirror of
https://github.com/github/codeql.git
synced 2025-12-18 01:33:15 +01:00
JavaScript: Add model of JSON parsers
This commit is contained in:
@@ -23,6 +23,7 @@ import semmle.javascript.Functions
|
||||
import semmle.javascript.HTML
|
||||
import semmle.javascript.JSDoc
|
||||
import semmle.javascript.JSON
|
||||
import semmle.javascript.JsonParsers
|
||||
import semmle.javascript.JSX
|
||||
import semmle.javascript.Lines
|
||||
import semmle.javascript.Locations
|
||||
|
||||
80
javascript/ql/src/semmle/javascript/JsonParsers.qll
Normal file
80
javascript/ql/src/semmle/javascript/JsonParsers.qll
Normal file
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* Provides classes for working with JSON parsers.
|
||||
*/
|
||||
import javascript
|
||||
|
||||
/**
|
||||
* A call to a JSON parser such as `JSON.parse`.
|
||||
*/
|
||||
abstract class JsonParserCall extends DataFlow::CallNode {
|
||||
/**
|
||||
* Gets the data flow node holding the input string to be parsed.
|
||||
*/
|
||||
abstract DataFlow::Node getInput();
|
||||
|
||||
/**
|
||||
* Gets the data flow node holding the resulting JSON object.
|
||||
*/
|
||||
abstract DataFlow::SourceNode getOutput();
|
||||
}
|
||||
|
||||
/**
|
||||
* A JSON parser that returns its result.
|
||||
*/
|
||||
private class PlainJsonParserCall extends JsonParserCall {
|
||||
PlainJsonParserCall() {
|
||||
exists (DataFlow::SourceNode callee | this = callee.getACall() |
|
||||
callee = DataFlow::globalVarRef("JSON").getAPropertyRead("parse") or
|
||||
callee = DataFlow::moduleImport("parse-json") or
|
||||
callee = DataFlow::moduleImport("json-parse-better-errors") or
|
||||
callee = DataFlow::moduleImport("json-safe-parse"))
|
||||
}
|
||||
|
||||
override DataFlow::Node getInput() {
|
||||
result = getArgument(0)
|
||||
}
|
||||
|
||||
override DataFlow::SourceNode getOutput() {
|
||||
result = this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A JSON parser that returns its result wrapped in a another object.
|
||||
*/
|
||||
private class JsonParserCallWithWrapper extends JsonParserCall {
|
||||
string outputPropName;
|
||||
|
||||
JsonParserCallWithWrapper() {
|
||||
exists (DataFlow::SourceNode callee | this = callee.getACall() |
|
||||
callee = DataFlow::moduleImport("safe-json-parse/tuple") and outputPropName = "1" or
|
||||
callee = DataFlow::moduleImport("safe-json-parse/result") and outputPropName = "v" or
|
||||
callee = DataFlow::moduleImport("fast-json-parse") and outputPropName = "value" or
|
||||
callee = DataFlow::moduleImport("json-parse-safe") and outputPropName = "value")
|
||||
}
|
||||
|
||||
override DataFlow::Node getInput() {
|
||||
result = getArgument(0)
|
||||
}
|
||||
|
||||
override DataFlow::SourceNode getOutput() {
|
||||
result = this.getAPropertyRead(outputPropName)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A JSON parser that returns its result through a callback argument.
|
||||
*/
|
||||
private class JsonParserCallWithCallback extends JsonParserCall {
|
||||
JsonParserCallWithCallback() {
|
||||
this = DataFlow::moduleImport("safe-json-parse/callback").getACall()
|
||||
}
|
||||
|
||||
override DataFlow::Node getInput() {
|
||||
result = getArgument(0)
|
||||
}
|
||||
|
||||
override DataFlow::SourceNode getOutput() {
|
||||
result = getCallback(1).getParameter(1)
|
||||
}
|
||||
}
|
||||
@@ -44,8 +44,11 @@ class DirectEval extends CallExpr {
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED. Use `JsonParserCall` and the data flow API instead.
|
||||
*
|
||||
* A call to `JSON.parse`.
|
||||
*/
|
||||
deprecated
|
||||
class JsonParseCall extends MethodCallExpr {
|
||||
JsonParseCall() {
|
||||
this = DataFlow::globalVarRef("JSON").getAMemberCall("parse").asExpr()
|
||||
|
||||
@@ -400,14 +400,11 @@ module TaintTracking {
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint propagating data flow edge arising from JSON parsing or unparsing.
|
||||
* A taint propagating data flow edge arising from JSON unparsing.
|
||||
*/
|
||||
private class JsonManipulationTaintStep extends AdditionalTaintStep, DataFlow::MethodCallNode {
|
||||
JsonManipulationTaintStep() {
|
||||
exists (string methodName |
|
||||
methodName = "parse" or methodName = "stringify" |
|
||||
this = DataFlow::globalVarRef("JSON").getAMemberCall(methodName)
|
||||
)
|
||||
private class JsonStringifyTaintStep extends AdditionalTaintStep, DataFlow::MethodCallNode {
|
||||
JsonStringifyTaintStep() {
|
||||
this = DataFlow::globalVarRef("JSON").getAMemberCall("stringify")
|
||||
}
|
||||
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
@@ -415,6 +412,20 @@ module TaintTracking {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint propagating data flow edge arising from JSON parsing.
|
||||
*/
|
||||
private class JsonParserTaintStep extends AdditionalTaintStep, DataFlow::CallNode {
|
||||
JsonParserCall call;
|
||||
|
||||
JsonParserTaintStep() { this = call }
|
||||
|
||||
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
|
||||
pred = call.getInput() and
|
||||
succ = call.getOutput()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `params` is a `URLSearchParams` object providing access to
|
||||
* the parameters encoded in `input`.
|
||||
|
||||
@@ -55,36 +55,6 @@ module NosqlInjection {
|
||||
RemoteFlowSourceAsSource() { this instanceof RemoteFlowSource }
|
||||
}
|
||||
|
||||
/**
|
||||
* A taint tracking configuration for tracking user input that flows
|
||||
* into a call to `JSON.parse`.
|
||||
*/
|
||||
private class RemoteJsonTrackingConfig extends TaintTracking::Configuration {
|
||||
RemoteJsonTrackingConfig() {
|
||||
this = "RemoteJsonTrackingConfig"
|
||||
}
|
||||
|
||||
override predicate isSource(DataFlow::Node nd) {
|
||||
nd instanceof RemoteFlowSource
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node nd) {
|
||||
nd.asExpr() = any(JsonParseCall c).getArgument(0)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `JSON.parse` where the argument is user-provided.
|
||||
*/
|
||||
class RemoteJson extends Source, DataFlow::ValueNode {
|
||||
RemoteJson() {
|
||||
exists (DataFlow::Node parsedArg |
|
||||
parsedArg.asExpr() = astNode.(JsonParseCall).getArgument(0) and
|
||||
any(RemoteJsonTrackingConfig cfg).hasFlow(_, parsedArg)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** An expression interpreted as a NoSQL query, viewed as a sink. */
|
||||
class NosqlQuerySink extends Sink, DataFlow::ValueNode {
|
||||
override NoSQL::Query astNode;
|
||||
|
||||
Reference in New Issue
Block a user