Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Removed scala converter lambdas and ensure they are added as helpers #7168

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 '1.9.24'
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"
}
}
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