add js/unsafe-code-construction query

This commit is contained in:
Erik Krogh Kristensen
2021-05-05 19:34:28 +02:00
parent 955ad8c458
commit 198a464346
3 changed files with 123 additions and 0 deletions

View File

@@ -0,0 +1,22 @@
/**
* @name Unsafe code constructed from libary input
* @description Using externally controlled strings to construct code may allow a malicious
* user to execute arbitrary code.
* @kind path-problem
* @problem.severity warning
* @precision high
* @id js/unsafe-code-construction
* @tags security
* external/cwe/cwe-094
* external/cwe/cwe-079
* external/cwe/cwe-116
*/
import javascript
import DataFlow::PathGraph
import semmle.javascript.security.dataflow.UnsafeCodeConstruction::UnsafeCodeConstruction
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "$@ flows to here and is later $@.", source.getNode(),
"Library input", sink.getNode().(Sink).getCodeSink(), "interpreted as code"

View File

@@ -0,0 +1,48 @@
/**
* Provides a taint-tracking configuration for reasoning about code
* constructed from libary input vulnerabilities.
*
* Note, for performance reasons: only import this file if
* `UnsafeCodeConstruction::Configuration` is needed, otherwise
* `UnsafeCodeConstructionCustomizations` should be imported instead.
*/
import javascript
module UnsafeCodeConstruction {
private import semmle.javascript.security.dataflow.CodeInjectionCustomizations::CodeInjection as CodeInjection
import UnsafeCodeConstructionCustomizations::UnsafeCodeConstruction
/**
* A taint-tracking configuration for reasoning about unsafe code constructed from library input.
*/
class Configuration extends TaintTracking::Configuration {
Configuration() { this = "UnsafeCodeConstruction" }
override predicate isSource(DataFlow::Node source) { source instanceof Source }
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
override predicate isSanitizer(DataFlow::Node node) {
super.isSanitizer(node) or
node instanceof CodeInjection::Sanitizer
}
override predicate isAdditionalTaintStep(DataFlow::Node src, DataFlow::Node trg) {
// HTML sanitizers are insufficient protection against code injection
src = trg.(HtmlSanitizerCall).getInput()
or
DataFlow::localFieldStep(src, trg)
}
// override to require that there is a path without unmatched return steps
override predicate hasFlowPath(DataFlow::SourcePathNode source, DataFlow::SinkPathNode sink) {
super.hasFlowPath(source, sink) and
exists(DataFlow::MidPathNode mid |
source.getASuccessor*() = mid and
sink = mid.getASuccessor() and
mid.getPathSummary().hasReturn() = false
)
}
}
}

View File

@@ -0,0 +1,53 @@
/**
* Provides default sources, sinks and sanitizers for reasoning about code
* constructed from libary input vulnerabilities, as well as extension points for
* adding your own.
*/
import javascript
module UnsafeCodeConstruction {
private import semmle.javascript.security.dataflow.CodeInjectionCustomizations::CodeInjection as CodeInjection
private import semmle.javascript.PackageExports as Exports
/**
* A source for code constructed from library input vulnerabilities.
*/
abstract class Source extends DataFlow::Node { }
/**
* A parameter of an exported function, seen as a source.
*/
class ExternalInputSource extends Source, DataFlow::ParameterNode {
ExternalInputSource() { this = Exports::getALibraryInputParameter() }
}
/**
* A sink for unsafe code constructed from library input vulnerabilities.
*/
abstract class Sink extends DataFlow::Node {
abstract DataFlow::Node getCodeSink();
}
/**
* Gets a node that is later executed as code in `codeSink`.
*/
private DataFlow::Node isExecutedAsCode(DataFlow::TypeBackTracker t, CodeInjection::Sink codeSink) {
t.start() and result = codeSink
or
exists(DataFlow::TypeBackTracker t2 | t2 = t.smallstep(result, isExecutedAsCode(t, codeSink)))
}
/**
* A string concatenation leaf that is later executed as code.
*/
class StringConcatExecutedAsCode extends Sink, StringOps::ConcatenationLeaf {
CodeInjection::Sink codeSink;
StringConcatExecutedAsCode() {
this.getRoot() = isExecutedAsCode(DataFlow::TypeBackTracker::end(), codeSink)
}
override DataFlow::Node getCodeSink() { result = codeSink }
}
}