Skip to content

Commit

Permalink
Removed scala converter lambdas and ensure they are added as helpers (#…
Browse files Browse the repository at this point in the history
…7146)

Removed scala converter lambdas and ensure they are added as helpers

(cherry picked from commit 5294fac)
  • Loading branch information
manuel-alvarez-alvarez committed Jun 12, 2024
1 parent 97065ed commit 45988c8
Show file tree
Hide file tree
Showing 12 changed files with 280 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,7 @@
public class ScalaJavaConverters {

public static <E> Iterable<E> toIterable(@Nonnull final scala.collection.Iterable<E> iterable) {
scala.collection.Iterator<E> iterator = iterable.iterator();
return () ->
new Iterator<E>() {
@Override
public boolean hasNext() {
return iterator.hasNext();
}

@Override
public E next() {
return iterator.next();
}
};
return new JavaIterable<>(iterable);
}

public static <E> Object[] toArray(@Nullable final scala.collection.Iterable<E> iterable) {
Expand All @@ -34,4 +22,38 @@ public static <E> Object[] toArray(@Nullable final scala.collection.Iterable<E>
}
return array;
}

public static class JavaIterable<E> implements Iterable<E> {

private final scala.collection.Iterable<E> iterable;

private JavaIterable(final scala.collection.Iterable<E> iterable) {
this.iterable = iterable;
}

@Override
@Nonnull
public Iterator<E> iterator() {
return new JavaIterator<>(iterable.iterator());
}
}

public static class JavaIterator<E> implements Iterator<E> {

private final scala.collection.Iterator<E> iterator;

private JavaIterator(final scala.collection.Iterator<E> iterator) {
this.iterator = iterator;
}

@Override
public boolean hasNext() {
return iterator.hasNext();
}

@Override
public E next() {
return iterator.next();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@
import scala.collection.Seq;

@Propagation
@CallSite(spi = IastCallSites.class, helpers = ScalaJavaConverters.class)
@CallSite(
spi = IastCallSites.class,
helpers = {
ScalaJavaConverters.class,
ScalaJavaConverters.JavaIterable.class,
ScalaJavaConverters.JavaIterator.class
})
public class StringContextCallSite {

@CallSite.After("java.lang.String scala.StringContext.s(scala.collection.Seq)")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@
import scala.collection.immutable.StringOps;

@Propagation
@CallSite(spi = IastCallSites.class, helpers = ScalaJavaConverters.class)
@CallSite(
spi = IastCallSites.class,
helpers = {
ScalaJavaConverters.class,
ScalaJavaConverters.JavaIterable.class,
ScalaJavaConverters.JavaIterator.class
})
public class StringOpsCallSite {

@CallSite.After(
Expand Down
38 changes: 38 additions & 0 deletions dd-smoke-tests/iast-propagation/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
plugins {
id 'com.github.johnrengelman.shadow'
id 'java'
id 'org.jetbrains.kotlin.jvm' version '2.0.0'
id 'scala'
id 'groovy'
}

apply from: "$rootDir/gradle/java.gradle"
description = 'IAST propagation Smoke Tests.'

// The standard spring-boot plugin doesn't play nice with our project
// so we'll build a fat jar instead
jar {
manifest {
attributes('Main-Class': 'datadog.smoketest.springboot.SpringbootApplication')
}
}

shadowJar {
configurations = [project.configurations.runtimeClasspath]
}

dependencies {
implementation project(':dd-trace-api')
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: '2.5.4'
implementation deps.scala
implementation deps.groovy
implementation deps.kotlin

testImplementation project(':dd-smoke-tests')
testImplementation(testFixtures(project(":dd-smoke-tests:iast-util")))
}

tasks.withType(Test).configureEach {
dependsOn "shadowJar"
jvmArgs "-Ddatadog.smoketest.springboot.shadowJar.path=${tasks.shadowJar.archiveFile.get()}"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import java.util.function.Supplier

class GroovyPropagation implements Supplier<List<String>> {

@Override
List<String> get() {
return ["plus", "interpolation"]
}

String plus(String tainted) {
return tainted + tainted
}


String interpolation(String tainted) {
return "interpolation: $tainted"
}
}
19 changes: 19 additions & 0 deletions dd-smoke-tests/iast-propagation/src/main/java/JavaPropagation.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import java.util.Arrays;
import java.util.List;
import java.util.function.Supplier;

public class JavaPropagation implements Supplier<List<String>> {

@Override
public List<String> get() {
return Arrays.asList("plus", "concat");
}

public String plus(String tainted) {
return tainted + tainted;
}

public String concat(String tainted) {
return tainted.concat(tainted);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package datadog.smoketest.springboot;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class PropagationController {

private final Map<String, Supplier<List<String>>> suppliers = new ConcurrentHashMap<>();

@GetMapping("/{language}")
public List<String> propagation(@PathVariable("language") final String language) {
return supplierForLanguage(language).get();
}

@GetMapping("/{language}/{method}")
public String propagation(
@PathVariable("language") final String language,
@PathVariable("method") final String method,
@RequestParam("param") final String param) {
return invokeMethod(supplierForLanguage(language), method, param);
}

@SuppressWarnings("unchecked")
private Supplier<List<String>> supplierForLanguage(final String language) {
return suppliers.computeIfAbsent(
language,
l -> {
try {
String name = Character.toUpperCase(l.charAt(0)) + l.substring(1) + "Propagation";
Class<?> instance = Thread.currentThread().getContextClassLoader().loadClass(name);
return (Supplier<List<String>>) instance.newInstance();
} catch (final Exception e) {
throw new RuntimeException(e);
}
});
}

private String invokeMethod(
final Supplier<List<String>> supplier, final String name, final String param) {
try {
final Method method = supplier.getClass().getDeclaredMethod(name, String.class);
return (String) method.invoke(supplier, param);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package datadog.smoketest.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringbootApplication {

public static void main(final String[] args) {
SpringApplication.run(SpringbootApplication.class, args);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import java.util.function.Supplier

class KotlinPropagation : Supplier<List<String>> {

override fun get(): List<String> = listOf("plus", "interpolation")

fun plus(param: String): String = param + param

fun interpolation(param: String): String = "Interpolation $param"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import java.util
import java.util.function.Supplier
import scala.collection.JavaConverters._

class ScalaPropagation extends Supplier[util.List[String]] {

override def get(): util.List[String] = List("plus", "s", "f", "raw").asJava

def plus(param: String): String = param + param

def s(param: String): String = s"s interpolation: $param"

def f(param: String): String = f"f interpolation: $param"

def raw(param: String): String = raw"raw interpolation: $param"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package datadog.smoketest

import groovy.json.JsonSlurper
import okhttp3.Request
import org.junit.Assume

import static datadog.trace.api.config.IastConfig.IAST_DEBUG_ENABLED
import static datadog.trace.api.config.IastConfig.IAST_DETECTION_MODE
import static datadog.trace.api.config.IastConfig.IAST_ENABLED

class IastPropagationSmokeTest extends AbstractIastServerSmokeTest {

@Override
ProcessBuilder createProcessBuilder() {
final jarPath = System.getProperty('datadog.smoketest.springboot.shadowJar.path')
List<String> command = []
command.add(javaPath())
command.addAll(defaultJavaProperties)
command.addAll((String[]) [
withSystemProperty(IAST_ENABLED, true),
withSystemProperty(IAST_DETECTION_MODE, 'FULL'),
withSystemProperty(IAST_DEBUG_ENABLED, true),
'-jar',
jarPath,
"--server.port=${httpPort}"
])
final processBuilder = new ProcessBuilder(command)
processBuilder.directory(new File(buildDirectory))
return processBuilder
}

void 'test propagation language=#language, method=#method'() {
setup:
// TODO fix when we have groovy default string propagation
Assume.assumeTrue(language != 'groovy')
String param = "${language}_${method}"
String url = "http://localhost:${httpPort}/${language}/${method}?param=$param"

when:
def request = new Request.Builder().url(url).get().build()
def response = client.newCall(request).execute()

then:
def responseBodyStr = response.body().string()
response.code() == 200
hasTainted { tainted ->
tainted.value == responseBodyStr &&
tainted.ranges[0].source.origin == 'http.request.parameter'
}

where:
[language, method] << methods()
}

private List<List<String>> methods() {
final languages = ['java', 'scala', 'groovy', 'kotlin']
return languages.collectMany { language ->
final methods = new JsonSlurper().parse(new URL("http://localhost:${httpPort}/${language}")) as List<String>
return methods.collect { method -> [language, method] }
}
}
}
1 change: 1 addition & 0 deletions settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ include ':dd-smoke-tests:appsec:springboot-graphql'
include ':dd-smoke-tests:appsec:springboot-security'
include ':dd-smoke-tests:debugger-integration-tests'
include ':dd-smoke-tests:datastreams:kafkaschemaregistry'
include ':dd-smoke-tests:iast-propagation'
include ':dd-smoke-tests:iast-util'
// TODO this fails too often with a jgit failure, so disable until fixed
//include ':dd-smoke-tests:debugger-integration-tests:latest-jdk-app'
Expand Down

0 comments on commit 45988c8

Please sign in to comment.