JS: Add more metrics

This commit is contained in:
Asger Feldthaus
2020-06-23 17:26:41 +01:00
parent 63d48bfe5c
commit d15c98d18c
12 changed files with 247 additions and 4 deletions

View File

@@ -1,6 +1,7 @@
/**
* Helpers to generating meta metrics, that is, metrics about the CodeQL analysis and extractor.
*/
private import javascript
private import semmle.javascript.dependencies.Dependencies
private import semmle.javascript.dependencies.FrameworkLibraries

View File

@@ -9,7 +9,6 @@ private import semmle.javascript.dependencies.Dependencies
private import semmle.javascript.dependencies.FrameworkLibraries
private import semmle.javascript.frameworks.Testing
private import DataFlow
import meta.MetaMetrics
/** An call site that is relevant for analysis quality. */

View File

@@ -0,0 +1,17 @@
/**
* @name Resolvable imports
* @description The number of imports that could be resolved to its target.
* @kind metric
* @metricType project
* @metricAggregate sum
* @tags meta
* @id js/meta/resolvable-imports
*/
import javascript
import CallGraphQuality
Import relevantImport() { not result.getFile() instanceof IgnoredFile }
select projectRoot(),
count(Import imprt | imprt = relevantImport() and exists(imprt.getImportedModule()))

View File

@@ -0,0 +1,16 @@
/**
* @name Route handlers
* @description The number of HTTP route handler functions found.
* @kind metric
* @metricType project
* @metricAggregate sum
* @tags meta
* @id js/meta/route-handlers
*/
import javascript
import CallGraphQuality
HTTP::RouteHandler relevantRouteHandler() { not result.getFile() instanceof IgnoredFile }
select projectRoot(), count(relevantRouteHandler())

View File

@@ -0,0 +1,22 @@
/**
* @name Sanitizers reachable from source
* @description The number of sanitizers reachable from a recognized taint source.
* @kind metric
* @metricType project
* @metricAggregate sum
* @tags meta
* @id js/meta/sanitizers-reachable-from-source
*/
import javascript
import TaintMetrics
class BasicTaintConfiguration extends TaintTracking::Configuration {
BasicTaintConfiguration() { this = "BasicTaintConfiguration" }
override predicate isSource(DataFlow::Node node) { node = relevantTaintSource() }
override predicate isSink(DataFlow::Node node) { node = relevantSanitizerInput() }
}
select projectRoot(), count(DataFlow::Node node | any(BasicTaintConfiguration cfg).hasFlow(_, node))

View File

@@ -0,0 +1,22 @@
/**
* @name Sinks reachable from sanitizer
* @description The number of sinks reachable from a recognized sanitizer call.
* @kind metric
* @metricType project
* @metricAggregate sum
* @tags meta
* @id js/meta/sinks-reachable-from-sanitizer
*/
import javascript
import TaintMetrics
class BasicTaintConfiguration extends TaintTracking::Configuration {
BasicTaintConfiguration() { this = "BasicTaintConfiguration" }
override predicate isSource(DataFlow::Node node) { node = relevantSanitizerOutput() }
override predicate isSink(DataFlow::Node node) { node = relevantTaintSink() }
}
select projectRoot(), count(DataFlow::Node node | any(BasicTaintConfiguration cfg).hasFlow(_, node))

View File

@@ -0,0 +1,89 @@
/**
* Provides predicates for measuring taint-tracking coverage.
*/
private import javascript
import meta.MetaMetrics
private import semmle.javascript.security.dataflow.ClientSideUrlRedirectCustomizations
private import semmle.javascript.security.dataflow.CodeInjectionCustomizations
private import semmle.javascript.security.dataflow.CommandInjectionCustomizations
private import semmle.javascript.security.dataflow.Xss as Xss
private import semmle.javascript.security.dataflow.NosqlInjectionCustomizations
private import semmle.javascript.security.dataflow.PrototypePollutionCustomizations
private import semmle.javascript.security.dataflow.RegExpInjectionCustomizations
private import semmle.javascript.security.dataflow.RequestForgeryCustomizations
private import semmle.javascript.security.dataflow.ServerSideUrlRedirectCustomizations
private import semmle.javascript.security.dataflow.SqlInjectionCustomizations
private import semmle.javascript.security.dataflow.TaintedPathCustomizations
private import semmle.javascript.security.dataflow.UnsafeDeserializationCustomizations
private import semmle.javascript.security.dataflow.XmlBombCustomizations
private import semmle.javascript.security.dataflow.XpathInjectionCustomizations
private import semmle.javascript.security.dataflow.XxeCustomizations
private import semmle.javascript.security.dataflow.ZipSlipCustomizations
/**
* Gets a relevant taint sink.
*
* To ensure this metric isn't dominated by a few queries with a huge number of sinks,
* we only include sinks for queries that have fairly specific sinks and/or have high severity
* relative to the number of sinks.
*
* Examples of excluded queries:
* - UnsafeDynamicMethodAccess: high severity (RCE) but has way too many sinks (every callee).
* - ClearTextLogging: not severe enough relative to number of sinks.
*/
DataFlow::Node relevantTaintSink() {
not result.getFile() instanceof IgnoredFile and
(
result instanceof ClientSideUrlRedirect::Sink or
result instanceof CodeInjection::Sink or
result instanceof CommandInjection::Sink or
result instanceof Xss::Shared::Sink or
result instanceof NosqlInjection::Sink or
result instanceof PrototypePollution::Sink or
result instanceof RegExpInjection::Sink or
result instanceof RequestForgery::Sink or
result instanceof ServerSideUrlRedirect::Sink or
result instanceof SqlInjection::Sink or
result instanceof TaintedPath::Sink or
result instanceof UnsafeDeserialization::Sink or
result instanceof XmlBomb::Sink or
result instanceof XpathInjection::Sink or
result instanceof Xxe::Sink or
result instanceof ZipSlip::Sink
)
}
/**
* Gets a remote flow source or `document.location` source.
*/
DataFlow::Node relevantTaintSource() {
not result.getFile() instanceof IgnoredFile and
(
result instanceof RemoteFlowSource
or
result = DOM::locationSource()
)
}
/**
* Gets the output of a call that shows intent to sanitize a value
* (indicating a likely vulnerability if the sanitizer was removed).
*
* Currently we only recognize HTML sanitizers.
*/
DataFlow::Node relevantSanitizerOutput() {
result = any(HtmlSanitizerCall call) and
not result.getFile() instanceof IgnoredFile
}
/**
* Gets the input to a call that shows intent to sanitize a value
* (indicating a likely vulnerability if the sanitizer was removed).
*
* Currently we only recognize HTML sanitizers.
*/
DataFlow::Node relevantSanitizerInput() {
result = any(HtmlSanitizerCall call).getInput() and
not result.getFile() instanceof IgnoredFile
}

View File

@@ -0,0 +1,14 @@
/**
* @name Taint sinks
* @description The number of high-severity taint sinks.
* @kind metric
* @metricType project
* @metricAggregate sum
* @tags meta
* @id js/meta/taint-sinks
*/
import javascript
import TaintMetrics
select projectRoot(), count(relevantTaintSink())

View File

@@ -0,0 +1,14 @@
/**
* @name Taint sources
* @description The number of remote flow sources and document.location sources
* @kind metric
* @metricType project
* @metricAggregate sum
* @tags meta
* @id js/meta/taint-sources
*/
import javascript
import TaintMetrics
select projectRoot(), count(relevantTaintSource())

View File

@@ -0,0 +1,24 @@
/**
* @name Taint steps
* @description The number of default taint steps.
* @kind metric
* @metricType project
* @metricAggregate sum
* @tags meta
* @id js/meta/taint-steps
*/
import javascript
import CallGraphQuality
class BasicTaintConfiguration extends TaintTracking::Configuration {
BasicTaintConfiguration() { this = "BasicTaintConfiguration" }
}
predicate relevantStep(DataFlow::Node pred, DataFlow::Node succ) {
any(BasicTaintConfiguration cfg).isAdditionalFlowStep(pred, succ) and
not pred.getFile() instanceof IgnoredFile and
not succ.getFile() instanceof IgnoredFile
}
select projectRoot(), count(DataFlow::Node pred, DataFlow::Node succ | relevantStep(pred, succ))

View File

@@ -0,0 +1,27 @@
/**
* @name Tainted expressions
* @description The number of expressions reachable from a remote flow source
* via default taint-tracking steps.
* @kind metric
* @metricType project
* @metricAggregate sum
* @tags meta
* @id js/meta/tainted-nodes
*/
import javascript
import TaintMetrics
class BasicTaintConfiguration extends TaintTracking::Configuration {
BasicTaintConfiguration() { this = "BasicTaintConfiguration" }
override predicate isSource(DataFlow::Node node) { node = relevantTaintSource() }
override predicate isSink(DataFlow::Node node) {
// To reduce noise from synthetic nodes, only count value nodes
node instanceof DataFlow::ValueNode and
not node.getFile() instanceof IgnoredFile
}
}
select projectRoot(), count(DataFlow::Node node | any(BasicTaintConfiguration cfg).hasFlow(_, node))

View File

@@ -12,8 +12,6 @@
import javascript
import meta.MetaMetrics
predicate isProperType(Type t) {
not t instanceof AnyType
}
predicate isProperType(Type t) { not t instanceof AnyType }
select projectRoot(), count(Expr e | isProperType(e.getType()))