add qldoc and customizations module

This commit is contained in:
Erik Krogh Kristensen
2020-06-12 11:26:49 +02:00
parent c375a0c611
commit adabd2daca
3 changed files with 107 additions and 42 deletions

View File

@@ -11,59 +11,30 @@
* external/cwe/cwe-116
*/
// TODO: Proper customizations module, Source class Sink class etc. and qldoc.
import javascript
import semmle.javascript.security.dataflow.ImproperCodeSanitization::ImproperCodeSanitization
import DataFlow::PathGraph
private import semmle.javascript.heuristics.HeuristicSinks
private import semmle.javascript.security.dataflow.CodeInjectionCustomizations
/**
* A taint-tracking configuration for reasoning about improper code sanitization vulnerabilities.
* Gets a type-tracked instance of `RemoteFlowSource` using type-tracker `t`.
*/
class Configuration extends TaintTracking::Configuration {
Configuration() { this = "ImproperCodeSanitization" }
override predicate isSource(DataFlow::Node source) { source = source() }
override predicate isSink(DataFlow::Node sink) { sink = sink() }
override predicate isSanitizer(DataFlow::Node sanitizer) {
sanitizer instanceof StringReplaceCall // any string-replace that happens after the bad-sanitizer, is assumed to be a good sanitizer.
// TODO: Specialize? This regexp sanitizes: /[<>\b\f\n\r\t\0\u2028\u2029]/g
}
}
private DataFlow::CallNode source() {
result instanceof HtmlSanitizerCall
or
result = DataFlow::globalVarRef("JSON").getAMemberCall("stringify")
}
private StringOps::ConcatenationLeaf sink() {
exists(StringOps::ConcatenationRoot root, int i |
root.getOperand(i) = result and
not exists(result.getStringValue())
|
exists(StringOps::ConcatenationLeaf functionLeaf |
functionLeaf = root.getOperand(any(int j | j < i))
|
functionLeaf
.getStringValue()
.regexpMatch([".*function( )?([a-zA-Z0-9]+)?( )?\\(.*", ".*eval\\(.*",
".*new Function\\(.*", "(^|.*[^a-zA-Z0-9])\\(.*\\)( )?=>.*"])
)
)
}
DataFlow::SourceNode remoteFlow(DataFlow::TypeTracker t) {
private DataFlow::SourceNode remoteFlow(DataFlow::TypeTracker t) {
t.start() and
result instanceof RemoteFlowSource
or
exists(DataFlow::TypeTracker t2 | result = remoteFlow(t2).track(t2, t))
}
DataFlow::SourceNode remoteFlow() { result = remoteFlow(DataFlow::TypeTracker::end()) }
/**
* Gets a type-tracked reference to a `RemoteFlowSource`.
*/
private DataFlow::SourceNode remoteFlow() { result = remoteFlow(DataFlow::TypeTracker::end()) }
/**
* Gets a type-back-tracked instance of a code-injection sink using type-tracker `t`.
*/
private DataFlow::Node endsInCodeInjectionSink(DataFlow::TypeBackTracker t) {
t.start() and
(
@@ -76,9 +47,11 @@ private DataFlow::Node endsInCodeInjectionSink(DataFlow::TypeBackTracker t) {
exists(DataFlow::TypeBackTracker t2 | t = t2.smallstep(result, endsInCodeInjectionSink(t2)))
}
DataFlow::Node endsInCodeInjectionSink() {
result = endsInCodeInjectionSink(DataFlow::TypeBackTracker::end()) and
result.getFile().getBaseName() = "bad-code-sanitization.js" // TODO: TMp
/**
* Gets a reference to to a data-flow node that ends in a code-injection sink.
*/
private DataFlow::Node endsInCodeInjectionSink() {
result = endsInCodeInjectionSink(DataFlow::TypeBackTracker::end())
}
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink

View File

@@ -0,0 +1,27 @@
/**
* Provides a taint-tracking configuration for reasoning about improper code
* sanitization.
*
* Note, for performance reasons: only import this file if
* `ImproperCodeSanitization::Configuration` is needed, otherwise
* `ImproperCodeSanitizationCustomizations` should be imported instead.
*/
import javascript
module ImproperCodeSanitization {
import ImproperCodeSanitizationCustomizations::ImproperCodeSanitization
/**
* A taint-tracking configuration for reasoning about improper code sanitization vulnerabilities.
*/
class Configuration extends TaintTracking::Configuration {
Configuration() { this = "ImproperCodeSanitization" }
override predicate isSource(DataFlow::Node source) { source instanceof Source }
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
override predicate isSanitizer(DataFlow::Node sanitizer) { sanitizer instanceof Sanitizer }
}
}

View File

@@ -0,0 +1,65 @@
/**
* Provides default sources, sinks and sanitizers for reasoning about
* improper code sanitization, as well as extension points for
* adding your own.
*/
import javascript
module ImproperCodeSanitization {
/**
* A data flow source for improper code sanitization.
*/
abstract class Source extends DataFlow::Node { }
/**
* A data flow sink for improper code sanitization.
*/
abstract class Sink extends DataFlow::Node { }
/**
* A sanitizer for improper code sanitization.
*/
abstract class Sanitizer extends DataFlow::Node { }
/**
* A call to a HTML sanitizer seen as a source for improper code sanitization
*/
class HtmlSanitizerCallAsSource extends Source {
HtmlSanitizerCallAsSource() { this instanceof HtmlSanitizerCall }
}
/**
* A call to `JSON.stringify()` seen as a source for improper code sanitization
*/
class JSONStringifyAsSource extends Source {
JSONStringifyAsSource() { this = DataFlow::globalVarRef("JSON").getAMemberCall("stringify") }
}
/**
* A leaf in a string-concatenation, where the string-concatenation constructs code that looks like a function.
*/
class FunctionStringConstruction extends Sink, StringOps::ConcatenationLeaf {
FunctionStringConstruction() {
exists(StringOps::ConcatenationRoot root, int i |
root.getOperand(i) = this and
not exists(this.getStringValue())
|
exists(StringOps::ConcatenationLeaf functionLeaf |
functionLeaf = root.getOperand(any(int j | j < i))
|
functionLeaf
.getStringValue()
.regexpMatch([".*function( )?([a-zA-Z0-9]+)?( )?\\(.*", ".*eval\\(.*",
".*new Function\\(.*", "(^|.*[^a-zA-Z0-9])\\(.*\\)( )?=>.*"])
)
)
}
}
/**
* A call to `String.prototype.replace` seen as a sanitizer for improper code sanitization.
* All calls to replace that happens after the initial improper sanitization is seen as a sanitizer.
*/
class StringReplaceCallAsSanitizer extends Sanitizer, StringReplaceCall { }
}