mirror of
https://github.com/github/codeql.git
synced 2026-05-03 12:45:27 +02:00
add js/unsafe-code-construction query
This commit is contained in:
22
javascript/ql/src/Security/CWE-094/UnsafeCodeConstruction.ql
Normal file
22
javascript/ql/src/Security/CWE-094/UnsafeCodeConstruction.ql
Normal 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"
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user