Skip to content

Commit ce7fee4

Browse files
hugo-synclaude
andcommitted
C#: add AdditionalTaintStep extension point for taint-tracking
Mirrors Java's AdditionalTaintStep (java/ql/lib/semmle/code/java/dataflow/FlowSteps.qll) and Ruby/Swift's existing ports of the same pattern: a `Unit`-based class that lets library code contribute additional taint steps applied to every taint-tracking configuration, with zero opt-in required. Wired into defaultAdditionalTaintStep in TaintTrackingPrivate.qll, tagged with model = "AdditionalTaintStep" for provenance. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
1 parent 3aecce9 commit ce7fee4

7 files changed

Lines changed: 72 additions & 0 deletions

File tree

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: feature
3+
---
4+
* Added the `AdditionalTaintStep` extension point (`semmle.code.csharp.dataflow.FlowSteps`). Extend this class to add additional taint steps that apply to all taint-tracking configurations.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* Provides classes representing various flow steps for taint tracking.
3+
*/
4+
5+
private import codeql.util.Unit
6+
private import semmle.code.csharp.dataflow.DataFlow
7+
8+
/**
9+
* A unit class for adding additional taint steps.
10+
*
11+
* Extend this class to add additional taint steps that should apply to all
12+
* taint configurations.
13+
*/
14+
class AdditionalTaintStep extends Unit {
15+
/**
16+
* Holds if the step from `node1` to `node2` should be considered a taint
17+
* step for all configurations.
18+
*/
19+
abstract predicate step(DataFlow::Node node1, DataFlow::Node node2);
20+
}

csharp/ql/lib/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ private import csharp
22
private import TaintTrackingPublic
33
private import FlowSummaryImpl as FlowSummaryImpl
44
private import semmle.code.csharp.Caching
5+
private import semmle.code.csharp.dataflow.FlowSteps
56
private import semmle.code.csharp.dataflow.internal.DataFlowDispatch
67
private import semmle.code.csharp.dataflow.internal.DataFlowPrivate
78
private import semmle.code.csharp.dispatch.Dispatch
@@ -173,6 +174,8 @@ private module Cached {
173174
or
174175
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom,
175176
nodeTo.(FlowSummaryNode).getSummaryNode(), false, model)
177+
or
178+
any(AdditionalTaintStep a).step(nodeFrom, nodeTo) and model = "AdditionalTaintStep"
176179
}
177180
}
178181

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
| Test.cs:13:35:13:45 | access to parameter taintSource | Test.cs:13:23:13:46 | call to method Step | AdditionalTaintStep |
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import csharp
2+
import semmle.code.csharp.dataflow.internal.TaintTrackingPrivate
3+
import TestAdditionalTaintStep
4+
5+
from DataFlow::Node src, DataFlow::Node sink, string model
6+
where defaultAdditionalTaintStep(src, sink, model) and model = "AdditionalTaintStep"
7+
select src, sink, model
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
class Marker
2+
{
3+
// A stand-in for a framework method that isn't otherwise understood by the
4+
// taint-tracking library, whose taint behaviour is modelled by a test-only
5+
// `AdditionalTaintStep` subclass instead.
6+
public static object Step(object x) => new object();
7+
}
8+
9+
class Test
10+
{
11+
void M(object taintSource)
12+
{
13+
var tainted = Marker.Step(taintSource);
14+
Sink(tainted);
15+
}
16+
17+
static void Sink(object o) { }
18+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import csharp
2+
import semmle.code.csharp.dataflow.FlowSteps
3+
4+
/**
5+
* A test-only additional taint step that treats calls to `Marker.Step` as
6+
* propagating taint from the argument to the call result, to verify that
7+
* `AdditionalTaintStep` subclasses are picked up by `defaultAdditionalTaintStep`.
8+
*/
9+
private class MarkerStepTaintStep extends AdditionalTaintStep {
10+
override predicate step(DataFlow::Node node1, DataFlow::Node node2) {
11+
exists(MethodCall mc |
12+
mc.getTarget().hasName("Step") and
13+
mc.getTarget().getDeclaringType().hasName("Marker")
14+
|
15+
node1.asExpr() = mc.getArgument(0) and
16+
node2.asExpr() = mc
17+
)
18+
}
19+
}

0 commit comments

Comments
 (0)