Skip to content

Commit bdbc145

Browse files
authored
Increase IAST propagation to StringBuilder append (#8010)
1 parent aa7092b commit bdbc145

File tree

8 files changed

+171
-0
lines changed

8 files changed

+171
-0
lines changed

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

+39
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,45 @@ public void onStringBuilderAppend(
128128
}
129129
}
130130

131+
@Override
132+
public void onStringBuilderAppend(
133+
@Nonnull CharSequence builder, @Nullable CharSequence param, int start, int end) {
134+
if (!canBeTainted(builder) || !canBeTainted(param)) {
135+
return;
136+
}
137+
if (start < 0 || end > param.length() || start > end) {
138+
return;
139+
}
140+
final IastContext ctx = IastContext.Provider.get();
141+
if (ctx == null) {
142+
return;
143+
}
144+
final TaintedObjects taintedObjects = ctx.getTaintedObjects();
145+
final TaintedObject paramTainted = taintedObjects.get(param);
146+
if (paramTainted == null) {
147+
return;
148+
}
149+
// We get the ranges for the new substring that will be appended to the builder
150+
final Range[] paramRanges = paramTainted.getRanges();
151+
final Range[] newParamRanges = Ranges.forSubstring(start, end - start, paramRanges);
152+
if (newParamRanges == null) {
153+
return;
154+
}
155+
final TaintedObject builderTainted = taintedObjects.get(builder);
156+
// If the builder is not tainted we must shift the ranges of the parameter
157+
// Else we shift the ranges of the parameter and merge with the ranges of the builder
158+
final int shift = builder.length() - (end - start);
159+
if (builderTainted == null) {
160+
final Range[] ranges = new Range[newParamRanges.length];
161+
Ranges.copyShift(newParamRanges, ranges, 0, shift);
162+
taintedObjects.taint(builder, ranges);
163+
} else {
164+
final Range[] builderRanges = builderTainted.getRanges();
165+
final Range[] ranges = mergeRanges(shift, builderRanges, newParamRanges);
166+
builderTainted.setRanges(ranges);
167+
}
168+
}
169+
131170
@Override
132171
public void onStringBuilderToString(
133172
@Nonnull final CharSequence builder, @Nonnull final String result) {

dd-java-agent/agent-iast/src/test/groovy/com/datadog/iast/propagation/StringModuleTest.groovy

+74
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,80 @@ class StringModuleTest extends IastModuleImplTestBase {
131131
sb('1==>234<==5==>678<==9') | 'a==>bcd<==e==>fgh<==i' | 1 | '1==>234<==5==>678<==9a==>bcd<==e==>fgh<==i'
132132
}
133133
134+
void 'onStringBuilderAppend null or empty (#builder, #param, #start, #end)'() {
135+
given:
136+
final result = builder?.append(param, start, end)
137+
138+
when:
139+
module.onStringBuilderAppend(result, param, start, end)
140+
141+
then:
142+
0 * _
143+
144+
where:
145+
builder | param | start | end
146+
sb('') | null | 0 | 0
147+
sb('') | '' | 0 | 0
148+
}
149+
150+
void 'onStringBuilderAppend without span (#builder, #param, #start, #end)'() {
151+
given:
152+
final result = builder?.append(param, start, end)
153+
154+
when:
155+
module.onStringBuilderAppend(result, param)
156+
157+
then:
158+
mockCalls * tracer.activeSpan() >> null
159+
0 * _
160+
161+
where:
162+
builder | param | start | end | mockCalls
163+
sb('1') | null | 0 | 0 | 0
164+
sb('3') | '4' | 0 | 0 | 1
165+
}
166+
167+
void 'onStringBuilderAppend (#builder, #param, #start, #end)'() {
168+
given:
169+
final taintedObjects = ctx.getTaintedObjects()
170+
def builderTainted = addFromTaintFormat(taintedObjects, builder)
171+
objectHolder.add(builderTainted)
172+
def paramTainted = addFromTaintFormat(taintedObjects, param)
173+
objectHolder.add(paramTainted)
174+
builderTainted?.append(paramTainted, start, end)
175+
176+
and:
177+
final result = getStringFromTaintFormat(expected)
178+
objectHolder.add(result)
179+
final shouldBeTainted = fromTaintFormat(expected) != null
180+
181+
when:
182+
module.onStringBuilderAppend(builderTainted, paramTainted, start, end)
183+
def taintedObject = taintedObjects.get(builderTainted)
184+
185+
then:
186+
if (shouldBeTainted) {
187+
assert taintedObject != null
188+
assert taintedObject.get() as String == result
189+
assert taintFormat(taintedObject.get() as String, taintedObject.getRanges()) == expected
190+
} else {
191+
assert taintedObject == null
192+
}
193+
194+
where:
195+
builder | param | start | end | expected
196+
sb('123') | '456' | 0 | 3 | '123456'
197+
sb('==>123<==') | '456' | 0 | 3 | '==>123<==456'
198+
sb('==>123<==') | '456' | 1 | 3 | '==>123<==56'
199+
sb('123') | '==>456<==' | 0 | 3 | '123==>456<=='
200+
sb('123') | '==>456<==' | 1 | 2 | '123==>5<=='
201+
sb('==>123<==') | '==>456<==' | 0 | 3 | '==>123<====>456<=='
202+
sb('1==>234<==5==>678<==9') | 'a==>bcd<==e' | 0 | 5 | '1==>234<==5==>678<==9a==>bcd<==e'
203+
sb('1==>234<==5==>678<==9') | 'a==>bcd<==e==>fgh<==i' | 0 | 9 | '1==>234<==5==>678<==9a==>bcd<==e==>fgh<==i'
204+
sb('1==>234<==5==>678<==9') | 'a==>bcd<==e==>fgh<==i' | 5 | 9 | '1==>234<==5==>678<==9==>fgh<==i'
205+
sb('1==>234<==5==>678<==9') | 'a==>bcd<==e==>fgh<==i' | 5 | 8 | '1==>234<==5==>678<==9==>fgh<=='
206+
}
207+
134208
void 'onStringBuilderInit null or empty (#builder, #param)'() {
135209
given:
136210
final result = builder?.append(param)

dd-java-agent/instrumentation/java-lang/src/main/java/datadog/trace/instrumentation/java/lang/StringBuilderCallSite.java

+21
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public static CharSequence afterInit(
3838

3939
@CallSite.After("java.lang.StringBuilder java.lang.StringBuilder.append(java.lang.String)")
4040
@CallSite.After("java.lang.StringBuilder java.lang.StringBuilder.append(java.lang.CharSequence)")
41+
@CallSite.After("java.lang.StringBuilder java.lang.StringBuilder.append(java.lang.StringBuffer)")
4142
@CallSite.After("java.lang.StringBuffer java.lang.StringBuffer.append(java.lang.String)")
4243
@CallSite.After("java.lang.StringBuffer java.lang.StringBuffer.append(java.lang.CharSequence)")
4344
@Nonnull
@@ -56,6 +57,26 @@ public static CharSequence afterAppend(
5657
return result;
5758
}
5859

60+
@CallSite.After(
61+
"java.lang.StringBuilder java.lang.StringBuilder.append(java.lang.CharSequence, int, int)")
62+
@Nonnull
63+
public static CharSequence afterAppendWithSubstring(
64+
@CallSite.This @Nonnull final CharSequence self,
65+
@CallSite.Argument(0) @Nullable final CharSequence param,
66+
@CallSite.Argument(1) final int start,
67+
@CallSite.Argument(2) final int end,
68+
@CallSite.Return @Nonnull final CharSequence result) {
69+
final StringModule module = InstrumentationBridge.STRING;
70+
if (module != null) {
71+
try {
72+
module.onStringBuilderAppend(self, param, start, end);
73+
} catch (final Throwable e) {
74+
module.onUnexpectedException("afterAppendWithSubstring threw", e);
75+
}
76+
}
77+
return result;
78+
}
79+
5980
@CallSite.Around("java.lang.StringBuilder java.lang.StringBuilder.append(java.lang.Object)")
6081
@CallSite.Around("java.lang.StringBuffer java.lang.StringBuffer.append(java.lang.Object)")
6182
@Nonnull

dd-java-agent/instrumentation/java-lang/src/test/groovy/datadog/trace/instrumentation/java/lang/StringBuilderCallSiteTest.groovy

+18
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,24 @@ class StringBuilderCallSiteTest extends AgentTestRunner {
9090
new TestStringBufferSuite() | new StringBuffer('Hello ')
9191
}
9292

93+
void 'test string builder append call site with start and end'() {
94+
setup:
95+
final iastModule = Mock(StringModule)
96+
InstrumentationBridge.registerIastModule(iastModule)
97+
98+
when:
99+
suite.append(target, param, start, end)
100+
101+
then:
102+
target.toString() == expected
103+
1 * iastModule.onStringBuilderAppend(target, param, start, end)
104+
0 * _
105+
106+
where:
107+
suite | target | param | start | end | expected
108+
new TestStringBuilderSuite() | new StringBuilder('Hello ') | 'World!' | 0 | 5 | 'Hello World'
109+
}
110+
93111
void 'test string builder toString call site'() {
94112
setup:
95113
final iastModule = Mock(StringModule)

dd-java-agent/instrumentation/java-lang/src/test/java/foo/bar/TestAbstractStringBuilderSuite.java

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ public interface TestAbstractStringBuilderSuite<E> {
1010

1111
void append(final E target, final CharSequence param);
1212

13+
void append(final E target, final CharSequence param, int start, int end);
14+
1315
void append(final E target, final Object param);
1416

1517
String substring(final E self, final int beginIndex, final int endIndex);

dd-java-agent/instrumentation/java-lang/src/test/java/foo/bar/TestStringBufferSuite.java

+7
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ public void append(final StringBuffer buffer, final CharSequence param) {
3737
LOGGER.debug("After string buffer append {}", result);
3838
}
3939

40+
@Override
41+
public void append(final StringBuffer builder, final CharSequence param, int start, int end) {
42+
LOGGER.debug("Before string buffer append {} with start {} and end {}", param, start, end);
43+
final StringBuffer result = builder.append(param, start, end);
44+
LOGGER.debug("After string buffer append {}", result);
45+
}
46+
4047
@Override
4148
public void append(final StringBuffer buffer, final Object param) {
4249
LOGGER.debug("Before string buffer append {}", param);

dd-java-agent/instrumentation/java-lang/src/test/java/foo/bar/TestStringBuilderSuite.java

+7
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@ public void append(final StringBuilder builder, final CharSequence param) {
3838
LOGGER.debug("After string builder append {}", result);
3939
}
4040

41+
@Override
42+
public void append(final StringBuilder builder, final CharSequence param, int start, int end) {
43+
LOGGER.debug("Before string builder append {} with start {} and end {}", param, start, end);
44+
final StringBuilder result = builder.append(param, start, end);
45+
LOGGER.debug("After string builder append {}", result);
46+
}
47+
4148
@Override
4249
public void append(final StringBuilder builder, final Object param) {
4350
LOGGER.debug("Before string builder append {}", param);

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

+3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ public interface StringModule extends IastModule {
1313

1414
void onStringBuilderAppend(@Nonnull CharSequence builder, @Nullable CharSequence param);
1515

16+
void onStringBuilderAppend(
17+
@Nonnull CharSequence builder, @Nullable CharSequence param, int start, int end);
18+
1619
void onStringBuilderToString(@Nonnull CharSequence builder, @Nonnull String result);
1720

1821
void onStringConcatFactory(

0 commit comments

Comments
 (0)