From 56a84002e7e2a00b21b7bd7d719c4997de082ad1 Mon Sep 17 00:00:00 2001 From: Asger F Date: Tue, 29 Sep 2026 10:26:07 +0200 Subject: [PATCH] unified: Add taint reach stats --- .../unified/internal/AnalysisQuality.qll | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/unified/ql/lib/codeql/unified/internal/AnalysisQuality.qll b/unified/ql/lib/codeql/unified/internal/AnalysisQuality.qll index 0553ecc3d858..463d9d3155f8 100644 --- a/unified/ql/lib/codeql/unified/internal/AnalysisQuality.qll +++ b/unified/ql/lib/codeql/unified/internal/AnalysisQuality.qll @@ -109,6 +109,32 @@ module CallGraphStats implements EntityStatsSig { module CallGraphStatsReport = EntityReportStats; +module TaintReach { + private class Candidate extends DataFlow::Node { + Candidate() { exists(this.asExpr()) } + } + + module TaintReachConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node node) { Models::isSource(node, _) } + + predicate isSink(DataFlow::Node node) { node instanceof Candidate } + } + + module TaintReachFlow = TaintTracking::Global; + + DataFlow::Node taintedNode() { TaintReachFlow::flowTo(result) } + + int numberOfTaintedNodes() { result = count(taintedNode()) } + + int numberOfCandidates() { result = count(Candidate c) } + + int perMillionNodes() { + result = (numberOfTaintedNodes() * 1000000) / numberOfCandidates() + or + numberOfCandidates() = 0 and result = 0 + } +} + /** * Gets summary statistics about taint. */ @@ -123,11 +149,11 @@ predicate taintStats(string key, int value) { or key = "Taint edges - number of edges" and none() or - key = "Taint reach - nodes tainted" and none() + key = "Taint reach - nodes tainted" and value = TaintReach::numberOfTaintedNodes() or key = "Taint reach - total non-summary nodes" and none() or - key = "Taint reach - per million nodes" and none() + key = "Taint reach - per million nodes" and value = TaintReach::perMillionNodes() or key = "Taint sinks - query sinks" and value = count(DataFlow::Node n | Models::isSink(n, _)) or