mirror of
https://github.com/github/codeql.git
synced 2026-04-29 18:55:14 +02:00
JS: Add schema validation as TaintedObject sanitizer
This commit is contained in:
@@ -36,6 +36,7 @@ import semmle.javascript.InclusionTests
|
||||
import semmle.javascript.JSDoc
|
||||
import semmle.javascript.JSON
|
||||
import semmle.javascript.JsonParsers
|
||||
import semmle.javascript.JsonSchema
|
||||
import semmle.javascript.JsonStringifiers
|
||||
import semmle.javascript.JSX
|
||||
import semmle.javascript.Lines
|
||||
|
||||
73
javascript/ql/src/semmle/javascript/JsonSchema.qll
Normal file
73
javascript/ql/src/semmle/javascript/JsonSchema.qll
Normal file
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* Provides classes and predicates for working with JSON schema libraries.
|
||||
*/
|
||||
|
||||
import javascript
|
||||
|
||||
/**
|
||||
* Provides classes and predicates for working with JSON schema libraries.
|
||||
*/
|
||||
module JsonSchema {
|
||||
/** A call that validates an input against a JSON schema. */
|
||||
abstract class ValidationCall extends DataFlow::CallNode {
|
||||
/** Gets the data flow node whose value is being validated. */
|
||||
abstract DataFlow::Node getInput();
|
||||
|
||||
/** Gets the return value that indicates successful validation. */
|
||||
boolean getPolarity() { result = true }
|
||||
}
|
||||
|
||||
/** Provides a model of the `ajv` library. */
|
||||
module Ajv {
|
||||
/** A method on `Ajv` that returns `this`. */
|
||||
private string chainedMethod() {
|
||||
result =
|
||||
["addSchema", "addMetaSchema", "removeSchema", "addFormat", "addKeyword", "removeKeyword"]
|
||||
}
|
||||
|
||||
/** An instance of `ajv`. */
|
||||
class Instance extends API::InvokeNode {
|
||||
Instance() { this = API::moduleImport("ajv").getAnInstantiation() }
|
||||
|
||||
/** Gets the data flow node holding the options passed to this `Ajv` instance. */
|
||||
DataFlow::Node getOptionsArg() { result = getArgument(0) }
|
||||
|
||||
/** Gets an API node that refers to this object. */
|
||||
API::Node ref() {
|
||||
result = getReturn()
|
||||
or
|
||||
result = ref().getMember(chainedMethod()).getReturn()
|
||||
}
|
||||
}
|
||||
|
||||
/** A call to the `validate` method of `ajv`. */
|
||||
class AjvValidationCall extends ValidationCall {
|
||||
Instance instance;
|
||||
int argIndex;
|
||||
|
||||
AjvValidationCall() {
|
||||
this = instance.ref().getMember("validate").getACall() and argIndex = 1
|
||||
or
|
||||
this = instance.ref().getMember(["compile", "getSchema"]).getReturn().getACall() and
|
||||
argIndex = 0
|
||||
or
|
||||
this = instance.ref().getMember("compileAsync").getPromised().getACall() and argIndex = 0
|
||||
}
|
||||
|
||||
override DataFlow::Node getInput() { result = getArgument(argIndex) }
|
||||
|
||||
/** Gets the argument holding additional options to the call. */
|
||||
DataFlow::Node getOwnOptionsArg() { result = getArgument(argIndex + 1) }
|
||||
|
||||
/** Gets a data flow passed as the extra options to this validation call or to the underlying `Ajv` instance. */
|
||||
DataFlow::Node getAnOptionsArg() {
|
||||
result = getOwnOptionsArg()
|
||||
or
|
||||
result = instance.getOptionsArg()
|
||||
}
|
||||
|
||||
/** Gets the ajv instance doing the validation. */
|
||||
Instance getAjvInstance() { result = instance }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -120,4 +120,19 @@ module TaintedObject {
|
||||
label = label()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A sanitizer guard that validates an input against a JSON schema.
|
||||
*/
|
||||
private class JsonSchemaValidationGuard extends SanitizerGuard {
|
||||
JsonSchema::ValidationCall call;
|
||||
|
||||
JsonSchemaValidationGuard() { this = call }
|
||||
|
||||
override predicate sanitizes(boolean outcome, Expr e, FlowLabel label) {
|
||||
outcome = call.getPolarity() and
|
||||
e = call.getInput().asExpr() and
|
||||
label = label()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user