-
Notifications
You must be signed in to change notification settings - Fork 292
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add XSS support for Velocity (#7546)
- Loading branch information
Showing
16 changed files
with
433 additions
and
0 deletions.
There are no files selected for viewing
Validating CODEOWNERS rules …
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
|
||
apply from: "$rootDir/gradle/java.gradle" | ||
apply plugin: 'call-site-instrumentation' | ||
addTestSuiteForDir('latestDepTest', 'test') | ||
|
||
dependencies { | ||
compileOnly group: 'org.apache.velocity', name: 'velocity', version: '1.5' | ||
compileOnly group: 'org.apache.velocity', name: 'velocity-tools', version: '1.3' | ||
testImplementation group: 'org.apache.velocity', name: 'velocity', version: '1.5' | ||
testImplementation group: 'org.apache.velocity', name: 'velocity-tools', version: '1.3' | ||
|
||
testRuntimeOnly project(':dd-java-agent:instrumentation:iast-instrumenter') | ||
|
||
latestDepTestImplementation group: 'org.apache.velocity', name: 'velocity', version: '+' | ||
latestDepTestImplementation group: 'org.apache.velocity', name: 'velocity-tools', version: '+' | ||
} |
66 changes: 66 additions & 0 deletions
66
...ity/src/main/java/datadog/trace/instrumentation/velocity/ASTReferenceInstrumentation.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package datadog.trace.instrumentation.velocity; | ||
|
||
import static datadog.trace.agent.tooling.bytebuddy.matcher.NameMatchers.named; | ||
import static net.bytebuddy.matcher.ElementMatchers.isMethod; | ||
import static net.bytebuddy.matcher.ElementMatchers.takesArgument; | ||
|
||
import com.google.auto.service.AutoService; | ||
import datadog.trace.agent.tooling.Instrumenter; | ||
import datadog.trace.agent.tooling.InstrumenterModule; | ||
import datadog.trace.api.iast.InstrumentationBridge; | ||
import datadog.trace.api.iast.Sink; | ||
import datadog.trace.api.iast.VulnerabilityTypes; | ||
import datadog.trace.api.iast.sink.XssModule; | ||
import net.bytebuddy.asm.Advice; | ||
import org.apache.velocity.context.InternalContextAdapter; | ||
import org.apache.velocity.runtime.parser.node.ASTReference; | ||
|
||
@AutoService(InstrumenterModule.class) | ||
public class ASTReferenceInstrumentation extends InstrumenterModule.Iast | ||
implements Instrumenter.ForSingleType { | ||
public ASTReferenceInstrumentation() { | ||
super("velocity"); | ||
} | ||
|
||
@Override | ||
public String instrumentedType() { | ||
return "org.apache.velocity.runtime.parser.node.ASTReference"; | ||
} | ||
|
||
@Override | ||
public void methodAdvice(MethodTransformer transformer) { | ||
transformer.applyAdvice( | ||
named("render") | ||
.and(isMethod()) | ||
.and( | ||
takesArgument(0, named("org.apache.velocity.context.InternalContextAdapter")) | ||
.and(takesArgument(1, named("java.io.Writer")))), | ||
ASTReferenceInstrumentation.class.getName() + "$ASTReferenceAdvice"); | ||
} | ||
|
||
public static class ASTReferenceAdvice { | ||
|
||
@Advice.OnMethodEnter(suppress = Throwable.class) | ||
@Sink(VulnerabilityTypes.XSS) | ||
public static void onEnter( | ||
@Advice.Argument(0) final InternalContextAdapter context, | ||
@Advice.This final ASTReference self) { | ||
if (self == null) { | ||
return; | ||
} | ||
final XssModule xssModule = InstrumentationBridge.XSS; | ||
if (xssModule == null) { | ||
return; | ||
} | ||
Object variable = self.getVariableValue(context, self.getRootString()); | ||
// For cases when you have a variable that is not a string such as the EscapeTool variable | ||
if (!(variable instanceof String)) { | ||
return; | ||
} | ||
final String charSec = (String) variable; | ||
final String file = context.getCurrentTemplateName(); | ||
final int line = self.getLine(); | ||
xssModule.onXss(charSec, file, line); | ||
} | ||
} | ||
} |
55 changes: 55 additions & 0 deletions
55
...ion/velocity/src/main/java/datadog/trace/instrumentation/velocity/EscapeToolCallSite.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package datadog.trace.instrumentation.velocity; | ||
|
||
import datadog.trace.agent.tooling.csi.CallSite; | ||
import datadog.trace.api.iast.IastCallSites; | ||
import datadog.trace.api.iast.InstrumentationBridge; | ||
import datadog.trace.api.iast.Propagation; | ||
import datadog.trace.api.iast.VulnerabilityMarks; | ||
import datadog.trace.api.iast.propagation.PropagationModule; | ||
import javax.annotation.Nullable; | ||
import org.apache.velocity.tools.generic.EscapeTool; | ||
|
||
@Propagation | ||
@CallSite(spi = IastCallSites.class) | ||
public class EscapeToolCallSite { | ||
|
||
@CallSite.After( | ||
"java.lang.String org.apache.velocity.tools.generic.EscapeTool.html(java.lang.Object)") | ||
@CallSite.After( | ||
"java.lang.String org.apache.velocity.tools.generic.EscapeTool.javascript(java.lang.Object)") | ||
@CallSite.After( | ||
"java.lang.String org.apache.velocity.tools.generic.EscapeTool.url(java.lang.Object)") | ||
@CallSite.After( | ||
"java.lang.String org.apache.velocity.tools.generic.EscapeTool.xml(java.lang.Object)") | ||
public static String afterEscape( | ||
@CallSite.This final EscapeTool self, | ||
@CallSite.Argument(0) @Nullable final Object input, | ||
@CallSite.Return final String result) { | ||
final PropagationModule module = InstrumentationBridge.PROPAGATION; | ||
if (module != null) { | ||
try { | ||
module.taintStringIfTainted(result, input, false, VulnerabilityMarks.XSS_MARK); | ||
} catch (final Throwable e) { | ||
module.onUnexpectedException("afterEscape threw", e); | ||
} | ||
} | ||
return result; | ||
} | ||
|
||
@CallSite.After( | ||
"java.lang.String org.apache.velocity.tools.generic.EscapeTool.sql(java.lang.Object)") | ||
public static String afterEscapeSQL( | ||
@CallSite.This final EscapeTool self, | ||
@CallSite.Argument(0) @Nullable final Object input, | ||
@CallSite.Return final String result) { | ||
final PropagationModule module = InstrumentationBridge.PROPAGATION; | ||
if (module != null) { | ||
try { | ||
module.taintStringIfTainted(result, input, false, VulnerabilityMarks.SQL_INJECTION_MARK); | ||
} catch (final Throwable e) { | ||
module.onUnexpectedException("afterEscapeSQL threw", e); | ||
} | ||
} | ||
return result; | ||
} | ||
} |
69 changes: 69 additions & 0 deletions
69
...test/groovy/datadog/trace/instrumentation/velocity/ASTReferenceInstrumentationTest.groovy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
package datadog.trace.instrumentation.velocity | ||
|
||
import datadog.trace.agent.test.AgentTestRunner | ||
import datadog.trace.api.iast.InstrumentationBridge | ||
import datadog.trace.api.iast.sink.XssModule | ||
import org.apache.velocity.Template | ||
import org.apache.velocity.VelocityContext | ||
import org.apache.velocity.app.VelocityEngine | ||
import org.apache.velocity.runtime.RuntimeConstants | ||
import org.apache.velocity.tools.generic.EscapeTool | ||
|
||
class ASTReferenceInstrumentationTest extends AgentTestRunner { | ||
|
||
@Override | ||
protected void configurePreAgent() { | ||
injectSysConfig('dd.iast.enabled', 'true') | ||
} | ||
|
||
void 'test ASTReference execute (insecure)'() { | ||
given: | ||
final module = Mock(XssModule) | ||
InstrumentationBridge.registerIastModule(module) | ||
VelocityEngine velocity = new VelocityEngine() | ||
velocity.setProperty( | ||
RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS, | ||
"org.apache.velocity.runtime.log.NullLogChute") | ||
velocity.init() | ||
Template template = velocity.getTemplate("src/test/resources/velocity-astreference-insecure.vm") | ||
VelocityContext context = new VelocityContext() | ||
context.put("param", param) | ||
when: | ||
template.merge(context, Mock(FileWriter)) | ||
then: | ||
1 * module.onXss(_, _, _) | ||
where: | ||
param << ["<script>alert(1)</script>", "name"] | ||
} | ||
void 'test ASTReference execute (secure)'() { | ||
given: | ||
final module = Mock(XssModule) | ||
InstrumentationBridge.registerIastModule(module) | ||
VelocityEngine velocity = new VelocityEngine() | ||
velocity.setProperty( | ||
RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS, | ||
"org.apache.velocity.runtime.log.NullLogChute") | ||
velocity.init() | ||
Template template = velocity.getTemplate("src/test/resources/velocity-astreference-secure.vm") | ||
VelocityContext context = new VelocityContext() | ||
context.put("esc", new EscapeTool()) | ||
context.put("param", param) | ||
when: | ||
template.merge(context, Mock(FileWriter)) | ||
then: | ||
0 * module.onXss(_, _, _) | ||
where: | ||
param << ["<script>alert(1)</script>", "name"] | ||
} | ||
} |
41 changes: 41 additions & 0 deletions
41
...city/src/test/groovy/datadog/trace/instrumentation/velocity/EscapeToolCallSiteTest.groovy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package datadog.trace.instrumentation.velocity | ||
|
||
import datadog.trace.agent.test.AgentTestRunner | ||
import datadog.trace.api.iast.InstrumentationBridge | ||
import datadog.trace.api.iast.propagation.PropagationModule | ||
import foo.bar.TestEscapeToolSuite | ||
import groovy.transform.CompileDynamic | ||
|
||
import static datadog.trace.api.iast.VulnerabilityMarks.SQL_INJECTION_MARK | ||
import static datadog.trace.api.iast.VulnerabilityMarks.XSS_MARK | ||
|
||
@CompileDynamic | ||
class EscapeToolCallSiteTest extends AgentTestRunner { | ||
|
||
@Override | ||
protected void configurePreAgent() { | ||
injectSysConfig("dd.iast.enabled", "true") | ||
} | ||
|
||
void 'test #method'() { | ||
given: | ||
final module = Mock(PropagationModule) | ||
InstrumentationBridge.registerIastModule(module) | ||
|
||
when: | ||
final result = TestEscapeToolSuite.&"$method".call(args) | ||
|
||
then: | ||
result == expected | ||
1 * module.taintStringIfTainted(_ as String, args[0], false, mark) | ||
0 * _ | ||
|
||
where: | ||
method | args | mark | expected | ||
'html' | ['Ø-This is a quote'] | XSS_MARK | 'Ø-This is a quote' | ||
'javascript' | ['Ø-This is a quote'] | XSS_MARK | '\\u00D8-This is a quote' | ||
'url' | ['Ø-This is a quote'] | XSS_MARK | '%C3%98-This+is+a+quote' | ||
'xml' | ['Ø-This is a quote'] | XSS_MARK | 'Ø-This is a quote' | ||
'sql' | ['Ø-This is a quote'] | SQL_INJECTION_MARK | 'Ø-This is a quote' | ||
} | ||
} |
28 changes: 28 additions & 0 deletions
28
dd-java-agent/instrumentation/velocity/src/test/java/foo/bar/TestEscapeToolSuite.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package foo.bar; | ||
|
||
import org.apache.velocity.tools.generic.EscapeTool; | ||
|
||
public class TestEscapeToolSuite { | ||
|
||
public static EscapeTool escapeTool = new EscapeTool(); | ||
|
||
public static String html(String input) { | ||
return escapeTool.html(input); | ||
} | ||
|
||
public static String javascript(String input) { | ||
return escapeTool.javascript(input); | ||
} | ||
|
||
public static String url(String input) { | ||
return escapeTool.url(input); | ||
} | ||
|
||
public static String xml(String input) { | ||
return escapeTool.xml(input); | ||
} | ||
|
||
public static String sql(String input) { | ||
return escapeTool.sql(input); | ||
} | ||
} |
3 changes: 3 additions & 0 deletions
3
dd-java-agent/instrumentation/velocity/src/test/resources/velocity-astreference-insecure.vm
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
<html> | ||
I am vulnerable $param | ||
</html> |
3 changes: 3 additions & 0 deletions
3
dd-java-agent/instrumentation/velocity/src/test/resources/velocity-astreference-secure.vm
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
<html> | ||
I am vulnerable $esc.html($param) | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
plugins { | ||
id 'java' | ||
id 'org.springframework.boot' version '2.7.15' | ||
id 'io.spring.dependency-management' version '1.0.15.RELEASE' | ||
id 'java-test-fixtures' | ||
} | ||
|
||
apply from: "$rootDir/gradle/java.gradle" | ||
description = 'SpringBoot Velocity Smoke Tests.' | ||
|
||
dependencies { | ||
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: '1.5.18.RELEASE' | ||
implementation group: 'org.apache.velocity', name: 'velocity', version: '1.5' | ||
implementation(group: 'org.apache.velocity', name: 'velocity-tools', version: '1.3') { | ||
exclude group: 'javax.servlet', module: 'servlet-api' | ||
} | ||
|
||
testImplementation project(':dd-smoke-tests') | ||
testImplementation(testFixtures(project(":dd-smoke-tests:iast-util"))) | ||
} | ||
|
||
tasks.withType(Test).configureEach { | ||
dependsOn "bootJar" | ||
jvmArgs "-Ddatadog.smoketest.springboot.shadowJar.path=${tasks.bootJar.archiveFile.get()}" | ||
} |
11 changes: 11 additions & 0 deletions
11
...springboot-velocity/src/main/java/datadog/smoketest/springboot/SpringbootApplication.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package datadog.smoketest.springboot; | ||
|
||
import org.springframework.boot.SpringApplication; | ||
import org.springframework.boot.autoconfigure.SpringBootApplication; | ||
|
||
@SpringBootApplication | ||
public class SpringbootApplication { | ||
public static void main(String[] args) { | ||
SpringApplication.run(SpringbootApplication.class, args); | ||
} | ||
} |
42 changes: 42 additions & 0 deletions
42
...e-tests/springboot-velocity/src/main/java/datadog/smoketest/springboot/XssController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package datadog.smoketest.springboot; | ||
|
||
import javax.servlet.http.HttpServletResponse; | ||
import org.apache.velocity.Template; | ||
import org.apache.velocity.VelocityContext; | ||
import org.apache.velocity.app.VelocityEngine; | ||
import org.apache.velocity.runtime.RuntimeConstants; | ||
import org.apache.velocity.tools.generic.EscapeTool; | ||
import org.springframework.stereotype.Controller; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RequestParam; | ||
|
||
@Controller | ||
@RequestMapping("/xss") | ||
public class XssController { | ||
|
||
private static final String DIRECTORY_TEMPLATES_TEST = "resources/main/templates/"; | ||
private static final String DIRECTORY_TEMPLATES_RUN = | ||
"dd-smoke-tests/springboot-velocity/src/main/resources/templates/"; | ||
|
||
@GetMapping("/velocity") | ||
public void xssVelocity( | ||
@RequestParam("velocity") String param, | ||
@RequestParam("templateName") String templateName, | ||
HttpServletResponse response) | ||
throws Exception { | ||
VelocityEngine velocity = new VelocityEngine(); | ||
// To avoid the creation of a Velocity log file | ||
velocity.setProperty( | ||
RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS, | ||
"org.apache.velocity.runtime.log.NullLogChute"); | ||
velocity.init(); | ||
Template template = velocity.getTemplate(DIRECTORY_TEMPLATES_TEST + templateName); | ||
|
||
VelocityContext context = new VelocityContext(); | ||
context.put("esc", new EscapeTool()); | ||
context.put("param", param); | ||
|
||
template.merge(context, response.getWriter()); | ||
} | ||
} |
3 changes: 3 additions & 0 deletions
3
dd-smoke-tests/springboot-velocity/src/main/resources/templates/velocity-insecure.vm
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
<html> | ||
Is this vulnerable? $param | ||
</html> |
3 changes: 3 additions & 0 deletions
3
dd-smoke-tests/springboot-velocity/src/main/resources/templates/velocity-secure.vm
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
<html> | ||
Is this vulnerable? $esc.html($param) | ||
</html> |
Oops, something went wrong.