Skip to content

Commit fdda8e1

Browse files
committed
APPSEC-55380 String taint tracking: translateEscapes
1 parent 22458b3 commit fdda8e1

File tree

7 files changed

+308
-0
lines changed

7 files changed

+308
-0
lines changed

dd-java-agent/agent-iast/src/main/java/com/datadog/iast/propagation/StringModuleImpl.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import java.util.stream.Stream;
3434
import javax.annotation.Nonnull;
3535
import javax.annotation.Nullable;
36+
import org.jetbrains.annotations.NotNull;
3637

3738
public class StringModuleImpl implements StringModule {
3839

@@ -293,6 +294,34 @@ public void onStringJoin(
293294
}
294295
}
295296

297+
@Override
298+
public void onStringTranslateEscapes(
299+
@NotNull String self, @org.jetbrains.annotations.Nullable String result) {
300+
if (!canBeTainted(result)) {
301+
return;
302+
}
303+
if (self == result) { // same ref, no change in taint status
304+
return;
305+
}
306+
final IastContext ctx = IastContext.Provider.get();
307+
if (ctx == null) {
308+
return;
309+
}
310+
final TaintedObjects taintedObjects = ctx.getTaintedObjects();
311+
final TaintedObject taintedSelf = taintedObjects.get(self);
312+
if (taintedSelf == null) {
313+
return; // original string is not tainted
314+
}
315+
final Range[] rangesSelf = taintedSelf.getRanges();
316+
if (rangesSelf.length == 0) {
317+
return; // original string is not tainted
318+
}
319+
final Range[] newRanges = Ranges.forSubstring(0, result.length(), rangesSelf);
320+
if (newRanges != null) {
321+
taintedObjects.taint(result, newRanges); // only possibility left
322+
}
323+
}
324+
296325
@Override
297326
@SuppressFBWarnings("ES_COMPARING_PARAMETER_STRING_WITH_EQ")
298327
public void onStringRepeat(@Nonnull String self, int count, @Nonnull String result) {
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
plugins {
2+
id 'idea'
3+
}
4+
5+
ext {
6+
minJavaVersionForTests = JavaVersion.VERSION_15
7+
}
8+
9+
apply from: "$rootDir/gradle/java.gradle"
10+
apply plugin: 'call-site-instrumentation'
11+
12+
muzzle {
13+
pass {
14+
coreJdk()
15+
}
16+
}
17+
18+
idea {
19+
module {
20+
jdkName = '17'
21+
}
22+
}
23+
24+
csi {
25+
javaVersion = JavaLanguageVersion.of(15)
26+
}
27+
28+
addTestSuiteForDir('latestDepTest', 'test')
29+
30+
dependencies {
31+
testRuntimeOnly project(':dd-java-agent:instrumentation:iast-instrumenter')
32+
}
33+
34+
project.tasks.withType(AbstractCompile).configureEach {
35+
setJavaVersion(it, 17)
36+
if (it.name != 'compileCsiJava') {
37+
sourceCompatibility = JavaVersion.VERSION_15
38+
targetCompatibility = JavaVersion.VERSION_15
39+
if (it instanceof JavaCompile) {
40+
it.options.release.set(15)
41+
}
42+
}
43+
}

dd-java-agent/instrumentation/java-lang/java-lang-15/gradle.lockfile

Lines changed: 153 additions & 0 deletions
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package datadog.trace.instrumentation.java.lang.jdk15;
2+
3+
import datadog.trace.agent.tooling.csi.CallSite;
4+
import datadog.trace.api.iast.IastCallSites;
5+
import datadog.trace.api.iast.InstrumentationBridge;
6+
import datadog.trace.api.iast.Propagation;
7+
import datadog.trace.api.iast.propagation.StringModule;
8+
9+
@Propagation
10+
@CallSite(
11+
spi = IastCallSites.class,
12+
enabled = {"datadog.trace.api.iast.IastEnabledChecks", "isMajorJavaVersionAtLeast", "15"})
13+
public class StringCallSite {
14+
@CallSite.After("java.lang.String java.lang.String.translateEscapes()")
15+
public static String afterTranslateEscapes(
16+
@CallSite.This final String self,
17+
@CallSite.Return final String result) {
18+
final StringModule module = InstrumentationBridge.STRING;
19+
try {
20+
if (module != null) {
21+
module.onTranslateEscapes(self, result);
22+
}
23+
} catch (final Throwable e) {
24+
module.onUnexpectedException("afterTranslateEscapes threw", e);
25+
}
26+
return result;
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package datadog.trace.instrumentation.java.lang.jdk15
2+
3+
import datadog.trace.agent.test.AgentTestRunner
4+
import datadog.trace.api.iast.InstrumentationBridge
5+
import datadog.trace.api.iast.propagation.StringModule
6+
import foo.bar.TestStringJDK15Suite
7+
import spock.lang.Requires
8+
9+
@Requires({
10+
jvm.java15Compatible
11+
})
12+
class StringCallSiteTest extends AgentTestRunner {
13+
14+
@Override
15+
protected void configurePreAgent() {
16+
injectSysConfig("dd.iast.enabled", "true")
17+
}
18+
19+
def 'test string translate escapes call site'() {
20+
setup:
21+
final iastModule = Mock(StringModule)
22+
InstrumentationBridge.registerIastModule(iastModule)
23+
24+
when:
25+
final result = TestStringJDK15Suite.stringTranslateEscapes(input)
26+
27+
then:
28+
result == output
29+
1 * iastModule.onStringTranslateEscapes(input, output)
30+
31+
where:
32+
input | output
33+
'Hello\tThis is a line' | 'Hello\n This is a line\n'
34+
}
35+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package foo.bar;
2+
3+
import org.slf4j.Logger;
4+
import org.slf4j.LoggerFactory;
5+
6+
public abstract class TestStringJDK15Suite {
7+
8+
private static final Logger LOGGER = LoggerFactory.getLogger(TestStringJDK15Suite.class);
9+
10+
private TestStringJDK15Suite() {}
11+
12+
public static String stringTranslateEscapes(String self) {
13+
LOGGER.debug("Before string translate escapes {}", self);
14+
final String result = self.translateEscapes();
15+
LOGGER.debug("After string translate escapes {}", result);
16+
return result;
17+
}
18+
}

internal-api/src/main/java/datadog/trace/api/iast/propagation/StringModule.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ void onStringJoin(
3535

3636
void onStringToUpperCase(@Nonnull String self, @Nullable String result);
3737

38+
void onStringTranslateEscapes(@Nonnull String self, @Nullable String result);
39+
3840
void onStringToLowerCase(@Nonnull String self, @Nullable String result);
3941

4042
void onStringTrim(@Nonnull String self, @Nullable String result);

0 commit comments

Comments
 (0)