diff --git a/.github/workflows/run-end-to-end.yml b/.github/workflows/run-end-to-end.yml
index a9b7c4a8bf4..0d8ba7b0333 100644
--- a/.github/workflows/run-end-to-end.yml
+++ b/.github/workflows/run-end-to-end.yml
@@ -529,6 +529,9 @@ jobs:
- name: Run APPSEC_LAMBDA_API_SECURITY scenario
if: steps.build.outcome == 'success' && !cancelled() && contains(inputs.scenarios, '"APPSEC_LAMBDA_API_SECURITY"')
run: ./run.sh APPSEC_LAMBDA_API_SECURITY
+ - name: Run APPSEC_LAMBDA_SCA scenario
+ if: steps.build.outcome == 'success' && !cancelled() && contains(inputs.scenarios, '"APPSEC_LAMBDA_SCA"')
+ run: ./run.sh APPSEC_LAMBDA_SCA
- name: Run APPSEC_LAMBDA_RASP scenario
if: steps.build.outcome == 'success' && !cancelled() && contains(inputs.scenarios, '"APPSEC_LAMBDA_RASP"')
run: ./run.sh APPSEC_LAMBDA_RASP
diff --git a/docs/understand/weblogs/end-to-end_weblog.md b/docs/understand/weblogs/end-to-end_weblog.md
index 23fa0d6127b..55c4244e6ee 100644
--- a/docs/understand/weblogs/end-to-end_weblog.md
+++ b/docs/understand/weblogs/end-to-end_weblog.md
@@ -887,6 +887,8 @@ Alternate call site for the same vulnerable function targeted by `/sca/vulnerabl
test first-hit-wins deduplication: when the same CVE is triggered from two different call sites, only the
first occurrence is reported in the `reached` array.
+The Java Lambda weblogs also implement both SCA endpoints, using Junrar 7.5.5 kept as a separate JAR. The vulnerable class is preloaded during handler initialization.
+
### GET /log/library
This endpoint facilitates logging a message using a logging library. It is primarily designed for testing log injection functionality. Weblog apps must log using JSON format.
diff --git a/manifests/java_lambda.yml b/manifests/java_lambda.yml
index adfe282c35d..4dc485300fa 100644
--- a/manifests/java_lambda.yml
+++ b/manifests/java_lambda.yml
@@ -132,6 +132,11 @@ manifest:
tests/appsec/test_reports.py::Test_TagsFromRule: v1.67.0
tests/appsec/test_reports.py::Test_TagsFromRule::test_category: v1.68.0
tests/appsec/test_reports.py::Test_TagsFromRule::test_type: v1.68.0
+ tests/appsec/test_sca_reachability.py::Test_SCA_Reachability_CVE_After_Vulnerable_Call: v1.68.0
+ tests/appsec/test_sca_reachability.py::Test_SCA_Reachability_CVE_Registered_At_Load_Time: v1.68.0
+ tests/appsec/test_sca_reachability.py::Test_SCA_Reachability_Deduplication: v1.68.0
+ tests/appsec/test_sca_reachability.py::Test_SCA_Reachability_Dependencies_Have_Metadata: v1.68.0
+ tests/appsec/test_sca_reachability.py::Test_SCA_Reachability_First_Hit_Wins: v1.68.0
tests/appsec/test_trace_tagging.py::Test_TraceTaggingRules: v1.67.0
tests/appsec/test_trace_tagging.py::Test_TraceTaggingRules::test_rule_with_attributes_keep_event: v1.68.0
tests/appsec/test_trace_tagging.py::Test_TraceTaggingRules::test_rule_with_attributes_keep_no_event: v1.68.0
diff --git a/manifests/nodejs_lambda.yml b/manifests/nodejs_lambda.yml
index 3b0b4007d34..7920240c596 100644
--- a/manifests/nodejs_lambda.yml
+++ b/manifests/nodejs_lambda.yml
@@ -139,6 +139,7 @@ manifest:
tests/appsec/test_reports.py::Test_RequestHeaders: '>=12.142.0' # TODO: a lower version might be supported
tests/appsec/test_reports.py::Test_StatusCode: missing_feature
tests/appsec/test_reports.py::Test_TagsFromRule: '>=12.142.0' # TODO: a lower version might be supported
+ tests/appsec/test_sca_reachability.py: missing_feature (SCA runtime reachability is not implemented)
tests/appsec/test_trace_tagging.py::Test_TraceTaggingRules: # TODO: a lower version might be supported
- weblog_declaration:
'*': missing_feature
diff --git a/manifests/python_lambda.yml b/manifests/python_lambda.yml
index f9e05136cd5..ef76c50362c 100644
--- a/manifests/python_lambda.yml
+++ b/manifests/python_lambda.yml
@@ -266,6 +266,7 @@ manifest:
tests/appsec/test_reports.py::Test_Info: v7.112.0
tests/appsec/test_reports.py::Test_RequestHeaders: v7.112.0
tests/appsec/test_reports.py::Test_StatusCode: v7.112.0
+ tests/appsec/test_sca_reachability.py: incomplete_test_app (Lambda SCA dependency fixtures and vulnerable-call endpoints are not implemented)
tests/appsec/test_trace_tagging.py::Test_TraceTaggingRules: v8.113.0
tests/appsec/test_traces.py::Test_AppSecEventSpanTags: v7.112.0
tests/appsec/test_traces.py::Test_AppSecEventSpanTags::test_header_collection: bug (APPSEC-58202)
diff --git a/manifests/ruby_lambda.yml b/manifests/ruby_lambda.yml
index cd183c66b17..8e805da030b 100644
--- a/manifests/ruby_lambda.yml
+++ b/manifests/ruby_lambda.yml
@@ -68,6 +68,7 @@ manifest:
tests/appsec/test_reports.py::Test_Info: v3.29.0
tests/appsec/test_reports.py::Test_RequestHeaders: v3.29.0
tests/appsec/test_reports.py::Test_StatusCode: v3.29.0
+ tests/appsec/test_sca_reachability.py: missing_feature (SCA runtime reachability is not implemented)
tests/appsec/test_trace_tagging.py::Test_TraceTaggingRules: v3.29.0
tests/appsec/test_trace_tagging.py::Test_TraceTaggingRules::test_rule_with_attributes_no_keep_event: v3.30.0
tests/appsec/test_trace_tagging.py::Test_TraceTaggingRules::test_rule_with_attributes_no_keep_no_event: v3.30.0
diff --git a/tests/appsec/test_sca_reachability.py b/tests/appsec/test_sca_reachability.py
index 04e1fdcf30b..aef5a54ef42 100644
--- a/tests/appsec/test_sca_reachability.py
+++ b/tests/appsec/test_sca_reachability.py
@@ -33,6 +33,12 @@
"path": "com.datadoghq.system_tests.springboot.ScaReachability",
"symbol": "junrarHit",
},
+ "java_lambda": {
+ "cve_id": ["GHSA-hf5p-q87m-crj7"],
+ "vulnerable_dep": "junrar",
+ "path": "com.datadoghq.lambda.ScaReachability",
+ "symbol": "junrarHit",
+ },
}
@@ -112,6 +118,7 @@ def _get_deps_with_reachability_metadata() -> set[str]:
@rfc(SCA_REACHABILITY_RFC)
@features.runtime_sca_reachability
@scenarios.runtime_sca_reachability
+@scenarios.appsec_lambda_sca
class Test_SCA_Reachability_Dependencies_Have_Metadata:
"""When DD_APPSEC_SCA_ENABLED=true, vulnerable dependencies have reachability metadata."""
@@ -129,6 +136,7 @@ def test_dependencies_have_metadata_key(self) -> None:
@rfc(SCA_REACHABILITY_RFC)
@features.runtime_sca_reachability
@scenarios.runtime_sca_reachability
+@scenarios.appsec_lambda_sca
class Test_SCA_Reachability_CVE_Registered_At_Load_Time:
"""CVEs are registered at startup with reached=[] before any vulnerable call."""
@@ -148,6 +156,7 @@ def test_cve_registered_at_load_time(self) -> None:
@rfc(SCA_REACHABILITY_RFC)
@features.runtime_sca_reachability
@scenarios.runtime_sca_reachability
+@scenarios.appsec_lambda_sca
class Test_SCA_Reachability_CVE_After_Vulnerable_Call:
"""Calling a vulnerable function reports CVE with caller info in reached array."""
@@ -156,6 +165,7 @@ def setup_cve_metadata_after_vulnerable_call(self) -> None:
self.r1 = weblog.get("/sca/vulnerable-call")
def test_cve_metadata_after_vulnerable_call(self) -> None:
+ assert self.r1.status_code == 200
cve_entries = _get_dependency_cve_metadata(_vulnerable_dep(), _cve_id())
assert len(cve_entries) >= 1, f"{_cve_id()} not found in {_vulnerable_dep()} metadata"
@@ -186,6 +196,7 @@ def test_cve_metadata_after_vulnerable_call(self) -> None:
@rfc(SCA_REACHABILITY_RFC)
@features.runtime_sca_reachability
@scenarios.runtime_sca_reachability
+@scenarios.appsec_lambda_sca
class Test_SCA_Reachability_First_Hit_Wins:
"""Two call sites for same CVE: only first hit is reported (max 1 reached per entry)."""
@@ -195,6 +206,8 @@ def setup_same_cve_first_hit_wins(self) -> None:
self.r2 = weblog.get("/sca/vulnerable-call-alt")
def test_same_cve_first_hit_wins(self) -> None:
+ assert self.r1.status_code == 200
+ assert self.r2.status_code == 200
cve_entries = _get_dependency_cve_metadata(_vulnerable_dep(), _cve_id())
assert len(cve_entries) >= 1, f"{_cve_id()} not found in {_vulnerable_dep()} metadata"
@@ -234,15 +247,17 @@ def test_same_cve_first_hit_wins(self) -> None:
@rfc(SCA_REACHABILITY_RFC)
@features.runtime_sca_reachability
@scenarios.runtime_sca_reachability
+@scenarios.appsec_lambda_sca
class Test_SCA_Reachability_Deduplication:
"""Repeated calls from same site for same CVE: still max 1 reached entry."""
def setup_deduplication_repeated_calls(self) -> None:
self.r0 = weblog.get("/")
- for _ in range(5):
- weblog.get("/sca/vulnerable-call")
+ self.responses = [weblog.get("/sca/vulnerable-call") for _ in range(5)]
def test_deduplication_repeated_calls(self) -> None:
+ for response in self.responses:
+ assert response.status_code == 200
cve_entries = _get_dependency_cve_metadata(_vulnerable_dep(), _cve_id())
assert len(cve_entries) >= 1, f"{_cve_id()} not found in {_vulnerable_dep()} metadata"
diff --git a/utils/_context/_scenarios/__init__.py b/utils/_context/_scenarios/__init__.py
index c79e6396e7a..d6c9620f57d 100644
--- a/utils/_context/_scenarios/__init__.py
+++ b/utils/_context/_scenarios/__init__.py
@@ -56,6 +56,7 @@
AppsecLambdaDefault,
AppsecLambdaInferredSpans,
AppsecLambdaRasp,
+ AppsecLambdaSca,
)
from .appsec_rasp import (
AppsecRasp,
@@ -399,6 +400,7 @@ class _Scenarios:
appsec_lambda_default = AppsecLambdaDefault()
appsec_lambda_blocking = AppsecLambdaBlocking()
appsec_lambda_api_security = AppsecLambdaApiSecurity()
+ appsec_lambda_sca = AppsecLambdaSca()
appsec_lambda_rasp = AppsecLambdaRasp()
appsec_lambda_inferred_spans = AppsecLambdaInferredSpans()
otel_collector = OtelCollectorScenario()
diff --git a/utils/_context/_scenarios/aws_lambda.py b/utils/_context/_scenarios/aws_lambda.py
index ce3e125ac56..30919185d00 100644
--- a/utils/_context/_scenarios/aws_lambda.py
+++ b/utils/_context/_scenarios/aws_lambda.py
@@ -211,6 +211,16 @@ def __init__(self) -> None:
)
+class AppsecLambdaSca(_LambdaScenario):
+ def __init__(self) -> None:
+ super().__init__(
+ "APPSEC_LAMBDA_SCA",
+ weblog_env={"DD_APPSEC_ENABLED": "false", "DD_APPSEC_SCA_ENABLED": "true"},
+ doc="SCA dependency metadata and runtime reachability in Lambda with AppSec disabled",
+ scenario_groups=[scenario_groups.appsec, scenario_groups.appsec_lambda],
+ )
+
+
class AppsecLambdaRasp(_LambdaScenario):
def __init__(self) -> None:
super().__init__(
diff --git a/utils/build/docker/java_lambda/java-alb-multi.Dockerfile b/utils/build/docker/java_lambda/java-alb-multi.Dockerfile
index 4d19c09b0b5..74685897f23 100644
--- a/utils/build/docker/java_lambda/java-alb-multi.Dockerfile
+++ b/utils/build/docker/java_lambda/java-alb-multi.Dockerfile
@@ -15,6 +15,7 @@ RUN /binaries/install_ddtrace.sh
# Copy built JAR and extract to Lambda task root
COPY --from=builder /build/target/java-lambda-weblog-*.jar /tmp/app.jar
RUN cd ${LAMBDA_TASK_ROOT} && jar xf /tmp/app.jar && rm /tmp/app.jar
+COPY --from=builder /build/target/sca-libs/*.jar ${LAMBDA_TASK_ROOT}/lib/
# Copy Datadog agent and entrypoint
RUN cp /dd-tracer/dd-java-agent.jar ${LAMBDA_TASK_ROOT}/dd-java-agent.jar
diff --git a/utils/build/docker/java_lambda/java-alb.Dockerfile b/utils/build/docker/java_lambda/java-alb.Dockerfile
index 8b7fadb89ad..14728d91b06 100644
--- a/utils/build/docker/java_lambda/java-alb.Dockerfile
+++ b/utils/build/docker/java_lambda/java-alb.Dockerfile
@@ -15,6 +15,7 @@ RUN /binaries/install_ddtrace.sh
# Copy built JAR and extract to Lambda task root
COPY --from=builder /build/target/java-lambda-weblog-*.jar /tmp/app.jar
RUN cd ${LAMBDA_TASK_ROOT} && jar xf /tmp/app.jar && rm /tmp/app.jar
+COPY --from=builder /build/target/sca-libs/*.jar ${LAMBDA_TASK_ROOT}/lib/
# Copy Datadog agent and entrypoint
RUN cp /dd-tracer/dd-java-agent.jar ${LAMBDA_TASK_ROOT}/dd-java-agent.jar
diff --git a/utils/build/docker/java_lambda/java-apigw-http.Dockerfile b/utils/build/docker/java_lambda/java-apigw-http.Dockerfile
index 8a3913f234e..0b92cb657cc 100644
--- a/utils/build/docker/java_lambda/java-apigw-http.Dockerfile
+++ b/utils/build/docker/java_lambda/java-apigw-http.Dockerfile
@@ -15,6 +15,7 @@ RUN /binaries/install_ddtrace.sh
# Copy built JAR and extract to Lambda task root
COPY --from=builder /build/target/java-lambda-weblog-*.jar /tmp/app.jar
RUN cd ${LAMBDA_TASK_ROOT} && jar xf /tmp/app.jar && rm /tmp/app.jar
+COPY --from=builder /build/target/sca-libs/*.jar ${LAMBDA_TASK_ROOT}/lib/
# Copy Datadog agent and entrypoint
RUN cp /dd-tracer/dd-java-agent.jar ${LAMBDA_TASK_ROOT}/dd-java-agent.jar
diff --git a/utils/build/docker/java_lambda/java-apigw-rest.Dockerfile b/utils/build/docker/java_lambda/java-apigw-rest.Dockerfile
index d91b15ad8e0..2719e780e3f 100644
--- a/utils/build/docker/java_lambda/java-apigw-rest.Dockerfile
+++ b/utils/build/docker/java_lambda/java-apigw-rest.Dockerfile
@@ -15,6 +15,7 @@ RUN /binaries/install_ddtrace.sh
# Copy built JAR and extract to Lambda task root
COPY --from=builder /build/target/java-lambda-weblog-*.jar /tmp/app.jar
RUN cd ${LAMBDA_TASK_ROOT} && jar xf /tmp/app.jar && rm /tmp/app.jar
+COPY --from=builder /build/target/sca-libs/*.jar ${LAMBDA_TASK_ROOT}/lib/
# Copy Datadog agent and entrypoint
RUN cp /dd-tracer/dd-java-agent.jar ${LAMBDA_TASK_ROOT}/dd-java-agent.jar
diff --git a/utils/build/docker/java_lambda/java-function-url.Dockerfile b/utils/build/docker/java_lambda/java-function-url.Dockerfile
index 6ac4014575f..e9b31e0ef99 100644
--- a/utils/build/docker/java_lambda/java-function-url.Dockerfile
+++ b/utils/build/docker/java_lambda/java-function-url.Dockerfile
@@ -15,6 +15,7 @@ RUN /binaries/install_ddtrace.sh
# Copy built JAR and extract to Lambda task root
COPY --from=builder /build/target/java-lambda-weblog-*.jar /tmp/app.jar
RUN cd ${LAMBDA_TASK_ROOT} && jar xf /tmp/app.jar && rm /tmp/app.jar
+COPY --from=builder /build/target/sca-libs/*.jar ${LAMBDA_TASK_ROOT}/lib/
# Copy Datadog agent and entrypoint
RUN cp /dd-tracer/dd-java-agent.jar ${LAMBDA_TASK_ROOT}/dd-java-agent.jar
diff --git a/utils/build/docker/java_lambda/pom.xml b/utils/build/docker/java_lambda/pom.xml
index 4d9ee8f3916..a15bab66620 100644
--- a/utils/build/docker/java_lambda/pom.xml
+++ b/utils/build/docker/java_lambda/pom.xml
@@ -93,16 +93,44 @@
sqlite-jdbc
3.47.1.0
+
+
+ com.github.junrar
+ junrar
+ 7.5.5
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+ 3.8.1
+
+
+ copy-sca-dependency
+ package
+ copy-dependencies
+
+ com.github.junrar
+ junrar
+ ${project.build.directory}/sca-libs
+
+
+
+
org.apache.maven.plugins
maven-shade-plugin
3.2.4
false
+
+
+ com.github.junrar:junrar
+
+
diff --git a/utils/build/docker/java_lambda/src/main/java/com/datadoghq/lambda/Handler.java b/utils/build/docker/java_lambda/src/main/java/com/datadoghq/lambda/Handler.java
index f93bef3e5e6..da78f1a1380 100644
--- a/utils/build/docker/java_lambda/src/main/java/com/datadoghq/lambda/Handler.java
+++ b/utils/build/docker/java_lambda/src/main/java/com/datadoghq/lambda/Handler.java
@@ -53,6 +53,10 @@ public class Handler implements RequestHandler