Skip to content

Commit

Permalink
Merge pull request #5855 from DataDog/glopes/akka-appsec
Browse files Browse the repository at this point in the history
ASM WAF support for Akka HTTP
  • Loading branch information
cataphract committed Oct 2, 2023
2 parents 4152f60 + a392fd7 commit d4ca13f
Show file tree
Hide file tree
Showing 31 changed files with 2,123 additions and 131 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,9 @@
2 akka.http.scaladsl.*
0 akka.http.scaladsl.Http2Ext
0 akka.http.scaladsl.HttpExt
0 akka.http.scaladsl.server.ExceptionHandler$
0 akka.http.scaladsl.common.StrictForm$
0 akka.http.impl.engine.server.HttpServerBluePrint$ControllerStage$$anon$*
# saves ~0.1s skipping ~407 classes
2 akka.stream.*
0 akka.stream.impl.FanIn$SubInput
Expand All @@ -133,6 +136,8 @@
0 akka.http.javadsl.model.HttpHeader
0 akka.http.scaladsl.model.HttpRequest
0 akka.http.scaladsl.model.headers.*
0 akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport
0 akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport$class
0 akka.http.scaladsl.unmarshalling.*
0 akka.http.scaladsl.server.PathMatcher$Matched
0 akka.http.scaladsl.server.directives.ParameterDirectives$class
Expand All @@ -142,6 +147,7 @@
0 akka.http.scaladsl.server.directives.FormFieldDirectives$class
0 akka.http.scaladsl.server.directives.FormFieldDirectives
0 akka.http.scaladsl.model.Uri
0 akka.http.scaladsl.model.Multipart$FormData
0 akka.http.scaladsl.model.FormData
0 akka.http.scaladsl.server.RequestContextImpl
0 akka.http.scaladsl.server.directives.CookieDirectives$class
Expand Down
50 changes: 34 additions & 16 deletions dd-java-agent/instrumentation/akka-http-10.0/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ apply plugin: 'call-site-instrumentation'
// we put the test classes in the baseTest test set so that the scala
// version is not inherited
addTestSuite('baseTest')
addTestSuiteExtendingForDir('baseForkedTest', 'baseTest', 'baseTest')
addTestSuiteForDir('version101Test', 'baseTest')
addTestSuiteExtendingForDir('version101ForkedTest', 'version101Test', 'baseTest')
addTestSuiteForDir('version102Scala213Test', 'latestDepTest')
addTestSuite('latestDepTest')
addTestSuite('lagomTest')
addTestSuite('iastTest')
Expand Down Expand Up @@ -87,6 +90,9 @@ artifacts {
}

sourceSets {
version102Scala213Test.groovy.srcDir sourceSets.baseTest.groovy
version102Scala213Test.scala.srcDir sourceSets.baseTest.scala

latestDepTest.groovy.srcDir sourceSets.baseTest.groovy
latestDepTest.scala.srcDir sourceSets.baseTest.scala
}
Expand All @@ -107,8 +113,9 @@ dependencies {
// First 10.0.x version with a convenient way to test http2 support
baseTestImplementation group: 'com.typesafe.akka', name: 'akka-http_2.11', version: '10.0.10'
baseTestImplementation group: 'com.typesafe.akka', name: 'akka-http2-support_2.11', version: '10.0.10'
baseTestImplementation group: 'com.typesafe.akka', name: 'akka-http-jackson_2.11', version: '10.0.10'
baseTestImplementation group: 'com.typesafe.akka', name: 'akka-http-spray-json_2.11', version: '10.0.10'

iastTestImplementation group: 'com.typesafe.akka', name: 'akka-http-jackson_2.11', version: '10.0.10'
iastTestImplementation(testFixtures(project(':dd-java-agent:agent-iast')))
iastTestCompileOnly group: 'de.thetaphi', name: 'forbiddenapis', version: '3.4'
iastTestRuntimeOnly project(':dd-java-agent:instrumentation:jackson-core')
Expand All @@ -120,8 +127,9 @@ dependencies {
version101TestImplementation group: 'com.typesafe.akka', name: 'akka-http_2.12', version: '10.1.+'
version101TestImplementation group: 'com.typesafe.akka', name: 'akka-http2-support_2.12', version: '10.1.+'
version101TestImplementation group: 'com.typesafe.akka', name: 'akka-stream_2.12', version: '2.5.+'
version101TestImplementation group: 'com.typesafe.akka', name: 'akka-http-jackson_2.12', version: '10.1.+'
version101TestImplementation group: 'com.typesafe.akka', name: 'akka-http-spray-json_2.12', version: '10.1.+'

version101IastTestImplementation group: 'com.typesafe.akka', name: 'akka-http-jackson_2.12', version: '10.1.+'
version101IastTestImplementation(testFixtures(project(':dd-java-agent:agent-iast')))

version102IastTestImplementation deps.scala212
Expand All @@ -130,10 +138,18 @@ dependencies {
version102IastTestImplementation group: 'com.typesafe.akka', name: 'akka-http-jackson_2.12', version: '10.2.+'
version102IastTestImplementation(testFixtures(project(':dd-java-agent:agent-iast')))

version102Scala213TestImplementation deps.scala213
version102Scala213TestImplementation group: 'com.typesafe.akka', name: 'akka-http_2.13', version: '10.2.+'
version102Scala213TestImplementation group: 'com.typesafe.akka', name: 'akka-stream_2.13', version: '2.6.+'
version102Scala213TestImplementation group: 'com.typesafe.akka', name: 'akka-http-jackson_2.13', version: '10.2.+'
version102Scala213TestImplementation group: 'com.typesafe.akka', name: 'akka-http-spray-json_2.13', version: '10.2.+'

latestDepTestImplementation deps.scala213
latestDepTestImplementation group: 'com.typesafe.akka', name: 'akka-http_2.13', version: '10.2.+'
latestDepTestImplementation group: 'com.typesafe.akka', name: 'akka-http_2.13', version: '10.5.+'
// http2 support is included in akka-http since 10.2.x
latestDepTestImplementation group: 'com.typesafe.akka', name: 'akka-stream_2.13', version: '2.6.+'
latestDepTestImplementation group: 'com.typesafe.akka', name: 'akka-stream_2.13', version: '2.7.0'
latestDepTestImplementation group: 'com.typesafe.akka', name: 'akka-http-jackson_2.13', version: '10.5.+'
latestDepTestImplementation group: 'com.typesafe.akka', name: 'akka-http-spray-json_2.13', version: '10.5.+'

// TODO: test with Scala 3
latestDepIastTestImplementation deps.scala213
Expand All @@ -152,20 +168,12 @@ dependencies {
lagomTestImplementation group: 'com.lightbend.lagom', name: 'lagom-javadsl-testkit_2.11', version: '1.4.0'
}

tasks.named("test").configure {
dependsOn "baseTest"
dependsOn "version101Test"
dependsOn "lagomTest"
dependsOn "iastTest"
dependsOn "version101IastTest"
dependsOn "version102IastTest"
}

tasks.named('latestDepTest').configure {
dependsOn "latestDepIastTest"
compileBaseTestGroovy {
classpath = classpath.plus(files(compileBaseTestScala.destinationDirectory))
dependsOn "compileBaseTestScala"
}

compileBaseTestGroovy {
compileBaseForkedTestGroovy {
classpath = classpath.plus(files(compileBaseTestScala.destinationDirectory))
dependsOn "compileBaseTestScala"
}
Expand All @@ -175,6 +183,16 @@ compileVersion101TestGroovy {
dependsOn "compileVersion101TestScala"
}

compileVersion101ForkedTestGroovy {
classpath = classpath.plus(files(compileVersion101TestScala.destinationDirectory))
dependsOn "compileVersion101TestScala"
}

compileVersion102Scala213TestGroovy {
classpath = classpath.plus(files(compileVersion102Scala213TestScala.destinationDirectory))
dependsOn "compileVersion102Scala213TestScala"
}

compileLatestDepTestGroovy {
classpath = classpath.plus(files(compileLatestDepTestScala.destinationDirectory))
dependsOn "compileLatestDepTestScala"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,21 +88,18 @@ abstract class AkkaHttpClientInstrumentationTest extends HttpClientTest {
span {
parent()
operationName operation()
resourceName "akka-http.client.request"
resourceName operation() // resource name is not set so defaults to operationName
spanType DDSpanTypes.HTTP_CLIENT
errored true
tags {
"$Tags.COMPONENT" "akka-http-client"
"$Tags.SPAN_KIND" Tags.SPAN_KIND_CLIENT
errorTags(exception)
defaultTags()
defaultTags(false, false)
}
}
}
}

where:
renameService << [false, true]
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,18 @@ import datadog.trace.agent.test.base.HttpServerTest
import datadog.trace.agent.test.naming.TestingGenericHttpNamingConventions
import datadog.trace.agent.test.utils.ThreadUtils
import datadog.trace.instrumentation.akkahttp.AkkaHttpServerDecorator
import okhttp3.HttpUrl
import okhttp3.MultipartBody
import okhttp3.Request
import okhttp3.RequestBody
import spock.lang.Shared

import java.util.concurrent.atomic.AtomicInteger

import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.BODY_JSON
import static datadog.trace.agent.test.base.HttpServerTest.ServerEndpoint.BODY_MULTIPART
import static org.junit.Assume.assumeTrue

abstract class AkkaHttpServerInstrumentationTest extends HttpServerTest<AkkaHttpTestWebServer> {

@Override
Expand Down Expand Up @@ -35,11 +42,51 @@ abstract class AkkaHttpServerInstrumentationTest extends HttpServerTest<AkkaHttp
return true
}

@Override
protected boolean enabledFinishTimingChecks() {
true
}

@Override
boolean changesAll404s() {
true
}

@Override
boolean testBlocking() {
true
}

@Override
boolean testBlockingOnResponse() {
true
}

@Override
boolean testRequestBody() {
true
}

@Override
boolean testBodyUrlencoded() {
true
}

@Override
boolean testBodyMultipart() {
true
}

@Override
boolean testBodyJson() {
true
}

@Override
boolean isRequestBodyNoStreaming() {
true
}

//@Ignore("https://github.com/DataDog/dd-trace-java/pull/5213")
@Override
boolean testBadUrl() {
Expand Down Expand Up @@ -73,6 +120,63 @@ abstract class AkkaHttpServerInstrumentationTest extends HttpServerTest<AkkaHttp
and:
TEST_WRITER.waitForTraces(totalInvocations)
}

def 'test instrumentation gateway multipart request body — strict variant'() {
setup:
assumeTrue(testBodyMultipart())
def body = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart('a', 'x')
.build()

def url = HttpUrl.get(BODY_MULTIPART.resolve(address)).newBuilder()
.encodedQuery('variant=strictUnmarshaller')
.build()
def request = new Request.Builder()
.url(url)
.method('POST', body)
.build()
def response = client.newCall(request).execute()
if (isDataStreamsEnabled()) {
TEST_DATA_STREAMS_WRITER.waitForGroups(1)
}

expect:
response.body().charStream().text == '[a:[x]]'

when:
TEST_WRITER.waitForTraces(1)

then:
TEST_WRITER.get(0).any {
it.getTag('request.body.converted') == '[a:[x]]'
}
}

def 'test instrumentation gateway json request body — spray variant'() {
assumeTrue(testBodyJson())

setup:
def url = HttpUrl.get(BODY_JSON.resolve(address)).newBuilder()
.encodedQuery('variant=spray')
.build()
def request = new Request.Builder()
.url(url)
.method('POST', RequestBody.create(okhttp3.MediaType.get('application/json'), '{"a":"x"}\n'))
.build()
def response = client.newCall(request).execute()

expect:
response.body().charStream().text == '{"a":"x"}'

when:
TEST_WRITER.waitForTraces(1)

then:
TEST_WRITER.get(0).any {
it.getTag('request.body.converted') == '[a:[x]]'
}
}
}

abstract class AkkaHttpServerInstrumentationSyncTest extends AkkaHttpServerInstrumentationTest {
Expand All @@ -85,6 +189,27 @@ abstract class AkkaHttpServerInstrumentationSyncTest extends AkkaHttpServerInstr
String expectedOperationName() {
return operation()
}

// we test body endpoints only on the async tests
@Override
boolean testRequestBody() {
false
}

@Override
boolean testBodyMultipart() {
false
}

@Override
boolean testBodyJson() {
false
}

@Override
boolean testBodyUrlencoded() {
false
}
}

class AkkaHttpServerInstrumentationSyncV0ForkedTest extends AkkaHttpServerInstrumentationSyncTest {
Expand Down Expand Up @@ -115,27 +240,57 @@ class AkkaHttpServerInstrumentationAsyncTest extends AkkaHttpServerInstrumentati
}

class AkkaHttpServerInstrumentationBindAndHandleTest extends AkkaHttpServerInstrumentationTest {
String akkaHttpVersion

@Override
HttpServer server() {
return new AkkaHttpTestWebServer(AkkaHttpTestWebServer.BindAndHandle())
AkkaHttpTestWebServer server = new AkkaHttpTestWebServer(AkkaHttpTestWebServer.BindAndHandle())
akkaHttpVersion = server.system().settings().config().getString('akka.http.version')
server
}

@Override
boolean redirectHasBody() {
return true
}

// StrictForm marshaller rejects with providing a Rejection
// We can't detect the BlockingException as a consequence
@Override
boolean testBodyMultipart() {
akkaHttpVersion != '10.0.10'
}

@Override
boolean testBodyUrlencoded() {
akkaHttpVersion != '10.0.10'
}
}

class AkkaHttpServerInstrumentationBindAndHandleAsyncWithRouteAsyncHandlerTest extends AkkaHttpServerInstrumentationTest {
String akkaHttpVersion

@Override
HttpServer server() {
return new AkkaHttpTestWebServer(AkkaHttpTestWebServer.BindAndHandleAsyncWithRouteAsyncHandler())
AkkaHttpTestWebServer server = new AkkaHttpTestWebServer(AkkaHttpTestWebServer.BindAndHandleAsyncWithRouteAsyncHandler())
akkaHttpVersion = server.system().settings().config().getString('akka.http.version')
server
}

@Override
boolean redirectHasBody() {
return true
}

@Override
boolean testBodyMultipart() {
akkaHttpVersion != '10.0.10'
}

@Override
boolean testBodyUrlencoded() {
akkaHttpVersion != '10.0.10'
}
}

class AkkaHttpServerInstrumentationAsyncHttp2Test extends AkkaHttpServerInstrumentationTest {
Expand Down
Loading

0 comments on commit d4ca13f

Please sign in to comment.