Skip to content

Commit daeb13d

Browse files
authored
Merge pull request github#3779 from asger-semmle/js/metric-queries
Approved by esbena
2 parents 696d19c + d15c98d commit daeb13d

12 files changed

+295
-24
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/**
2+
* Helpers to generating meta metrics, that is, metrics about the CodeQL analysis and extractor.
3+
*/
4+
5+
private import javascript
6+
private import semmle.javascript.dependencies.Dependencies
7+
private import semmle.javascript.dependencies.FrameworkLibraries
8+
private import semmle.javascript.frameworks.Testing
9+
10+
/**
11+
* Gets the root folder of the snapshot.
12+
*
13+
* This is selected as the location for project-wide metrics.
14+
*/
15+
Folder projectRoot() { result.getRelativePath() = "" }
16+
17+
/** A file we ignore because it is a test file or compiled/generated/bundled code. */
18+
class IgnoredFile extends File {
19+
IgnoredFile() {
20+
any(Test t).getFile() = this
21+
or
22+
getRelativePath().regexpMatch("(?i).*/test(case)?s?/.*")
23+
or
24+
getBaseName().regexpMatch("(?i)(.*[._\\-]|^)(min|bundle|concat|spec|tests?)\\.[a-zA-Z]+")
25+
or
26+
exists(TopLevel tl | tl.getFile() = this |
27+
tl.isExterns()
28+
or
29+
tl instanceof FrameworkLibraryInstance
30+
)
31+
}
32+
}

javascript/ql/src/meta/analysis-quality/CallGraphQuality.qll

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,30 +9,7 @@ private import semmle.javascript.dependencies.Dependencies
99
private import semmle.javascript.dependencies.FrameworkLibraries
1010
private import semmle.javascript.frameworks.Testing
1111
private import DataFlow
12-
13-
/**
14-
* Gets the root folder of the snapshot.
15-
*
16-
* This is selected as the location for project-wide metrics.
17-
*/
18-
Folder projectRoot() { result.getRelativePath() = "" }
19-
20-
/** A file we ignore because it is a test file or compiled/generated/bundled code. */
21-
class IgnoredFile extends File {
22-
IgnoredFile() {
23-
any(Test t).getFile() = this
24-
or
25-
getRelativePath().regexpMatch("(?i).*/test(case)?s?/.*")
26-
or
27-
getBaseName().regexpMatch("(?i)(.*[._\\-]|^)(min|bundle|concat|spec|tests?)\\.[a-zA-Z]+")
28-
or
29-
exists(TopLevel tl | tl.getFile() = this |
30-
tl.isExterns()
31-
or
32-
tl instanceof FrameworkLibraryInstance
33-
)
34-
}
35-
}
12+
import meta.MetaMetrics
3613

3714
/** An call site that is relevant for analysis quality. */
3815
class RelevantInvoke extends InvokeNode {
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/**
2+
* @name Resolvable imports
3+
* @description The number of imports that could be resolved to its target.
4+
* @kind metric
5+
* @metricType project
6+
* @metricAggregate sum
7+
* @tags meta
8+
* @id js/meta/resolvable-imports
9+
*/
10+
11+
import javascript
12+
import CallGraphQuality
13+
14+
Import relevantImport() { not result.getFile() instanceof IgnoredFile }
15+
16+
select projectRoot(),
17+
count(Import imprt | imprt = relevantImport() and exists(imprt.getImportedModule()))
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/**
2+
* @name Route handlers
3+
* @description The number of HTTP route handler functions found.
4+
* @kind metric
5+
* @metricType project
6+
* @metricAggregate sum
7+
* @tags meta
8+
* @id js/meta/route-handlers
9+
*/
10+
11+
import javascript
12+
import CallGraphQuality
13+
14+
HTTP::RouteHandler relevantRouteHandler() { not result.getFile() instanceof IgnoredFile }
15+
16+
select projectRoot(), count(relevantRouteHandler())
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/**
2+
* @name Sanitizers reachable from source
3+
* @description The number of sanitizers reachable from a recognized taint source.
4+
* @kind metric
5+
* @metricType project
6+
* @metricAggregate sum
7+
* @tags meta
8+
* @id js/meta/sanitizers-reachable-from-source
9+
*/
10+
11+
import javascript
12+
import TaintMetrics
13+
14+
class BasicTaintConfiguration extends TaintTracking::Configuration {
15+
BasicTaintConfiguration() { this = "BasicTaintConfiguration" }
16+
17+
override predicate isSource(DataFlow::Node node) { node = relevantTaintSource() }
18+
19+
override predicate isSink(DataFlow::Node node) { node = relevantSanitizerInput() }
20+
}
21+
22+
select projectRoot(), count(DataFlow::Node node | any(BasicTaintConfiguration cfg).hasFlow(_, node))
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/**
2+
* @name Sinks reachable from sanitizer
3+
* @description The number of sinks reachable from a recognized sanitizer call.
4+
* @kind metric
5+
* @metricType project
6+
* @metricAggregate sum
7+
* @tags meta
8+
* @id js/meta/sinks-reachable-from-sanitizer
9+
*/
10+
11+
import javascript
12+
import TaintMetrics
13+
14+
class BasicTaintConfiguration extends TaintTracking::Configuration {
15+
BasicTaintConfiguration() { this = "BasicTaintConfiguration" }
16+
17+
override predicate isSource(DataFlow::Node node) { node = relevantSanitizerOutput() }
18+
19+
override predicate isSink(DataFlow::Node node) { node = relevantTaintSink() }
20+
}
21+
22+
select projectRoot(), count(DataFlow::Node node | any(BasicTaintConfiguration cfg).hasFlow(_, node))
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/**
2+
* Provides predicates for measuring taint-tracking coverage.
3+
*/
4+
5+
private import javascript
6+
import meta.MetaMetrics
7+
private import semmle.javascript.security.dataflow.ClientSideUrlRedirectCustomizations
8+
private import semmle.javascript.security.dataflow.CodeInjectionCustomizations
9+
private import semmle.javascript.security.dataflow.CommandInjectionCustomizations
10+
private import semmle.javascript.security.dataflow.Xss as Xss
11+
private import semmle.javascript.security.dataflow.NosqlInjectionCustomizations
12+
private import semmle.javascript.security.dataflow.PrototypePollutionCustomizations
13+
private import semmle.javascript.security.dataflow.RegExpInjectionCustomizations
14+
private import semmle.javascript.security.dataflow.RequestForgeryCustomizations
15+
private import semmle.javascript.security.dataflow.ServerSideUrlRedirectCustomizations
16+
private import semmle.javascript.security.dataflow.SqlInjectionCustomizations
17+
private import semmle.javascript.security.dataflow.TaintedPathCustomizations
18+
private import semmle.javascript.security.dataflow.UnsafeDeserializationCustomizations
19+
private import semmle.javascript.security.dataflow.XmlBombCustomizations
20+
private import semmle.javascript.security.dataflow.XpathInjectionCustomizations
21+
private import semmle.javascript.security.dataflow.XxeCustomizations
22+
private import semmle.javascript.security.dataflow.ZipSlipCustomizations
23+
24+
/**
25+
* Gets a relevant taint sink.
26+
*
27+
* To ensure this metric isn't dominated by a few queries with a huge number of sinks,
28+
* we only include sinks for queries that have fairly specific sinks and/or have high severity
29+
* relative to the number of sinks.
30+
*
31+
* Examples of excluded queries:
32+
* - UnsafeDynamicMethodAccess: high severity (RCE) but has way too many sinks (every callee).
33+
* - ClearTextLogging: not severe enough relative to number of sinks.
34+
*/
35+
DataFlow::Node relevantTaintSink() {
36+
not result.getFile() instanceof IgnoredFile and
37+
(
38+
result instanceof ClientSideUrlRedirect::Sink or
39+
result instanceof CodeInjection::Sink or
40+
result instanceof CommandInjection::Sink or
41+
result instanceof Xss::Shared::Sink or
42+
result instanceof NosqlInjection::Sink or
43+
result instanceof PrototypePollution::Sink or
44+
result instanceof RegExpInjection::Sink or
45+
result instanceof RequestForgery::Sink or
46+
result instanceof ServerSideUrlRedirect::Sink or
47+
result instanceof SqlInjection::Sink or
48+
result instanceof TaintedPath::Sink or
49+
result instanceof UnsafeDeserialization::Sink or
50+
result instanceof XmlBomb::Sink or
51+
result instanceof XpathInjection::Sink or
52+
result instanceof Xxe::Sink or
53+
result instanceof ZipSlip::Sink
54+
)
55+
}
56+
57+
/**
58+
* Gets a remote flow source or `document.location` source.
59+
*/
60+
DataFlow::Node relevantTaintSource() {
61+
not result.getFile() instanceof IgnoredFile and
62+
(
63+
result instanceof RemoteFlowSource
64+
or
65+
result = DOM::locationSource()
66+
)
67+
}
68+
69+
/**
70+
* Gets the output of a call that shows intent to sanitize a value
71+
* (indicating a likely vulnerability if the sanitizer was removed).
72+
*
73+
* Currently we only recognize HTML sanitizers.
74+
*/
75+
DataFlow::Node relevantSanitizerOutput() {
76+
result = any(HtmlSanitizerCall call) and
77+
not result.getFile() instanceof IgnoredFile
78+
}
79+
80+
/**
81+
* Gets the input to a call that shows intent to sanitize a value
82+
* (indicating a likely vulnerability if the sanitizer was removed).
83+
*
84+
* Currently we only recognize HTML sanitizers.
85+
*/
86+
DataFlow::Node relevantSanitizerInput() {
87+
result = any(HtmlSanitizerCall call).getInput() and
88+
not result.getFile() instanceof IgnoredFile
89+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/**
2+
* @name Taint sinks
3+
* @description The number of high-severity taint sinks.
4+
* @kind metric
5+
* @metricType project
6+
* @metricAggregate sum
7+
* @tags meta
8+
* @id js/meta/taint-sinks
9+
*/
10+
11+
import javascript
12+
import TaintMetrics
13+
14+
select projectRoot(), count(relevantTaintSink())
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/**
2+
* @name Taint sources
3+
* @description The number of remote flow sources and document.location sources
4+
* @kind metric
5+
* @metricType project
6+
* @metricAggregate sum
7+
* @tags meta
8+
* @id js/meta/taint-sources
9+
*/
10+
11+
import javascript
12+
import TaintMetrics
13+
14+
select projectRoot(), count(relevantTaintSource())
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* @name Taint steps
3+
* @description The number of default taint steps.
4+
* @kind metric
5+
* @metricType project
6+
* @metricAggregate sum
7+
* @tags meta
8+
* @id js/meta/taint-steps
9+
*/
10+
11+
import javascript
12+
import CallGraphQuality
13+
14+
class BasicTaintConfiguration extends TaintTracking::Configuration {
15+
BasicTaintConfiguration() { this = "BasicTaintConfiguration" }
16+
}
17+
18+
predicate relevantStep(DataFlow::Node pred, DataFlow::Node succ) {
19+
any(BasicTaintConfiguration cfg).isAdditionalFlowStep(pred, succ) and
20+
not pred.getFile() instanceof IgnoredFile and
21+
not succ.getFile() instanceof IgnoredFile
22+
}
23+
24+
select projectRoot(), count(DataFlow::Node pred, DataFlow::Node succ | relevantStep(pred, succ))

0 commit comments

Comments
 (0)