Skip to content

Commit 198a464

Browse files
committed
add js/unsafe-code-construction query
1 parent 955ad8c commit 198a464

File tree

3 files changed

+123
-0
lines changed

3 files changed

+123
-0
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/**
2+
* @name Unsafe code constructed from libary input
3+
* @description Using externally controlled strings to construct code may allow a malicious
4+
* user to execute arbitrary code.
5+
* @kind path-problem
6+
* @problem.severity warning
7+
* @precision high
8+
* @id js/unsafe-code-construction
9+
* @tags security
10+
* external/cwe/cwe-094
11+
* external/cwe/cwe-079
12+
* external/cwe/cwe-116
13+
*/
14+
15+
import javascript
16+
import DataFlow::PathGraph
17+
import semmle.javascript.security.dataflow.UnsafeCodeConstruction::UnsafeCodeConstruction
18+
19+
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
20+
where cfg.hasFlowPath(source, sink)
21+
select sink.getNode(), source, sink, "$@ flows to here and is later $@.", source.getNode(),
22+
"Library input", sink.getNode().(Sink).getCodeSink(), "interpreted as code"
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/**
2+
* Provides a taint-tracking configuration for reasoning about code
3+
* constructed from libary input vulnerabilities.
4+
*
5+
* Note, for performance reasons: only import this file if
6+
* `UnsafeCodeConstruction::Configuration` is needed, otherwise
7+
* `UnsafeCodeConstructionCustomizations` should be imported instead.
8+
*/
9+
10+
import javascript
11+
12+
module UnsafeCodeConstruction {
13+
private import semmle.javascript.security.dataflow.CodeInjectionCustomizations::CodeInjection as CodeInjection
14+
import UnsafeCodeConstructionCustomizations::UnsafeCodeConstruction
15+
16+
/**
17+
* A taint-tracking configuration for reasoning about unsafe code constructed from library input.
18+
*/
19+
class Configuration extends TaintTracking::Configuration {
20+
Configuration() { this = "UnsafeCodeConstruction" }
21+
22+
override predicate isSource(DataFlow::Node source) { source instanceof Source }
23+
24+
override predicate isSink(DataFlow::Node sink) { sink instanceof Sink }
25+
26+
override predicate isSanitizer(DataFlow::Node node) {
27+
super.isSanitizer(node) or
28+
node instanceof CodeInjection::Sanitizer
29+
}
30+
31+
override predicate isAdditionalTaintStep(DataFlow::Node src, DataFlow::Node trg) {
32+
// HTML sanitizers are insufficient protection against code injection
33+
src = trg.(HtmlSanitizerCall).getInput()
34+
or
35+
DataFlow::localFieldStep(src, trg)
36+
}
37+
38+
// override to require that there is a path without unmatched return steps
39+
override predicate hasFlowPath(DataFlow::SourcePathNode source, DataFlow::SinkPathNode sink) {
40+
super.hasFlowPath(source, sink) and
41+
exists(DataFlow::MidPathNode mid |
42+
source.getASuccessor*() = mid and
43+
sink = mid.getASuccessor() and
44+
mid.getPathSummary().hasReturn() = false
45+
)
46+
}
47+
}
48+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/**
2+
* Provides default sources, sinks and sanitizers for reasoning about code
3+
* constructed from libary input vulnerabilities, as well as extension points for
4+
* adding your own.
5+
*/
6+
7+
import javascript
8+
9+
module UnsafeCodeConstruction {
10+
private import semmle.javascript.security.dataflow.CodeInjectionCustomizations::CodeInjection as CodeInjection
11+
private import semmle.javascript.PackageExports as Exports
12+
13+
/**
14+
* A source for code constructed from library input vulnerabilities.
15+
*/
16+
abstract class Source extends DataFlow::Node { }
17+
18+
/**
19+
* A parameter of an exported function, seen as a source.
20+
*/
21+
class ExternalInputSource extends Source, DataFlow::ParameterNode {
22+
ExternalInputSource() { this = Exports::getALibraryInputParameter() }
23+
}
24+
25+
/**
26+
* A sink for unsafe code constructed from library input vulnerabilities.
27+
*/
28+
abstract class Sink extends DataFlow::Node {
29+
abstract DataFlow::Node getCodeSink();
30+
}
31+
32+
/**
33+
* Gets a node that is later executed as code in `codeSink`.
34+
*/
35+
private DataFlow::Node isExecutedAsCode(DataFlow::TypeBackTracker t, CodeInjection::Sink codeSink) {
36+
t.start() and result = codeSink
37+
or
38+
exists(DataFlow::TypeBackTracker t2 | t2 = t.smallstep(result, isExecutedAsCode(t, codeSink)))
39+
}
40+
41+
/**
42+
* A string concatenation leaf that is later executed as code.
43+
*/
44+
class StringConcatExecutedAsCode extends Sink, StringOps::ConcatenationLeaf {
45+
CodeInjection::Sink codeSink;
46+
47+
StringConcatExecutedAsCode() {
48+
this.getRoot() = isExecutedAsCode(DataFlow::TypeBackTracker::end(), codeSink)
49+
}
50+
51+
override DataFlow::Node getCodeSink() { result = codeSink }
52+
}
53+
}

0 commit comments

Comments
 (0)