Skip to content

Commit ad042ce

Browse files
authored
Add XSS support for JSP (#6944)
What Does This Do Add instrumentation to call XSS module: javax.servlet.jsp.JspWriter#print javax.servlet.jsp.JspWriter#println javax.servlet.jsp.JspWriter#write jakarta.servlet.jsp.JspWriter#print jakarta.servlet.jsp.JspWriter#println jakarta.servlet.jsp.JspWriter#write Add smoke tests Motivation Being able to report XSS vulnerabilities in JSP
1 parent f6f57a6 commit ad042ce

File tree

25 files changed

+623
-0
lines changed

25 files changed

+623
-0
lines changed

dd-java-agent/instrumentation/servlet/request-5/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ dependencies {
5454
implementation files(relocatedJavaxJar.outputs.files)
5555
compileOnly group: 'jakarta.servlet', name: 'jakarta.servlet-api', version: '5.0.0'
5656
testImplementation group: 'jakarta.servlet', name: 'jakarta.servlet-api', version: '5.0.0'
57+
testImplementation group: 'jakarta.servlet.jsp', name: 'jakarta.servlet.jsp-api', version: '3.0.0'
5758
testRuntimeOnly project(':dd-java-agent:instrumentation:iast-instrumenter')
5859

5960
javaxClassesToRelocate project(':dd-java-agent:instrumentation:servlet-common'), {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package datadog.trace.instrumentation.servlet5.jsp;
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.Sink;
7+
import datadog.trace.api.iast.VulnerabilityTypes;
8+
import datadog.trace.api.iast.sink.XssModule;
9+
import javax.annotation.Nonnull;
10+
11+
@Sink(VulnerabilityTypes.XSS)
12+
@CallSite(spi = IastCallSites.class)
13+
public class JakartaJspWriterCallSite {
14+
15+
@CallSite.Before("void jakarta.servlet.jsp.JspWriter.print(java.lang.String)")
16+
@CallSite.Before("void jakarta.servlet.jsp.JspWriter.println(java.lang.String)")
17+
@CallSite.Before("void jakarta.servlet.jsp.JspWriter.write(java.lang.String)")
18+
public static void beforeStringParam(@CallSite.Argument(0) @Nonnull final String s) {
19+
final XssModule module = InstrumentationBridge.XSS;
20+
if (module != null) {
21+
try {
22+
module.onXss(s);
23+
} catch (final Throwable e) {
24+
module.onUnexpectedException("beforeStringParam threw", e);
25+
}
26+
}
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package datadog.trace.instrumentation.servlet5.jsp;
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.Sink;
7+
import datadog.trace.api.iast.VulnerabilityTypes;
8+
import datadog.trace.api.iast.sink.XssModule;
9+
import javax.annotation.Nonnull;
10+
11+
@Sink(VulnerabilityTypes.XSS)
12+
@CallSite(
13+
spi = IastCallSites.class,
14+
enabled = {"datadog.trace.api.iast.IastEnabledChecks", "isFullDetection"})
15+
public class JakartaJspWriterFullDetectionCallSite {
16+
17+
@CallSite.Before("void jakarta.servlet.jsp.JspWriter.print(char[])")
18+
@CallSite.Before("void jakarta.servlet.jsp.JspWriter.println(char[])")
19+
@CallSite.Before("void jakarta.servlet.jsp.JspWriter.write(char[])")
20+
public static void beforeCharArrayParam(@CallSite.Argument(0) @Nonnull final char[] buf) {
21+
final XssModule module = InstrumentationBridge.XSS;
22+
if (module != null) {
23+
try {
24+
module.onXss(buf);
25+
} catch (final Throwable e) {
26+
module.onUnexpectedException("beforeCharArrayParam threw", e);
27+
}
28+
}
29+
}
30+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import datadog.trace.agent.test.AgentTestRunner
2+
import datadog.trace.api.iast.InstrumentationBridge
3+
import datadog.trace.api.iast.sink.XssModule
4+
import foo.bar.smoketest.TestJspWriterSuite
5+
6+
import jakarta.servlet.jsp.JspWriter
7+
8+
class JakartaJspWriterCallsiteTest extends AgentTestRunner{
9+
10+
static final STRING = "test"
11+
static final CHAR_ARRAY = STRING.toCharArray()
12+
13+
@Override
14+
protected void configurePreAgent() {
15+
injectSysConfig("dd.iast.enabled", "true")
16+
}
17+
18+
void 'test JspWriter'() {
19+
setup:
20+
final iastModule = Mock(XssModule)
21+
InstrumentationBridge.registerIastModule(iastModule)
22+
final writer = Mock(JspWriter)
23+
final suite = new TestJspWriterSuite(writer)
24+
25+
when:
26+
suite.&"$method".call(args)
27+
28+
then:
29+
expected * iastModule.onXss(args[0])
30+
0 * iastModule._
31+
32+
where:
33+
method | args | expected
34+
"printTest" | [STRING] | 1
35+
"printlnTest" | [STRING] | 1
36+
"write" | [STRING] | 1
37+
"printTest" | [CHAR_ARRAY] | 0
38+
"printlnTest" | [CHAR_ARRAY] | 0
39+
"write" | [CHAR_ARRAY] | 0
40+
}
41+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import datadog.trace.agent.test.AgentTestRunner
2+
import datadog.trace.api.iast.InstrumentationBridge
3+
import datadog.trace.api.iast.sink.XssModule
4+
import foo.bar.smoketest.TestJspWriterSuite
5+
import jakarta.servlet.jsp.JspWriter
6+
7+
class JakartaJspWriterFullDetectionCallsiteTest extends AgentTestRunner{
8+
9+
static final STRING = "test"
10+
static final CHAR_ARRAY = STRING.toCharArray()
11+
12+
@Override
13+
protected void configurePreAgent() {
14+
injectSysConfig("dd.iast.enabled", "true")
15+
injectSysConfig("dd.iast.detection.mode", "FULL")
16+
}
17+
18+
void 'test JspWriter'() {
19+
setup:
20+
final iastModule = Mock(XssModule)
21+
InstrumentationBridge.registerIastModule(iastModule)
22+
final writer = Mock(JspWriter)
23+
final suite = new TestJspWriterSuite(writer)
24+
25+
when:
26+
suite.&"$method".call(args)
27+
28+
then:
29+
1 * iastModule.onXss(args[0])
30+
0 * iastModule._
31+
32+
where:
33+
method | args
34+
"printTest" | [STRING]
35+
"printlnTest" | [STRING]
36+
"write" | [STRING]
37+
"printTest" | [CHAR_ARRAY]
38+
"printlnTest" | [CHAR_ARRAY]
39+
"write" | [CHAR_ARRAY]
40+
}
41+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package foo.bar.smoketest;
2+
3+
import jakarta.servlet.jsp.JspWriter;
4+
import java.io.IOException;
5+
6+
public class TestJspWriterSuite {
7+
8+
JspWriter writer;
9+
10+
public TestJspWriterSuite(final JspWriter writer) {
11+
this.writer = writer;
12+
}
13+
14+
public void printlnTest(char x[]) throws IOException {
15+
writer.println(x);
16+
}
17+
18+
public void printlnTest(String x) throws IOException {
19+
writer.println(x);
20+
}
21+
22+
public void printTest(char s[]) throws IOException {
23+
writer.print(s);
24+
}
25+
26+
public void printTest(String s) throws IOException {
27+
writer.print(s);
28+
}
29+
30+
public void write(char s[]) throws IOException {
31+
writer.write(s);
32+
}
33+
34+
public void write(String s) throws IOException {
35+
writer.write(s);
36+
}
37+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package datadog.trace.instrumentation.servlet.jsp;
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.Sink;
7+
import datadog.trace.api.iast.VulnerabilityTypes;
8+
import datadog.trace.api.iast.sink.XssModule;
9+
import javax.annotation.Nonnull;
10+
11+
@Sink(VulnerabilityTypes.XSS)
12+
@CallSite(spi = IastCallSites.class)
13+
public class JspWriterCallSite {
14+
15+
@CallSite.Before("void javax.servlet.jsp.JspWriter.print(java.lang.String)")
16+
@CallSite.Before("void javax.servlet.jsp.JspWriter.println(java.lang.String)")
17+
@CallSite.Before("void javax.servlet.jsp.JspWriter.write(java.lang.String)")
18+
public static void beforeStringParam(@CallSite.Argument(0) @Nonnull final String s) {
19+
final XssModule module = InstrumentationBridge.XSS;
20+
if (module != null) {
21+
try {
22+
module.onXss(s);
23+
} catch (final Throwable e) {
24+
module.onUnexpectedException("beforeStringParam threw", e);
25+
}
26+
}
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package datadog.trace.instrumentation.servlet.jsp;
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.Sink;
7+
import datadog.trace.api.iast.VulnerabilityTypes;
8+
import datadog.trace.api.iast.sink.XssModule;
9+
import javax.annotation.Nonnull;
10+
11+
@Sink(VulnerabilityTypes.XSS)
12+
@CallSite(
13+
spi = IastCallSites.class,
14+
enabled = {"datadog.trace.api.iast.IastEnabledChecks", "isFullDetection"})
15+
public class JspWriterFullDetectionCallSite {
16+
17+
@CallSite.Before("void javax.servlet.jsp.JspWriter.print(char[])")
18+
@CallSite.Before("void javax.servlet.jsp.JspWriter.println(char[])")
19+
@CallSite.Before("void javax.servlet.jsp.JspWriter.write(char[])")
20+
public static void beforeCharArrayParam(@CallSite.Argument(0) @Nonnull final char[] buf) {
21+
final XssModule module = InstrumentationBridge.XSS;
22+
if (module != null) {
23+
try {
24+
module.onXss(buf);
25+
} catch (final Throwable e) {
26+
module.onUnexpectedException("beforeCharArrayParam threw", e);
27+
}
28+
}
29+
}
30+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import datadog.trace.agent.test.AgentTestRunner
2+
import datadog.trace.api.iast.InstrumentationBridge
3+
import datadog.trace.api.iast.sink.XssModule
4+
import foo.bar.TestJspWriterSuite
5+
6+
import javax.servlet.jsp.JspWriter
7+
8+
class JspWriterCallSiteTest extends AgentTestRunner{
9+
10+
static final STRING = "test"
11+
static final CHAR_ARRAY = STRING.toCharArray()
12+
13+
@Override
14+
protected void configurePreAgent() {
15+
injectSysConfig("dd.iast.enabled", "true")
16+
}
17+
18+
void 'test JspWriter'() {
19+
setup:
20+
final iastModule = Mock(XssModule)
21+
InstrumentationBridge.registerIastModule(iastModule)
22+
final writer = Mock(JspWriter)
23+
final suite = new TestJspWriterSuite(writer)
24+
25+
when:
26+
suite.&"$method".call(args)
27+
28+
then:
29+
expected * iastModule.onXss(args[0])
30+
0 * iastModule._
31+
32+
where:
33+
method | args | expected
34+
"printTest" | [STRING] | 1
35+
"printlnTest" | [STRING] | 1
36+
"write" | [STRING] | 1
37+
"printTest" | [CHAR_ARRAY] | 0
38+
"printlnTest" | [CHAR_ARRAY] | 0
39+
"write" | [CHAR_ARRAY] | 0
40+
}
41+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import datadog.trace.agent.test.AgentTestRunner
2+
import datadog.trace.api.iast.InstrumentationBridge
3+
import datadog.trace.api.iast.sink.XssModule
4+
import foo.bar.TestJspWriterSuite
5+
6+
import javax.servlet.jsp.JspWriter
7+
8+
class JspWriterFullDetectionCallSiteTest extends AgentTestRunner{
9+
10+
static final STRING = "test"
11+
static final CHAR_ARRAY = STRING.toCharArray()
12+
13+
@Override
14+
protected void configurePreAgent() {
15+
injectSysConfig("dd.iast.enabled", "true")
16+
injectSysConfig("dd.iast.detection.mode", "FULL")
17+
}
18+
19+
void 'test JspWriter'() {
20+
setup:
21+
final iastModule = Mock(XssModule)
22+
InstrumentationBridge.registerIastModule(iastModule)
23+
final writer = Mock(JspWriter)
24+
final suite = new TestJspWriterSuite(writer)
25+
26+
when:
27+
suite.&"$method".call(args)
28+
29+
then:
30+
1 * iastModule.onXss(args[0])
31+
0 * iastModule._
32+
33+
where:
34+
method | args
35+
"printTest" | [STRING]
36+
"printlnTest" | [STRING]
37+
"write" | [STRING]
38+
"printTest" | [CHAR_ARRAY]
39+
"printlnTest" | [CHAR_ARRAY]
40+
"write" | [CHAR_ARRAY]
41+
}
42+
}

0 commit comments

Comments
 (0)