Skip to content

Commit ed724b7

Browse files
Try to match tainted objects with sources when checking vulnerabilities with unbounded objects
1 parent 34c6955 commit ed724b7

File tree

3 files changed

+77
-6
lines changed

3 files changed

+77
-6
lines changed

dd-java-agent/agent-iast/src/main/java/com/datadog/iast/sink/SinkModuleBase.java

+19-5
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import static com.datadog.iast.util.ObjectVisitor.State.CONTINUE;
44
import static com.datadog.iast.util.ObjectVisitor.State.EXIT;
5+
import static datadog.trace.api.iast.VulnerabilityMarks.NOT_MARKED;
56

67
import com.datadog.iast.HasDependencies;
78
import com.datadog.iast.IastRequestContext;
@@ -58,7 +59,7 @@ public void registerDependencies(@Nonnull final Dependencies dependencies) {
5859
if (!overheadController.consumeQuota(Operations.REPORT_VULNERABILITY, span)) {
5960
return null;
6061
}
61-
final Evidence result = new Evidence(value.toString(), ranges);
62+
final Evidence result = buildEvidence(value, ranges);
6263
report(span, type, result);
6364
return result;
6465
}
@@ -117,7 +118,7 @@ public void registerDependencies(@Nonnull final Dependencies dependencies) {
117118
return null;
118119
}
119120

120-
final Evidence result = new Evidence(evidence, notMarkedRanges);
121+
final Evidence result = buildEvidence(evidence, notMarkedRanges);
121122
report(span, type, result);
122123
return result;
123124
}
@@ -159,7 +160,7 @@ public void registerDependencies(@Nonnull final Dependencies dependencies) {
159160
if (notMarkedRanges == null || notMarkedRanges.length == 0) {
160161
return null;
161162
}
162-
final Evidence result = new Evidence(evidence.toString(), notMarkedRanges);
163+
final Evidence result = buildEvidence(evidence, notMarkedRanges);
163164
report(span, type, result);
164165
return result;
165166
}
@@ -177,8 +178,21 @@ protected StackTraceElement getCurrentStackTrace() {
177178
return stackWalker.walk(SinkModuleBase::findValidPackageForVulnerability);
178179
}
179180

180-
protected String getServiceName(final AgentSpan span) {
181-
return span != null ? span.getServiceName() : null;
181+
protected Evidence buildEvidence(final Object value, final Range[] ranges) {
182+
if (Ranges.isUnbound(ranges)) {
183+
final Range range = ranges[0];
184+
if (range != null && range.getSource() != null && range.getSource().getValue() != null) {
185+
final String sourceValue = range.getSource().getValue();
186+
final String evidenceValue = value.toString();
187+
final int start = evidenceValue.indexOf(sourceValue);
188+
if (start >= 0) {
189+
return new Evidence(
190+
evidenceValue,
191+
new Range[] {new Range(start, sourceValue.length(), range.getSource(), NOT_MARKED)});
192+
}
193+
}
194+
}
195+
return new Evidence(value instanceof String ? (String) value : value.toString(), ranges);
182196
}
183197

184198
static StackTraceElement findValidPackageForVulnerability(

dd-java-agent/agent-iast/src/main/java/com/datadog/iast/taint/Ranges.java

+8
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,14 @@ public static Range[] forObject(final @Nonnull Source source, final int mark) {
5959
return new Range[] {new Range(0, Integer.MAX_VALUE, source, mark)};
6060
}
6161

62+
public static boolean isUnbound(@Nonnull final Range[] ranges) {
63+
if (ranges.length != 1) {
64+
return false;
65+
}
66+
final Range range = ranges[0];
67+
return range.getStart() == 0 && range.getLength() == Integer.MAX_VALUE;
68+
}
69+
6270
public static void copyShift(
6371
final @Nonnull Range[] src, final @Nonnull Range[] dst, final int dstPos, final int shift) {
6472
copyShift(src, dst, dstPos, shift, src.length);

dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/sink/AbstractSinkModuleTest.groovy

+50-1
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,37 @@
11
package com.datadog.iast.sink
22

33
import com.datadog.iast.IastModuleImplTestBase
4+
import com.datadog.iast.IastRequestContext
5+
import com.datadog.iast.model.Source
6+
import com.datadog.iast.overhead.Operations
7+
import com.datadog.iast.propagation.PropagationModuleImpl
8+
import com.datadog.iast.taint.Ranges
9+
import datadog.trace.api.gateway.RequestContext
10+
import datadog.trace.api.gateway.RequestContextSlot
11+
import datadog.trace.bootstrap.instrumentation.api.AgentSpan
412

5-
class AbstractSinkModuleTest extends IastModuleImplTestBase {
13+
import static com.datadog.iast.model.VulnerabilityType.SSRF
14+
import static datadog.trace.api.iast.SourceTypes.REQUEST_PARAMETER_VALUE
15+
16+
class AbstractSinkModuleTest extends IastModuleImplTestBase {
617

718
final StackTraceElement ignoredPackageClassElement = element("org.springframework.Ignored")
819
final StackTraceElement notIgnoredPackageClassElement = element("datadog.smoketest.NotIgnored")
920
final StackTraceElement notInIastExclusionTrie = element("not.in.iast.exclusion.Class")
1021

22+
private IastRequestContext ctx
23+
private AgentSpan span
24+
25+
void setup() {
26+
ctx = new IastRequestContext()
27+
final reqCtx = Mock(RequestContext) {
28+
getData(RequestContextSlot.IAST) >> ctx
29+
}
30+
span = Mock(AgentSpan) {
31+
getRequestContext() >> reqCtx
32+
}
33+
tracer.activeSpan() >> span
34+
}
1135

1236
void 'filter ignored package element from stack'() {
1337

@@ -43,6 +67,31 @@ class AbstractSinkModuleTest extends IastModuleImplTestBase {
4367
result == expected
4468
}
4569

70+
void 'test reporting evidence on objects'() {
71+
given:
72+
final sink = registerDependencies(new SinkModuleBase() {})
73+
final propagation = new PropagationModuleImpl()
74+
final input = new String(source.value)
75+
ctx.getTaintedObjects().taint(source.value, Ranges.forCharSequence(input, source))
76+
77+
when:
78+
propagation.taintIfTainted(toReport, input)
79+
final evidence = sink.checkInjection(span, ctx, SSRF, toReport)
80+
81+
then:
82+
1 * overheadController.consumeQuota(Operations.REPORT_VULNERABILITY, span) >> true
83+
evidence.ranges.length == 1
84+
85+
final range = evidence.ranges[0]
86+
final taintedEvidence = evidence.value.toString().substring(range.start, range.start + range.length)
87+
taintedEvidence == input
88+
89+
where:
90+
source | toReport
91+
new Source(REQUEST_PARAMETER_VALUE, 'url', 'datadog.com') | new URL('https://datadog.com/index.html')
92+
new Source(REQUEST_PARAMETER_VALUE, 'url', 'datadog.com') | new URI('https://datadog.com/index.html')
93+
}
94+
4695
private StackTraceElement element(final String declaringClass) {
4796
return new StackTraceElement(declaringClass, "method", "fileName", 1)
4897
}

0 commit comments

Comments
 (0)