diff --git a/Dockerfile b/Dockerfile index 6f8c2d60..78b0d31e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,7 +20,7 @@ RUN set -eux; \ if [ -z "${git_ref:-}" ]; then \ # NOTE: datadog gem must be >= 2.24 to install on Ruby 4.0.x. MAKEFLAGS="-j$(nproc)" \ - gem install datadog -v 2.34 --install-dir "/opt/ruby/gems/$runtime" --no-document; \ + gem install datadog -v 2.39 --install-dir "/opt/ruby/gems/$runtime" --no-document; \ else \ echo "building tracer from ref: $git_ref\n"; \ git clone https://github.com/DataDog/dd-trace-rb.git --depth 1 --single-branch -b $git_ref /tmp/dd-trace-rb; \ @@ -49,7 +49,7 @@ RUN rm -rf ./ruby/gems/$runtime/gems/aws*/ # Remove binaries not needed in AWS Lambda RUN find . -name '*linux-musl*' -prune -exec rm -rf {} + -# Cache files zipped gem files, that aren't used by during runtime, only during +# Cache files zipped gem files, that aren't used by during runtime, only during # installation, so they are safe to delete RUN rm -rf "/opt/ruby/gems/${runtime}/cache" RUN cd /opt diff --git a/datadog-lambda.gemspec b/datadog-lambda.gemspec index 5a937cda..d757ab97 100644 --- a/datadog-lambda.gemspec +++ b/datadog-lambda.gemspec @@ -35,9 +35,9 @@ Gem::Specification.new do |spec| # We don't add this as a direct dependency, because it has # native modules that are difficult to package for lambda. - # NOTE: 2.24 is the floor for Ruby 4.0 install support; bumped to 2.30 - # to match the version pinned in the layer Dockerfile. - spec.add_development_dependency 'datadog', '~> 2.30' + # + # NOTE: 2.24 is the floor for Ruby 4.0 install support + spec.add_development_dependency 'datadog', '~> 2.39' # Development dependencies # NOTE: rake >= 13 is required for Ruby 4.0 support — rake 12.x pulls in diff --git a/integration_tests/snapshots/logs/appsec-request_ruby32.log b/integration_tests/snapshots/logs/appsec-request_ruby32.log index 9e4c5ffb..40b62eb2 100644 --- a/integration_tests/snapshots/logs/appsec-request_ruby32.log +++ b/integration_tests/snapshots/logs/appsec-request_ruby32.log @@ -3,7 +3,7 @@ END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB -I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - CORE - {"date":"XXXX","os_name":"XXXX","version":"2.34.0","lang":"ruby","lang_version":"3.2.X","env":null,"service":"index","dd_version":null,"debug":false,"tags":"_dd.origin:lambda","runtime_metrics_enabled":false,"vm":"ruby-3.2.X","health_metrics_enabled":false,"profiling_enabled":false,"dynamic_instrumentation_enabled":false} +I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - CORE - {"date":"XXXX","os_name":"XXXX","version":"2.39.0","lang":"ruby","lang_version":"3.2.X","env":null,"service":"index","dd_version":null,"debug":false,"tags":"_dd.origin:lambda","runtime_metrics_enabled":false,"vm":"ruby-3.2.X","health_metrics_enabled":false,"profiling_enabled":false,"dynamic_instrumentation_enabled":false} I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - TRACING - {"enabled":true,"agent_url":null,"analytics_enabled":false,"sample_rate":null,"sampling_rules":null,"integrations_loaded":"aws@","partial_flushing_enabled":false} START START @@ -24,5 +24,5 @@ W, [XXXX] WARN XXXX[datadog] Unable to patch Datadog::Tracing::Contrib::Aws::In {"traces":[[{"error":0,"meta":{"XXXX": "XXXX"},"metrics":{"XXXX": "XXXX"},"meta_struct":{},"name":"aws.lambda","parent_id":"XXXX","resource":"dd-tracer-serverless-span","service":"aws.lambda","span_id":"XXXX","trace_id":"XXXX","type":"serverless","span_links":[],"start":XXXX,"duration":XXXX}]]} {"traces":[[{"error":0,"meta":{"XXXX": "XXXX"},"metrics":{"XXXX": "XXXX"},"meta_struct":{},"name":"aws.lambda","parent_id":"XXXX","resource":"dd-tracer-serverless-span","service":"aws.lambda","span_id":"XXXX","trace_id":"XXXX","type":"serverless","span_links":[],"start":XXXX,"duration":XXXX}]]} {"traces":[[{"error":0,"meta":{"XXXX": "XXXX"},"metrics":{"XXXX": "XXXX"},"meta_struct":{},"name":"aws.lambda","parent_id":"XXXX","resource":"dd-tracer-serverless-span","service":"aws.lambda","span_id":"XXXX","trace_id":"XXXX","type":"serverless","span_links":[],"start":XXXX,"duration":XXXX}]]} -{"traces":[[{"error":0,"meta":{"XXXX": "XXXX"},\"on_match\":[]},\"rule_matches\":[{\"operator\":\"is_sqli\",\"operator_value\":\"\",\"parameters\":[{\"address\":\"server.request.query\",\"key_path\":[\"q\"],\"value\":\"1' OR '1'='1\",\"highlight\":[\"s&sos\"]}]}]}]}","_dd.base_service":"index"},"metrics":{"XXXX": "XXXX"},"meta_struct":{},"name":"aws.lambda","parent_id":"XXXX","resource":"dd-tracer-serverless-span","service":"aws.lambda","span_id":"XXXX","trace_id":"XXXX","type":"serverless","span_links":[],"start":XXXX,"duration":XXXX}]]} -{"traces":[[{"error":0,"meta":{"XXXX": "XXXX"},\"on_match\":[]},\"rule_matches\":[{\"operator\":\"match_regex\",\"operator_value\":\"^Arachni\\\\/v\",\"parameters\":[{\"address\":\"server.request.headers.no_cookies\",\"key_path\":[\"user-agent\"],\"value\":\"Arachni/v1.0\",\"highlight\":[\"Arachni/v\"]}]}]}]}","_dd.base_service":"index"},"metrics":{"XXXX": "XXXX"},"meta_struct":{},"name":"aws.lambda","parent_id":"XXXX","resource":"dd-tracer-serverless-span","service":"aws.lambda","span_id":"XXXX","trace_id":"XXXX","type":"serverless","span_links":[],"start":XXXX,"duration":XXXX}]]} +{"traces":[[{"error":0,"meta":{"XXXX": "XXXX"},\"on_match\":[]},\"rule_matches\":[{\"operator\":\"match_regex\",\"operator_value\":\"^Arachni\\\\/v\",\"parameters\":[{\"address\":\"server.request.headers.no_cookies\",\"key_path\":[\"user-agent\"],\"value\":\"Arachni/v1.0\",\"highlight\":[\"Arachni/v\"]}]}]}]}","_dd.base_service":"index","_dd.svc_src":"m"},"metrics":{"XXXX": "XXXX"},"meta_struct":{},"name":"aws.lambda","parent_id":"XXXX","resource":"dd-tracer-serverless-span","service":"aws.lambda","span_id":"XXXX","trace_id":"XXXX","type":"serverless","span_links":[],"start":XXXX,"duration":XXXX}]]} +{"traces":[[{"error":0,"meta":{"XXXX": "XXXX"}]","_dd.appsec.s.req.query":"[{\"q\":[8]}]","_dd.appsec.s.req.params":"[{\"proxy\":[8]}]","_dd.appsec.json":"{\"triggers\":[{\"rule\":{\"id\":\"crs-942-100\",\"name\":\"SQL Injection Attack Detected via libinjection\",\"tags\":{\"type\":\"sql_injection\",\"crs_id\":\"942100\",\"category\":\"attack_attempt\",\"cwe\":\"89\",\"capec\":\"1000/152/248/66\",\"module\":\"waf\"},\"on_match\":[]},\"rule_matches\":[{\"operator\":\"is_sqli\",\"operator_value\":\"\",\"parameters\":[{\"address\":\"server.request.query\",\"key_path\":[\"q\"],\"value\":\"1' OR '1'='1\",\"highlight\":[\"s&sos\"]}]}]}]}","_dd.base_service":"index","_dd.svc_src":"m"},"metrics":{"XXXX": "XXXX"},"meta_struct":{},"name":"aws.lambda","parent_id":"XXXX","resource":"dd-tracer-serverless-span","service":"aws.lambda","span_id":"XXXX","trace_id":"XXXX","type":"serverless","span_links":[],"start":XXXX,"duration":XXXX}]]} diff --git a/integration_tests/snapshots/logs/appsec-request_ruby33.log b/integration_tests/snapshots/logs/appsec-request_ruby33.log index 4434c4aa..e5e55115 100644 --- a/integration_tests/snapshots/logs/appsec-request_ruby33.log +++ b/integration_tests/snapshots/logs/appsec-request_ruby33.log @@ -3,7 +3,7 @@ END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB -I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - CORE - {"date":"XXXX","os_name":"XXXX","version":"2.34.0","lang":"ruby","lang_version":"3.3.X","env":null,"service":"index","dd_version":null,"debug":false,"tags":"_dd.origin:lambda","runtime_metrics_enabled":false,"vm":"ruby-3.3.X","health_metrics_enabled":false,"profiling_enabled":false,"dynamic_instrumentation_enabled":false} +I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - CORE - {"date":"XXXX","os_name":"XXXX","version":"2.39.0","lang":"ruby","lang_version":"3.3.X","env":null,"service":"index","dd_version":null,"debug":false,"tags":"_dd.origin:lambda","runtime_metrics_enabled":false,"vm":"ruby-3.3.X","health_metrics_enabled":false,"profiling_enabled":false,"dynamic_instrumentation_enabled":false} I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - TRACING - {"enabled":true,"agent_url":null,"analytics_enabled":false,"sample_rate":null,"sampling_rules":null,"integrations_loaded":"aws@","partial_flushing_enabled":false} START START @@ -24,5 +24,5 @@ W, [XXXX] WARN XXXX[datadog] Unable to patch Datadog::Tracing::Contrib::Aws::In {"traces":[[{"error":0,"meta":{"XXXX": "XXXX"},"metrics":{"XXXX": "XXXX"},"meta_struct":{},"name":"aws.lambda","parent_id":"XXXX","resource":"dd-tracer-serverless-span","service":"aws.lambda","span_id":"XXXX","trace_id":"XXXX","type":"serverless","span_links":[],"start":XXXX,"duration":XXXX}]]} {"traces":[[{"error":0,"meta":{"XXXX": "XXXX"},"metrics":{"XXXX": "XXXX"},"meta_struct":{},"name":"aws.lambda","parent_id":"XXXX","resource":"dd-tracer-serverless-span","service":"aws.lambda","span_id":"XXXX","trace_id":"XXXX","type":"serverless","span_links":[],"start":XXXX,"duration":XXXX}]]} {"traces":[[{"error":0,"meta":{"XXXX": "XXXX"},"metrics":{"XXXX": "XXXX"},"meta_struct":{},"name":"aws.lambda","parent_id":"XXXX","resource":"dd-tracer-serverless-span","service":"aws.lambda","span_id":"XXXX","trace_id":"XXXX","type":"serverless","span_links":[],"start":XXXX,"duration":XXXX}]]} -{"traces":[[{"error":0,"meta":{"XXXX": "XXXX"},\"on_match\":[]},\"rule_matches\":[{\"operator\":\"is_sqli\",\"operator_value\":\"\",\"parameters\":[{\"address\":\"server.request.query\",\"key_path\":[\"q\"],\"value\":\"1' OR '1'='1\",\"highlight\":[\"s&sos\"]}]}]}]}","_dd.base_service":"index"},"metrics":{"XXXX": "XXXX"},"meta_struct":{},"name":"aws.lambda","parent_id":"XXXX","resource":"dd-tracer-serverless-span","service":"aws.lambda","span_id":"XXXX","trace_id":"XXXX","type":"serverless","span_links":[],"start":XXXX,"duration":XXXX}]]} -{"traces":[[{"error":0,"meta":{"XXXX": "XXXX"},\"on_match\":[]},\"rule_matches\":[{\"operator\":\"match_regex\",\"operator_value\":\"^Arachni\\\\/v\",\"parameters\":[{\"address\":\"server.request.headers.no_cookies\",\"key_path\":[\"user-agent\"],\"value\":\"Arachni/v1.0\",\"highlight\":[\"Arachni/v\"]}]}]}]}","_dd.base_service":"index"},"metrics":{"XXXX": "XXXX"},"meta_struct":{},"name":"aws.lambda","parent_id":"XXXX","resource":"dd-tracer-serverless-span","service":"aws.lambda","span_id":"XXXX","trace_id":"XXXX","type":"serverless","span_links":[],"start":XXXX,"duration":XXXX}]]} +{"traces":[[{"error":0,"meta":{"XXXX": "XXXX"},\"on_match\":[]},\"rule_matches\":[{\"operator\":\"match_regex\",\"operator_value\":\"^Arachni\\\\/v\",\"parameters\":[{\"address\":\"server.request.headers.no_cookies\",\"key_path\":[\"user-agent\"],\"value\":\"Arachni/v1.0\",\"highlight\":[\"Arachni/v\"]}]}]}]}","_dd.base_service":"index","_dd.svc_src":"m"},"metrics":{"XXXX": "XXXX"},"meta_struct":{},"name":"aws.lambda","parent_id":"XXXX","resource":"dd-tracer-serverless-span","service":"aws.lambda","span_id":"XXXX","trace_id":"XXXX","type":"serverless","span_links":[],"start":XXXX,"duration":XXXX}]]} +{"traces":[[{"error":0,"meta":{"XXXX": "XXXX"}]","_dd.appsec.s.req.query":"[{\"q\":[8]}]","_dd.appsec.s.req.params":"[{\"proxy\":[8]}]","_dd.appsec.json":"{\"triggers\":[{\"rule\":{\"id\":\"crs-942-100\",\"name\":\"SQL Injection Attack Detected via libinjection\",\"tags\":{\"type\":\"sql_injection\",\"crs_id\":\"942100\",\"category\":\"attack_attempt\",\"cwe\":\"89\",\"capec\":\"1000/152/248/66\",\"module\":\"waf\"},\"on_match\":[]},\"rule_matches\":[{\"operator\":\"is_sqli\",\"operator_value\":\"\",\"parameters\":[{\"address\":\"server.request.query\",\"key_path\":[\"q\"],\"value\":\"1' OR '1'='1\",\"highlight\":[\"s&sos\"]}]}]}]}","_dd.base_service":"index","_dd.svc_src":"m"},"metrics":{"XXXX": "XXXX"},"meta_struct":{},"name":"aws.lambda","parent_id":"XXXX","resource":"dd-tracer-serverless-span","service":"aws.lambda","span_id":"XXXX","trace_id":"XXXX","type":"serverless","span_links":[],"start":XXXX,"duration":XXXX}]]} diff --git a/integration_tests/snapshots/logs/appsec-request_ruby34.log b/integration_tests/snapshots/logs/appsec-request_ruby34.log index 052a533b..99516ed2 100644 --- a/integration_tests/snapshots/logs/appsec-request_ruby34.log +++ b/integration_tests/snapshots/logs/appsec-request_ruby34.log @@ -3,7 +3,7 @@ END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB -I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - CORE - {"date":"XXXX","os_name":"XXXX","version":"2.34.0","lang":"ruby","lang_version":"3.4.X","env":null,"service":"index","dd_version":null,"debug":false,"tags":"_dd.origin:lambda","runtime_metrics_enabled":false,"vm":"ruby-3.4.X","health_metrics_enabled":false,"profiling_enabled":false,"dynamic_instrumentation_enabled":false} +I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - CORE - {"date":"XXXX","os_name":"XXXX","version":"2.39.0","lang":"ruby","lang_version":"3.4.X","env":null,"service":"index","dd_version":null,"debug":false,"tags":"_dd.origin:lambda","runtime_metrics_enabled":false,"vm":"ruby-3.4.X","health_metrics_enabled":false,"profiling_enabled":false,"dynamic_instrumentation_enabled":false} I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - TRACING - {"enabled":true,"agent_url":null,"analytics_enabled":false,"sample_rate":null,"sampling_rules":null,"integrations_loaded":"aws@","partial_flushing_enabled":false} START START @@ -24,5 +24,5 @@ W, [XXXX] WARN XXXX[datadog] Unable to patch Datadog::Tracing::Contrib::Aws::In {"traces":[[{"error":0,"meta":{"XXXX": "XXXX"},"metrics":{"XXXX": "XXXX"},"meta_struct":{},"name":"aws.lambda","parent_id":"XXXX","resource":"dd-tracer-serverless-span","service":"aws.lambda","span_id":"XXXX","trace_id":"XXXX","type":"serverless","span_links":[],"start":XXXX,"duration":XXXX}]]} {"traces":[[{"error":0,"meta":{"XXXX": "XXXX"},"metrics":{"XXXX": "XXXX"},"meta_struct":{},"name":"aws.lambda","parent_id":"XXXX","resource":"dd-tracer-serverless-span","service":"aws.lambda","span_id":"XXXX","trace_id":"XXXX","type":"serverless","span_links":[],"start":XXXX,"duration":XXXX}]]} {"traces":[[{"error":0,"meta":{"XXXX": "XXXX"},"metrics":{"XXXX": "XXXX"},"meta_struct":{},"name":"aws.lambda","parent_id":"XXXX","resource":"dd-tracer-serverless-span","service":"aws.lambda","span_id":"XXXX","trace_id":"XXXX","type":"serverless","span_links":[],"start":XXXX,"duration":XXXX}]]} -{"traces":[[{"error":0,"meta":{"XXXX": "XXXX"},\"on_match\":[]},\"rule_matches\":[{\"operator\":\"is_sqli\",\"operator_value\":\"\",\"parameters\":[{\"address\":\"server.request.query\",\"key_path\":[\"q\"],\"value\":\"1' OR '1'='1\",\"highlight\":[\"s&sos\"]}]}]}]}","_dd.base_service":"index"},"metrics":{"XXXX": "XXXX"},"meta_struct":{},"name":"aws.lambda","parent_id":"XXXX","resource":"dd-tracer-serverless-span","service":"aws.lambda","span_id":"XXXX","trace_id":"XXXX","type":"serverless","span_links":[],"start":XXXX,"duration":XXXX}]]} -{"traces":[[{"error":0,"meta":{"XXXX": "XXXX"},\"on_match\":[]},\"rule_matches\":[{\"operator\":\"match_regex\",\"operator_value\":\"^Arachni\\\\/v\",\"parameters\":[{\"address\":\"server.request.headers.no_cookies\",\"key_path\":[\"user-agent\"],\"value\":\"Arachni/v1.0\",\"highlight\":[\"Arachni/v\"]}]}]}]}","_dd.base_service":"index"},"metrics":{"XXXX": "XXXX"},"meta_struct":{},"name":"aws.lambda","parent_id":"XXXX","resource":"dd-tracer-serverless-span","service":"aws.lambda","span_id":"XXXX","trace_id":"XXXX","type":"serverless","span_links":[],"start":XXXX,"duration":XXXX}]]} +{"traces":[[{"error":0,"meta":{"XXXX": "XXXX"},\"on_match\":[]},\"rule_matches\":[{\"operator\":\"match_regex\",\"operator_value\":\"^Arachni\\\\/v\",\"parameters\":[{\"address\":\"server.request.headers.no_cookies\",\"key_path\":[\"user-agent\"],\"value\":\"Arachni/v1.0\",\"highlight\":[\"Arachni/v\"]}]}]}]}","_dd.base_service":"index","_dd.svc_src":"m"},"metrics":{"XXXX": "XXXX"},"meta_struct":{},"name":"aws.lambda","parent_id":"XXXX","resource":"dd-tracer-serverless-span","service":"aws.lambda","span_id":"XXXX","trace_id":"XXXX","type":"serverless","span_links":[],"start":XXXX,"duration":XXXX}]]} +{"traces":[[{"error":0,"meta":{"XXXX": "XXXX"}]","_dd.appsec.s.req.query":"[{\"q\":[8]}]","_dd.appsec.s.req.params":"[{\"proxy\":[8]}]","_dd.appsec.json":"{\"triggers\":[{\"rule\":{\"id\":\"crs-942-100\",\"name\":\"SQL Injection Attack Detected via libinjection\",\"tags\":{\"type\":\"sql_injection\",\"crs_id\":\"942100\",\"category\":\"attack_attempt\",\"cwe\":\"89\",\"capec\":\"1000/152/248/66\",\"module\":\"waf\"},\"on_match\":[]},\"rule_matches\":[{\"operator\":\"is_sqli\",\"operator_value\":\"\",\"parameters\":[{\"address\":\"server.request.query\",\"key_path\":[\"q\"],\"value\":\"1' OR '1'='1\",\"highlight\":[\"s&sos\"]}]}]}]}","_dd.base_service":"index","_dd.svc_src":"m"},"metrics":{"XXXX": "XXXX"},"meta_struct":{},"name":"aws.lambda","parent_id":"XXXX","resource":"dd-tracer-serverless-span","service":"aws.lambda","span_id":"XXXX","trace_id":"XXXX","type":"serverless","span_links":[],"start":XXXX,"duration":XXXX}]]} diff --git a/integration_tests/snapshots/logs/appsec-request_ruby40.log b/integration_tests/snapshots/logs/appsec-request_ruby40.log index 775d5166..6a71ea9a 100644 --- a/integration_tests/snapshots/logs/appsec-request_ruby40.log +++ b/integration_tests/snapshots/logs/appsec-request_ruby40.log @@ -3,7 +3,7 @@ END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB -I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - CORE - {"date":"XXXX","os_name":"XXXX","version":"2.34.0","lang":"ruby","lang_version":"4.0.X","env":null,"service":"index","dd_version":null,"debug":false,"tags":"_dd.origin:lambda","runtime_metrics_enabled":false,"vm":"ruby-4.0.X","health_metrics_enabled":false,"profiling_enabled":false,"dynamic_instrumentation_enabled":false} +I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - CORE - {"date":"XXXX","os_name":"XXXX","version":"2.39.0","lang":"ruby","lang_version":"4.0.X","env":null,"service":"index","dd_version":null,"debug":false,"tags":"_dd.origin:lambda","runtime_metrics_enabled":false,"vm":"ruby-4.0.X","health_metrics_enabled":false,"profiling_enabled":false,"dynamic_instrumentation_enabled":false} I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - TRACING - {"enabled":true,"agent_url":null,"analytics_enabled":false,"sample_rate":null,"sampling_rules":null,"integrations_loaded":"aws@","partial_flushing_enabled":false} START START @@ -24,5 +24,5 @@ W, [XXXX] WARN XXXX[datadog] Unable to patch Datadog::Tracing::Contrib::Aws::In {"traces":[[{"error":0,"meta":{"XXXX": "XXXX"},"metrics":{"XXXX": "XXXX"},"meta_struct":{},"name":"aws.lambda","parent_id":"XXXX","resource":"dd-tracer-serverless-span","service":"aws.lambda","span_id":"XXXX","trace_id":"XXXX","type":"serverless","span_links":[],"start":XXXX,"duration":XXXX}]]} {"traces":[[{"error":0,"meta":{"XXXX": "XXXX"},"metrics":{"XXXX": "XXXX"},"meta_struct":{},"name":"aws.lambda","parent_id":"XXXX","resource":"dd-tracer-serverless-span","service":"aws.lambda","span_id":"XXXX","trace_id":"XXXX","type":"serverless","span_links":[],"start":XXXX,"duration":XXXX}]]} {"traces":[[{"error":0,"meta":{"XXXX": "XXXX"},"metrics":{"XXXX": "XXXX"},"meta_struct":{},"name":"aws.lambda","parent_id":"XXXX","resource":"dd-tracer-serverless-span","service":"aws.lambda","span_id":"XXXX","trace_id":"XXXX","type":"serverless","span_links":[],"start":XXXX,"duration":XXXX}]]} -{"traces":[[{"error":0,"meta":{"XXXX": "XXXX"},\"on_match\":[]},\"rule_matches\":[{\"operator\":\"is_sqli\",\"operator_value\":\"\",\"parameters\":[{\"address\":\"server.request.query\",\"key_path\":[\"q\"],\"value\":\"1' OR '1'='1\",\"highlight\":[\"s&sos\"]}]}]}]}","_dd.base_service":"index"},"metrics":{"XXXX": "XXXX"},"meta_struct":{},"name":"aws.lambda","parent_id":"XXXX","resource":"dd-tracer-serverless-span","service":"aws.lambda","span_id":"XXXX","trace_id":"XXXX","type":"serverless","span_links":[],"start":XXXX,"duration":XXXX}]]} -{"traces":[[{"error":0,"meta":{"XXXX": "XXXX"},\"on_match\":[]},\"rule_matches\":[{\"operator\":\"match_regex\",\"operator_value\":\"^Arachni\\\\/v\",\"parameters\":[{\"address\":\"server.request.headers.no_cookies\",\"key_path\":[\"user-agent\"],\"value\":\"Arachni/v1.0\",\"highlight\":[\"Arachni/v\"]}]}]}]}","_dd.base_service":"index"},"metrics":{"XXXX": "XXXX"},"meta_struct":{},"name":"aws.lambda","parent_id":"XXXX","resource":"dd-tracer-serverless-span","service":"aws.lambda","span_id":"XXXX","trace_id":"XXXX","type":"serverless","span_links":[],"start":XXXX,"duration":XXXX}]]} +{"traces":[[{"error":0,"meta":{"XXXX": "XXXX"},\"on_match\":[]},\"rule_matches\":[{\"operator\":\"match_regex\",\"operator_value\":\"^Arachni\\\\/v\",\"parameters\":[{\"address\":\"server.request.headers.no_cookies\",\"key_path\":[\"user-agent\"],\"value\":\"Arachni/v1.0\",\"highlight\":[\"Arachni/v\"]}]}]}]}","_dd.base_service":"index","_dd.svc_src":"m"},"metrics":{"XXXX": "XXXX"},"meta_struct":{},"name":"aws.lambda","parent_id":"XXXX","resource":"dd-tracer-serverless-span","service":"aws.lambda","span_id":"XXXX","trace_id":"XXXX","type":"serverless","span_links":[],"start":XXXX,"duration":XXXX}]]} +{"traces":[[{"error":0,"meta":{"XXXX": "XXXX"}]","_dd.appsec.s.req.query":"[{\"q\":[8]}]","_dd.appsec.s.req.params":"[{\"proxy\":[8]}]","_dd.appsec.json":"{\"triggers\":[{\"rule\":{\"id\":\"crs-942-100\",\"name\":\"SQL Injection Attack Detected via libinjection\",\"tags\":{\"type\":\"sql_injection\",\"crs_id\":\"942100\",\"category\":\"attack_attempt\",\"cwe\":\"89\",\"capec\":\"1000/152/248/66\",\"module\":\"waf\"},\"on_match\":[]},\"rule_matches\":[{\"operator\":\"is_sqli\",\"operator_value\":\"\",\"parameters\":[{\"address\":\"server.request.query\",\"key_path\":[\"q\"],\"value\":\"1' OR '1'='1\",\"highlight\":[\"s&sos\"]}]}]}]}","_dd.base_service":"index","_dd.svc_src":"m"},"metrics":{"XXXX": "XXXX"},"meta_struct":{},"name":"aws.lambda","parent_id":"XXXX","resource":"dd-tracer-serverless-span","service":"aws.lambda","span_id":"XXXX","trace_id":"XXXX","type":"serverless","span_links":[],"start":XXXX,"duration":XXXX}]]} diff --git a/integration_tests/snapshots/logs/async-metrics_ruby32.log b/integration_tests/snapshots/logs/async-metrics_ruby32.log index bba7953c..15e4d9ef 100644 --- a/integration_tests/snapshots/logs/async-metrics_ruby32.log +++ b/integration_tests/snapshots/logs/async-metrics_ruby32.log @@ -3,7 +3,7 @@ END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB -I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - CORE - {"date":"XXXX","os_name":"XXXX","version":"2.34.0","lang":"ruby","lang_version":"3.2.X","env":null,"service":"index","dd_version":null,"debug":false,"tags":"_dd.origin:lambda","runtime_metrics_enabled":false,"vm":"ruby-3.2.X","health_metrics_enabled":false,"profiling_enabled":false,"dynamic_instrumentation_enabled":false} +I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - CORE - {"date":"XXXX","os_name":"XXXX","version":"2.39.0","lang":"ruby","lang_version":"3.2.X","env":null,"service":"index","dd_version":null,"debug":false,"tags":"_dd.origin:lambda","runtime_metrics_enabled":false,"vm":"ruby-3.2.X","health_metrics_enabled":false,"profiling_enabled":false,"dynamic_instrumentation_enabled":false} I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - TRACING - {"enabled":true,"agent_url":null,"analytics_enabled":false,"sample_rate":null,"sampling_rules":null,"integrations_loaded":"aws@","partial_flushing_enabled":false} Processed APIGateway request Processed APIGateway request diff --git a/integration_tests/snapshots/logs/async-metrics_ruby33.log b/integration_tests/snapshots/logs/async-metrics_ruby33.log index 8f6cb6e9..4ce7a4af 100644 --- a/integration_tests/snapshots/logs/async-metrics_ruby33.log +++ b/integration_tests/snapshots/logs/async-metrics_ruby33.log @@ -3,7 +3,7 @@ END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB -I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - CORE - {"date":"XXXX","os_name":"XXXX","version":"2.34.0","lang":"ruby","lang_version":"3.3.X","env":null,"service":"index","dd_version":null,"debug":false,"tags":"_dd.origin:lambda","runtime_metrics_enabled":false,"vm":"ruby-3.3.X","health_metrics_enabled":false,"profiling_enabled":false,"dynamic_instrumentation_enabled":false} +I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - CORE - {"date":"XXXX","os_name":"XXXX","version":"2.39.0","lang":"ruby","lang_version":"3.3.X","env":null,"service":"index","dd_version":null,"debug":false,"tags":"_dd.origin:lambda","runtime_metrics_enabled":false,"vm":"ruby-3.3.X","health_metrics_enabled":false,"profiling_enabled":false,"dynamic_instrumentation_enabled":false} I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - TRACING - {"enabled":true,"agent_url":null,"analytics_enabled":false,"sample_rate":null,"sampling_rules":null,"integrations_loaded":"aws@","partial_flushing_enabled":false} Processed APIGateway request Processed APIGateway request diff --git a/integration_tests/snapshots/logs/async-metrics_ruby34.log b/integration_tests/snapshots/logs/async-metrics_ruby34.log index 10f40413..ef2addb7 100644 --- a/integration_tests/snapshots/logs/async-metrics_ruby34.log +++ b/integration_tests/snapshots/logs/async-metrics_ruby34.log @@ -3,7 +3,7 @@ END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB -I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - CORE - {"date":"XXXX","os_name":"XXXX","version":"2.34.0","lang":"ruby","lang_version":"3.4.X","env":null,"service":"index","dd_version":null,"debug":false,"tags":"_dd.origin:lambda","runtime_metrics_enabled":false,"vm":"ruby-3.4.X","health_metrics_enabled":false,"profiling_enabled":false,"dynamic_instrumentation_enabled":false} +I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - CORE - {"date":"XXXX","os_name":"XXXX","version":"2.39.0","lang":"ruby","lang_version":"3.4.X","env":null,"service":"index","dd_version":null,"debug":false,"tags":"_dd.origin:lambda","runtime_metrics_enabled":false,"vm":"ruby-3.4.X","health_metrics_enabled":false,"profiling_enabled":false,"dynamic_instrumentation_enabled":false} I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - TRACING - {"enabled":true,"agent_url":null,"analytics_enabled":false,"sample_rate":null,"sampling_rules":null,"integrations_loaded":"aws@","partial_flushing_enabled":false} Processed APIGateway request Processed APIGateway request diff --git a/integration_tests/snapshots/logs/async-metrics_ruby40.log b/integration_tests/snapshots/logs/async-metrics_ruby40.log index 5544ab2b..eabad3b1 100644 --- a/integration_tests/snapshots/logs/async-metrics_ruby40.log +++ b/integration_tests/snapshots/logs/async-metrics_ruby40.log @@ -3,7 +3,7 @@ END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB -I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - CORE - {"date":"XXXX","os_name":"XXXX","version":"2.34.0","lang":"ruby","lang_version":"4.0.X","env":null,"service":"index","dd_version":null,"debug":false,"tags":"_dd.origin:lambda","runtime_metrics_enabled":false,"vm":"ruby-4.0.X","health_metrics_enabled":false,"profiling_enabled":false,"dynamic_instrumentation_enabled":false} +I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - CORE - {"date":"XXXX","os_name":"XXXX","version":"2.39.0","lang":"ruby","lang_version":"4.0.X","env":null,"service":"index","dd_version":null,"debug":false,"tags":"_dd.origin:lambda","runtime_metrics_enabled":false,"vm":"ruby-4.0.X","health_metrics_enabled":false,"profiling_enabled":false,"dynamic_instrumentation_enabled":false} I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - TRACING - {"enabled":true,"agent_url":null,"analytics_enabled":false,"sample_rate":null,"sampling_rules":null,"integrations_loaded":"aws@","partial_flushing_enabled":false} Processed APIGateway request Processed APIGateway request diff --git a/integration_tests/snapshots/logs/http-requests_ruby32.log b/integration_tests/snapshots/logs/http-requests_ruby32.log index 8aae1d30..9156bf91 100644 --- a/integration_tests/snapshots/logs/http-requests_ruby32.log +++ b/integration_tests/snapshots/logs/http-requests_ruby32.log @@ -3,7 +3,7 @@ END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB -I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - CORE - {"date":"XXXX","os_name":"XXXX","version":"2.34.0","lang":"ruby","lang_version":"3.2.X","env":null,"service":"index","dd_version":null,"debug":false,"tags":"_dd.origin:lambda","runtime_metrics_enabled":false,"vm":"ruby-3.2.X","health_metrics_enabled":false,"profiling_enabled":false,"dynamic_instrumentation_enabled":false} +I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - CORE - {"date":"XXXX","os_name":"XXXX","version":"2.39.0","lang":"ruby","lang_version":"3.2.X","env":null,"service":"index","dd_version":null,"debug":false,"tags":"_dd.origin:lambda","runtime_metrics_enabled":false,"vm":"ruby-3.2.X","health_metrics_enabled":false,"profiling_enabled":false,"dynamic_instrumentation_enabled":false} I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - TRACING - {"enabled":true,"agent_url":null,"analytics_enabled":false,"sample_rate":null,"sampling_rules":null,"integrations_loaded":"aws@","partial_flushing_enabled":false} START START diff --git a/integration_tests/snapshots/logs/http-requests_ruby33.log b/integration_tests/snapshots/logs/http-requests_ruby33.log index bc992c68..06490105 100644 --- a/integration_tests/snapshots/logs/http-requests_ruby33.log +++ b/integration_tests/snapshots/logs/http-requests_ruby33.log @@ -3,7 +3,7 @@ END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB -I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - CORE - {"date":"XXXX","os_name":"XXXX","version":"2.34.0","lang":"ruby","lang_version":"3.3.X","env":null,"service":"index","dd_version":null,"debug":false,"tags":"_dd.origin:lambda","runtime_metrics_enabled":false,"vm":"ruby-3.3.X","health_metrics_enabled":false,"profiling_enabled":false,"dynamic_instrumentation_enabled":false} +I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - CORE - {"date":"XXXX","os_name":"XXXX","version":"2.39.0","lang":"ruby","lang_version":"3.3.X","env":null,"service":"index","dd_version":null,"debug":false,"tags":"_dd.origin:lambda","runtime_metrics_enabled":false,"vm":"ruby-3.3.X","health_metrics_enabled":false,"profiling_enabled":false,"dynamic_instrumentation_enabled":false} I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - TRACING - {"enabled":true,"agent_url":null,"analytics_enabled":false,"sample_rate":null,"sampling_rules":null,"integrations_loaded":"aws@","partial_flushing_enabled":false} START START diff --git a/integration_tests/snapshots/logs/http-requests_ruby34.log b/integration_tests/snapshots/logs/http-requests_ruby34.log index d43328e2..c0ddc4e6 100644 --- a/integration_tests/snapshots/logs/http-requests_ruby34.log +++ b/integration_tests/snapshots/logs/http-requests_ruby34.log @@ -3,7 +3,7 @@ END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB -I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - CORE - {"date":"XXXX","os_name":"XXXX","version":"2.34.0","lang":"ruby","lang_version":"3.4.X","env":null,"service":"index","dd_version":null,"debug":false,"tags":"_dd.origin:lambda","runtime_metrics_enabled":false,"vm":"ruby-3.4.X","health_metrics_enabled":false,"profiling_enabled":false,"dynamic_instrumentation_enabled":false} +I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - CORE - {"date":"XXXX","os_name":"XXXX","version":"2.39.0","lang":"ruby","lang_version":"3.4.X","env":null,"service":"index","dd_version":null,"debug":false,"tags":"_dd.origin:lambda","runtime_metrics_enabled":false,"vm":"ruby-3.4.X","health_metrics_enabled":false,"profiling_enabled":false,"dynamic_instrumentation_enabled":false} I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - TRACING - {"enabled":true,"agent_url":null,"analytics_enabled":false,"sample_rate":null,"sampling_rules":null,"integrations_loaded":"aws@","partial_flushing_enabled":false} START START diff --git a/integration_tests/snapshots/logs/http-requests_ruby40.log b/integration_tests/snapshots/logs/http-requests_ruby40.log index 91820fcb..5230a2e6 100644 --- a/integration_tests/snapshots/logs/http-requests_ruby40.log +++ b/integration_tests/snapshots/logs/http-requests_ruby40.log @@ -3,7 +3,7 @@ END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB -I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - CORE - {"date":"XXXX","os_name":"XXXX","version":"2.34.0","lang":"ruby","lang_version":"4.0.X","env":null,"service":"index","dd_version":null,"debug":false,"tags":"_dd.origin:lambda","runtime_metrics_enabled":false,"vm":"ruby-4.0.X","health_metrics_enabled":false,"profiling_enabled":false,"dynamic_instrumentation_enabled":false} +I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - CORE - {"date":"XXXX","os_name":"XXXX","version":"2.39.0","lang":"ruby","lang_version":"4.0.X","env":null,"service":"index","dd_version":null,"debug":false,"tags":"_dd.origin:lambda","runtime_metrics_enabled":false,"vm":"ruby-4.0.X","health_metrics_enabled":false,"profiling_enabled":false,"dynamic_instrumentation_enabled":false} I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - TRACING - {"enabled":true,"agent_url":null,"analytics_enabled":false,"sample_rate":null,"sampling_rules":null,"integrations_loaded":"aws@","partial_flushing_enabled":false} START START diff --git a/integration_tests/snapshots/logs/process-input-traced_ruby32.log b/integration_tests/snapshots/logs/process-input-traced_ruby32.log index c525c3ae..9d228d19 100644 --- a/integration_tests/snapshots/logs/process-input-traced_ruby32.log +++ b/integration_tests/snapshots/logs/process-input-traced_ruby32.log @@ -3,7 +3,7 @@ END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB -I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - CORE - {"date":"XXXX","os_name":"XXXX","version":"2.34.0","lang":"ruby","lang_version":"3.2.X","env":null,"service":"index","dd_version":null,"debug":false,"tags":"_dd.origin:lambda","runtime_metrics_enabled":false,"vm":"ruby-3.2.X","health_metrics_enabled":false,"profiling_enabled":false,"dynamic_instrumentation_enabled":false} +I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - CORE - {"date":"XXXX","os_name":"XXXX","version":"2.39.0","lang":"ruby","lang_version":"3.2.X","env":null,"service":"index","dd_version":null,"debug":false,"tags":"_dd.origin:lambda","runtime_metrics_enabled":false,"vm":"ruby-3.2.X","health_metrics_enabled":false,"profiling_enabled":false,"dynamic_instrumentation_enabled":false} I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - TRACING - {"enabled":true,"agent_url":null,"analytics_enabled":false,"sample_rate":null,"sampling_rules":null,"integrations_loaded":"aws@","partial_flushing_enabled":false} START START diff --git a/integration_tests/snapshots/logs/process-input-traced_ruby33.log b/integration_tests/snapshots/logs/process-input-traced_ruby33.log index ebad69de..b5a61448 100644 --- a/integration_tests/snapshots/logs/process-input-traced_ruby33.log +++ b/integration_tests/snapshots/logs/process-input-traced_ruby33.log @@ -3,7 +3,7 @@ END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB -I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - CORE - {"date":"XXXX","os_name":"XXXX","version":"2.34.0","lang":"ruby","lang_version":"3.3.X","env":null,"service":"index","dd_version":null,"debug":false,"tags":"_dd.origin:lambda","runtime_metrics_enabled":false,"vm":"ruby-3.3.X","health_metrics_enabled":false,"profiling_enabled":false,"dynamic_instrumentation_enabled":false} +I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - CORE - {"date":"XXXX","os_name":"XXXX","version":"2.39.0","lang":"ruby","lang_version":"3.3.X","env":null,"service":"index","dd_version":null,"debug":false,"tags":"_dd.origin:lambda","runtime_metrics_enabled":false,"vm":"ruby-3.3.X","health_metrics_enabled":false,"profiling_enabled":false,"dynamic_instrumentation_enabled":false} I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - TRACING - {"enabled":true,"agent_url":null,"analytics_enabled":false,"sample_rate":null,"sampling_rules":null,"integrations_loaded":"aws@","partial_flushing_enabled":false} START START diff --git a/integration_tests/snapshots/logs/process-input-traced_ruby34.log b/integration_tests/snapshots/logs/process-input-traced_ruby34.log index 98d07559..9ab7dce6 100644 --- a/integration_tests/snapshots/logs/process-input-traced_ruby34.log +++ b/integration_tests/snapshots/logs/process-input-traced_ruby34.log @@ -3,7 +3,7 @@ END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB -I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - CORE - {"date":"XXXX","os_name":"XXXX","version":"2.34.0","lang":"ruby","lang_version":"3.4.X","env":null,"service":"index","dd_version":null,"debug":false,"tags":"_dd.origin:lambda","runtime_metrics_enabled":false,"vm":"ruby-3.4.X","health_metrics_enabled":false,"profiling_enabled":false,"dynamic_instrumentation_enabled":false} +I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - CORE - {"date":"XXXX","os_name":"XXXX","version":"2.39.0","lang":"ruby","lang_version":"3.4.X","env":null,"service":"index","dd_version":null,"debug":false,"tags":"_dd.origin:lambda","runtime_metrics_enabled":false,"vm":"ruby-3.4.X","health_metrics_enabled":false,"profiling_enabled":false,"dynamic_instrumentation_enabled":false} I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - TRACING - {"enabled":true,"agent_url":null,"analytics_enabled":false,"sample_rate":null,"sampling_rules":null,"integrations_loaded":"aws@","partial_flushing_enabled":false} START START diff --git a/integration_tests/snapshots/logs/process-input-traced_ruby40.log b/integration_tests/snapshots/logs/process-input-traced_ruby40.log index cdc27f1b..58479a37 100644 --- a/integration_tests/snapshots/logs/process-input-traced_ruby40.log +++ b/integration_tests/snapshots/logs/process-input-traced_ruby40.log @@ -3,7 +3,7 @@ END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB -I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - CORE - {"date":"XXXX","os_name":"XXXX","version":"2.34.0","lang":"ruby","lang_version":"4.0.X","env":null,"service":"index","dd_version":null,"debug":false,"tags":"_dd.origin:lambda","runtime_metrics_enabled":false,"vm":"ruby-4.0.X","health_metrics_enabled":false,"profiling_enabled":false,"dynamic_instrumentation_enabled":false} +I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - CORE - {"date":"XXXX","os_name":"XXXX","version":"2.39.0","lang":"ruby","lang_version":"4.0.X","env":null,"service":"index","dd_version":null,"debug":false,"tags":"_dd.origin:lambda","runtime_metrics_enabled":false,"vm":"ruby-4.0.X","health_metrics_enabled":false,"profiling_enabled":false,"dynamic_instrumentation_enabled":false} I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - TRACING - {"enabled":true,"agent_url":null,"analytics_enabled":false,"sample_rate":null,"sampling_rules":null,"integrations_loaded":"aws@","partial_flushing_enabled":false} START START diff --git a/integration_tests/snapshots/logs/sync-metrics_ruby32.log b/integration_tests/snapshots/logs/sync-metrics_ruby32.log index 47f837fc..119cea75 100644 --- a/integration_tests/snapshots/logs/sync-metrics_ruby32.log +++ b/integration_tests/snapshots/logs/sync-metrics_ruby32.log @@ -3,7 +3,7 @@ END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB -I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - CORE - {"date":"XXXX","os_name":"XXXX","version":"2.34.0","lang":"ruby","lang_version":"3.2.X","env":null,"service":"index","dd_version":null,"debug":false,"tags":"_dd.origin:lambda","runtime_metrics_enabled":false,"vm":"ruby-3.2.X","health_metrics_enabled":false,"profiling_enabled":false,"dynamic_instrumentation_enabled":false} +I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - CORE - {"date":"XXXX","os_name":"XXXX","version":"2.39.0","lang":"ruby","lang_version":"3.2.X","env":null,"service":"index","dd_version":null,"debug":false,"tags":"_dd.origin:lambda","runtime_metrics_enabled":false,"vm":"ruby-3.2.X","health_metrics_enabled":false,"profiling_enabled":false,"dynamic_instrumentation_enabled":false} I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - TRACING - {"enabled":true,"agent_url":null,"analytics_enabled":false,"sample_rate":null,"sampling_rules":null,"integrations_loaded":"aws@","partial_flushing_enabled":false} Processed APIGateway request Processed APIGateway request diff --git a/integration_tests/snapshots/logs/sync-metrics_ruby33.log b/integration_tests/snapshots/logs/sync-metrics_ruby33.log index c82e098e..1fff9f03 100644 --- a/integration_tests/snapshots/logs/sync-metrics_ruby33.log +++ b/integration_tests/snapshots/logs/sync-metrics_ruby33.log @@ -3,7 +3,7 @@ END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB -I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - CORE - {"date":"XXXX","os_name":"XXXX","version":"2.34.0","lang":"ruby","lang_version":"3.3.X","env":null,"service":"index","dd_version":null,"debug":false,"tags":"_dd.origin:lambda","runtime_metrics_enabled":false,"vm":"ruby-3.3.X","health_metrics_enabled":false,"profiling_enabled":false,"dynamic_instrumentation_enabled":false} +I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - CORE - {"date":"XXXX","os_name":"XXXX","version":"2.39.0","lang":"ruby","lang_version":"3.3.X","env":null,"service":"index","dd_version":null,"debug":false,"tags":"_dd.origin:lambda","runtime_metrics_enabled":false,"vm":"ruby-3.3.X","health_metrics_enabled":false,"profiling_enabled":false,"dynamic_instrumentation_enabled":false} I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - TRACING - {"enabled":true,"agent_url":null,"analytics_enabled":false,"sample_rate":null,"sampling_rules":null,"integrations_loaded":"aws@","partial_flushing_enabled":false} Processed APIGateway request Processed APIGateway request diff --git a/integration_tests/snapshots/logs/sync-metrics_ruby34.log b/integration_tests/snapshots/logs/sync-metrics_ruby34.log index 25dac9d3..fdf5136a 100644 --- a/integration_tests/snapshots/logs/sync-metrics_ruby34.log +++ b/integration_tests/snapshots/logs/sync-metrics_ruby34.log @@ -3,7 +3,7 @@ END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB -I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - CORE - {"date":"XXXX","os_name":"XXXX","version":"2.34.0","lang":"ruby","lang_version":"3.4.X","env":null,"service":"index","dd_version":null,"debug":false,"tags":"_dd.origin:lambda","runtime_metrics_enabled":false,"vm":"ruby-3.4.X","health_metrics_enabled":false,"profiling_enabled":false,"dynamic_instrumentation_enabled":false} +I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - CORE - {"date":"XXXX","os_name":"XXXX","version":"2.39.0","lang":"ruby","lang_version":"3.4.X","env":null,"service":"index","dd_version":null,"debug":false,"tags":"_dd.origin:lambda","runtime_metrics_enabled":false,"vm":"ruby-3.4.X","health_metrics_enabled":false,"profiling_enabled":false,"dynamic_instrumentation_enabled":false} I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - TRACING - {"enabled":true,"agent_url":null,"analytics_enabled":false,"sample_rate":null,"sampling_rules":null,"integrations_loaded":"aws@","partial_flushing_enabled":false} Processed APIGateway request Processed APIGateway request diff --git a/integration_tests/snapshots/logs/sync-metrics_ruby40.log b/integration_tests/snapshots/logs/sync-metrics_ruby40.log index 0e512d07..79d4769f 100644 --- a/integration_tests/snapshots/logs/sync-metrics_ruby40.log +++ b/integration_tests/snapshots/logs/sync-metrics_ruby40.log @@ -3,7 +3,7 @@ END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB END Duration: XXXX ms Memory Used: XXXX MB -I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - CORE - {"date":"XXXX","os_name":"XXXX","version":"2.34.0","lang":"ruby","lang_version":"4.0.X","env":null,"service":"index","dd_version":null,"debug":false,"tags":"_dd.origin:lambda","runtime_metrics_enabled":false,"vm":"ruby-4.0.X","health_metrics_enabled":false,"profiling_enabled":false,"dynamic_instrumentation_enabled":false} +I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - CORE - {"date":"XXXX","os_name":"XXXX","version":"2.39.0","lang":"ruby","lang_version":"4.0.X","env":null,"service":"index","dd_version":null,"debug":false,"tags":"_dd.origin:lambda","runtime_metrics_enabled":false,"vm":"ruby-4.0.X","health_metrics_enabled":false,"profiling_enabled":false,"dynamic_instrumentation_enabled":false} I, [XXXX] INFO XXXX[datadog] DATADOG CONFIGURATION - TRACING - {"enabled":true,"agent_url":null,"analytics_enabled":false,"sample_rate":null,"sampling_rules":null,"integrations_loaded":"aws@","partial_flushing_enabled":false} Processed APIGateway request Processed APIGateway request diff --git a/lib/datadog/lambda.rb b/lib/datadog/lambda.rb index 30e78cc4..ca71f479 100644 --- a/lib/datadog/lambda.rb +++ b/lib/datadog/lambda.rb @@ -60,7 +60,7 @@ def self.configure_apm # @param event [Object] event sent to lambda # @param context [Object] lambda context # @param block [Proc] implementation of the handler function. - # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity + # rubocop:disable Metrics/CyclomaticComplexity def self.wrap(event, context, &block) @response = nil @listener ||= initialize_listener @@ -68,7 +68,7 @@ def self.wrap(event, context, &block) begin cold = @is_cold_start @listener&.on_start(event:, request_context: context, cold_start: cold) - @response = @listener&.response_override || block.call + @response = @listener ? @listener.with_appsec(&block) : block.call rescue StandardError => e record_enhanced('errors', context) raise e @@ -80,7 +80,7 @@ def self.wrap(event, context, &block) end @response end - # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity + # rubocop:enable Metrics/CyclomaticComplexity # Gets the current tracing context def self.trace_context diff --git a/lib/datadog/lambda/appsec.rb b/lib/datadog/lambda/appsec.rb index 4466023f..fbc018cd 100644 --- a/lib/datadog/lambda/appsec.rb +++ b/lib/datadog/lambda/appsec.rb @@ -1,7 +1,10 @@ # frozen_string_literal: true -require 'json' +require 'datadog/appsec/api_security' + +require_relative 'appsec/tagging' require_relative 'appsec/request' +require_relative 'appsec/response' require_relative 'appsec/event_normalizer' require_relative 'appsec/response_normalizer' @@ -10,7 +13,7 @@ module Lambda # AppSec integration for AWS Lambda invocations. module AppSec class << self - # rubocop:disable Metrics/AbcSize + # rubocop:disable Metrics/AbcSize, Metrics/MethodLength def on_start(event, trace:, span:, cold_start: false) @request = nil return unless enabled? @@ -18,11 +21,12 @@ def on_start(event, trace:, span:, cold_start: false) context = create_context(trace, span) return unless Datadog::AppSec::Context.active - tag_and_keep(context, cold_start: cold_start) + Tagging.tag_and_keep(context, cold_start: cold_start) event = EventNormalizer.normalize(event) @request = Request.from_normalized(event) + Tagging.tag_request(@request, context: context) payload = Datadog::AppSec::Instrumentation::Gateway::DataContainer.new( event, context: context ) @@ -40,26 +44,34 @@ def on_start(event, trace:, span:, cold_start: false) Datadog::AppSec::Context.deactivate if context Datadog::Utils.logger.debug("failed to start AppSec: #{e}") end - # rubocop:enable Metrics/AbcSize + # rubocop:enable Metrics/AbcSize, Metrics/MethodLength - # rubocop:disable Metrics/AbcSize + # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity def on_finish(response) return unless enabled? context = Datadog::AppSec::Context.active return unless context - response = ResponseNormalizer.normalize(response) - payload = Datadog::AppSec::Instrumentation::Gateway::DataContainer.new( - response, context: context - ) + normalized = ResponseNormalizer.normalize(response) + response = Response.from_normalized(normalized) - interrupt_params = catch(Datadog::AppSec::Ext::INTERRUPT) do - Datadog::AppSec::Instrumentation.gateway.push('aws_lambda.response.start', payload) - nil + interrupt_params = nil + unless context.interrupted? + Tagging.tag_response(response, context: context) + payload = Datadog::AppSec::Instrumentation::Gateway::DataContainer.new( + normalized, context: context + ) + + interrupt_params = catch(Datadog::AppSec::Ext::INTERRUPT) do + Datadog::AppSec::Instrumentation.gateway.push('aws_lambda.response.start', payload) + nil + end + + context.mark_as_interrupted! if interrupt_params end - context.mark_as_interrupted! if interrupt_params + extract_api_security_schema(context, request: @request, response: response) Datadog::AppSec::Event.record(context, request: @request) context.export_metrics @@ -71,10 +83,30 @@ def on_finish(response) ensure Datadog::AppSec::Context.deactivate if context end - # rubocop:enable Metrics/AbcSize + # rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity + + def catch_interrupt + interrupt_params = catch(Datadog::AppSec::Ext::INTERRUPT) { return yield } + + if (context = Datadog::AppSec::Context.active) + context.mark_as_interrupted! + end + + response_override(interrupt_params, headers: @request.headers) + end private + def extract_api_security_schema(context, request:, response:) + return unless request && response + + if Datadog::AppSec::APISecurity.enabled? && + Datadog::AppSec::APISecurity.sample_trace?(context.trace) && + Datadog::AppSec::APISecurity.sample?(request, response) + context.extract_schema! + end + end + def enabled? defined?(Datadog::AppSec) && Datadog::AppSec.respond_to?(:enabled?) && @@ -93,34 +125,6 @@ def create_context(trace, span) context end - def tag_and_keep(context, cold_start:) - span = context.span - trace = context.trace - - return unless trace && span - - span.set_metric(Datadog::AppSec::Ext::TAG_APPSEC_ENABLED, 1) - span.set_tag('_dd.runtime_family', 'ruby') - span.set_tag('_dd.appsec.waf.version', Datadog::AppSec::WAF::VERSION::BASE_STRING) - - ruleset_version = context.waf_runner_ruleset_version - return unless ruleset_version - - span.set_tag('_dd.appsec.event_rules.version', ruleset_version) - - return unless cold_start - - span.set_tag( - '_dd.appsec.event_rules.addresses', JSON.dump(context.waf_runner_known_addresses) - ) - - trace.keep! - trace.set_tag( - Datadog::Tracing::Metadata::Ext::Distributed::TAG_DECISION_MAKER, - Datadog::Tracing::Sampling::Ext::Decision::ASM - ) - end - def response_override(interrupt_params, headers:) response = Datadog::AppSec::Response.from_interrupt_params( interrupt_params, headers['accept'] diff --git a/lib/datadog/lambda/appsec/request.rb b/lib/datadog/lambda/appsec/request.rb index 42926f29..dfeb726d 100644 --- a/lib/datadog/lambda/appsec/request.rb +++ b/lib/datadog/lambda/appsec/request.rb @@ -10,17 +10,16 @@ module AppSec # @see Datadog::AppSec::Event.record # @see Datadog::AppSec::Contrib::Rack::Gateway::Request class Request - attr_reader :host, :user_agent, :remote_addr, :headers + attr_reader :host, :user_agent, :remote_addr, :headers, + :request_method, :path class << self def from_normalized(event) - headers = lowercase_headers(event) - new( - host: headers['host'], - user_agent: headers['user-agent'], remote_addr: event['source_ip'], - headers: headers + headers: lowercase_headers(event), + request_method: event['method'], + path: event['path'] ) end @@ -33,11 +32,13 @@ def lowercase_headers(event) end end - def initialize(host:, user_agent:, remote_addr:, headers:) - @host = host - @user_agent = user_agent - @remote_addr = remote_addr + def initialize(remote_addr:, headers:, request_method:, path:) @headers = headers + @host = headers['host'] + @user_agent = headers['user-agent'] + @remote_addr = remote_addr + @request_method = request_method + @path = path end end end diff --git a/lib/datadog/lambda/appsec/response.rb b/lib/datadog/lambda/appsec/response.rb new file mode 100644 index 00000000..a1ff23fa --- /dev/null +++ b/lib/datadog/lambda/appsec/response.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Datadog + module Lambda + module AppSec + # Minimal response object for AppSec event recording. + # + # WARNING: It's a minimal data for interface compliance + # + # @see Datadog::AppSec::APISecurity.sample? + # @see Datadog::AppSec::Contrib::Rack::Gateway::Response + class Response + attr_reader :status, :headers, :body + + def self.from_normalized(normalized) + new( + status: normalized['status_code'].to_i, + headers: normalized['headers'] || {}, + body: normalized['body'] + ) + end + + def initialize(status:, headers:, body:) + @status = status + @headers = headers + @body = body + end + end + end + end +end diff --git a/lib/datadog/lambda/appsec/tagging.rb b/lib/datadog/lambda/appsec/tagging.rb new file mode 100644 index 00000000..37f2abd5 --- /dev/null +++ b/lib/datadog/lambda/appsec/tagging.rb @@ -0,0 +1,73 @@ +# frozen_string_literal: true + +require 'json' + +require 'datadog/tracing/client_ip' +require 'datadog/core/header_collection' +require 'datadog/appsec/default_header_tags' + +module Datadog + module Lambda + module AppSec + # Applies AppSec request, response, and keep-decision tags to the Lambda span + module Tagging + module_function + + def tag_request(request, context:) + span = context.span + return unless span + + headers = Datadog::Core::HeaderCollection.from_hash(request.headers) + Datadog::AppSec::DefaultHeaderTags.tag_request(span, headers) + + return if span.get_tag(Datadog::Tracing::Metadata::Ext::HTTP::TAG_CLIENT_IP) + + Datadog::Tracing::ClientIp.set_client_ip_tag!( + span, headers: headers, remote_ip: request.remote_addr + ) + end + + def tag_response(response, context:) + span = context.span + return unless span + + headers = Datadog::Core::HeaderCollection.from_hash(response.headers) + Datadog::AppSec::DefaultHeaderTags.tag_response(span, headers) + + return if headers.get('content-length') + + if (body = response.body) + span.set_tag('http.response.headers.content-length', body.bytesize.to_s) + end + end + + def tag_and_keep(context, cold_start:) + span = context.span + trace = context.trace + + return unless trace && span + + span.set_metric(Datadog::AppSec::Ext::TAG_APPSEC_ENABLED, 1) + span.set_tag('_dd.runtime_family', 'ruby') + span.set_tag('_dd.appsec.waf.version', Datadog::AppSec::WAF::VERSION::BASE_STRING) + + ruleset_version = context.waf_runner_ruleset_version + return unless ruleset_version + + span.set_tag('_dd.appsec.event_rules.version', ruleset_version) + + return unless cold_start + + trace.keep! + trace.set_tag( + Datadog::Tracing::Metadata::Ext::Distributed::TAG_DECISION_MAKER, + Datadog::Tracing::Sampling::Ext::Decision::ASM + ) + span.set_tag( + '_dd.appsec.event_rules.addresses', JSON.dump(context.waf_runner_known_addresses) + ) + end + end + end + end +end diff --git a/lib/datadog/lambda/trace/listener.rb b/lib/datadog/lambda/trace/listener.rb index c89a8be6..0528e22a 100644 --- a/lib/datadog/lambda/trace/listener.rb +++ b/lib/datadog/lambda/trace/listener.rb @@ -61,6 +61,10 @@ def on_start(event:, request_context:, cold_start:) end # rubocop:enable Metrics/AbcSize + def with_appsec(&block) + @response_override || Datadog::Lambda::AppSec.catch_interrupt(&block) + end + def on_end(response:, request_context:) if (override = Datadog::Lambda::AppSec.on_finish(response)) @response_override = override diff --git a/test/datadog/lambda.spec.rb b/test/datadog/lambda.spec.rb index 804f5411..7d52c216 100644 --- a/test/datadog/lambda.spec.rb +++ b/test/datadog/lambda.spec.rb @@ -41,6 +41,9 @@ context 'when previous invocation successful response followed by a failing invocation' do before do + # NOTE: we don't need appsec here + allow(listener).to receive(:with_appsec) { |&block| block.call } + Datadog::Lambda.instance_variable_set(:@listener, listener) Datadog::Lambda.wrap('1', ctx) { {'statusCode' => 200} } Datadog::Lambda.wrap('1', ctx) { raise 'boom' } rescue nil diff --git a/test/datadog/lambda/appsec.spec.rb b/test/datadog/lambda/appsec.spec.rb index 8628f52b..60272061 100644 --- a/test/datadog/lambda/appsec.spec.rb +++ b/test/datadog/lambda/appsec.spec.rb @@ -12,17 +12,18 @@ end let(:gateway) { instance_double(Datadog::AppSec::Instrumentation::Gateway) } - let(:context_span) { instance_double(Datadog::Tracing::SpanOperation, set_tag: nil, set_metric: nil) } + let(:context_span) { instance_double(Datadog::Tracing::SpanOperation, set_tag: nil, set_metric: nil, get_tag: nil) } let(:appsec_context) do instance_double( Datadog::AppSec::Context, - span: context_span, state: {}, export_metrics: nil, export_request_telemetry: nil + span: context_span, state: {}, interrupted?: false, + export_metrics: nil, export_request_telemetry: nil ) end describe '.on_start' do let(:trace) { instance_double(Datadog::Tracing::TraceOperation) } - let(:span) { instance_double(Datadog::Tracing::SpanOperation, set_metric: nil, set_tag: nil) } + let(:span) { instance_double(Datadog::Tracing::SpanOperation, set_metric: nil, set_tag: nil, get_tag: nil) } context 'when appsec is disabled' do before do @@ -253,6 +254,50 @@ end end + describe '.catch_interrupt' do + context 'when the handler returns normally' do + it { expect(described_class.catch_interrupt { 'result' }).to eq('result') } + it { expect(described_class.catch_interrupt { nil }).to be_nil } + end + + context 'when the handler throws a blocking interrupt' do + before do + allow(Datadog::AppSec).to receive_messages(enabled?: true, security_engine: security_engine) + allow(Datadog::AppSec::Context).to receive_messages(new: appsec_context, activate: nil, active: appsec_context) + allow(appsec_context).to receive_messages( + trace: trace, waf_runner_ruleset_version: nil, mark_as_interrupted!: nil + ) + + described_class.on_start({'httpMethod' => 'GET', 'path' => '/'}, trace: trace, span: span) + end + + let(:security_engine) { instance_double(Datadog::AppSec::SecurityEngine::Engine, new_runner: waf_runner) } + let(:waf_runner) { instance_double(Datadog::AppSec::SecurityEngine::Runner, ruleset_version: nil) } + let(:trace) { instance_double(Datadog::Tracing::TraceOperation) } + let(:span) { instance_double(Datadog::Tracing::SpanOperation, set_metric: nil, set_tag: nil, get_tag: nil) } + + it 'returns a Lambda-shaped blocking response' do + result = described_class.catch_interrupt do + throw(Datadog::AppSec::Ext::INTERRUPT, {'status_code' => 403, 'type' => 'auto'}) + end + + expect(result).to include( + 'statusCode' => 403, + 'headers' => {'Content-Type' => 'application/json'}, + 'body' => include('blocked') + ) + end + + it 'marks the context as interrupted' do + described_class.catch_interrupt do + throw(Datadog::AppSec::Ext::INTERRUPT, {'status_code' => 403, 'type' => 'auto'}) + end + + expect(appsec_context).to have_received(:mark_as_interrupted!) + end + end + end + describe '.on_finish' do context 'when appsec is disabled' do before do @@ -283,6 +328,7 @@ allow(Datadog::AppSec).to receive(:enabled?).and_return(true) allow(Datadog::AppSec::Context).to receive_messages(active: appsec_context, deactivate: nil) allow(Datadog::AppSec::Event).to receive(:record) + allow(Datadog::AppSec::APISecurity).to receive(:enabled?).and_return(false) end it 'pushes response and records events' do @@ -323,7 +369,7 @@ end let(:trace) { instance_double(Datadog::Tracing::TraceOperation) } - let(:span) { instance_double(Datadog::Tracing::SpanOperation, set_metric: nil, set_tag: nil) } + let(:span) { instance_double(Datadog::Tracing::SpanOperation, set_metric: nil, set_tag: nil, get_tag: nil) } let(:security_engine) { instance_double(Datadog::AppSec::SecurityEngine::Engine, new_runner: waf_runner) } let(:waf_runner) { instance_double(Datadog::AppSec::SecurityEngine::Runner, ruleset_version: nil) } @@ -350,7 +396,7 @@ end let(:trace) { instance_double(Datadog::Tracing::TraceOperation) } - let(:span) { instance_double(Datadog::Tracing::SpanOperation, set_metric: nil, set_tag: nil) } + let(:span) { instance_double(Datadog::Tracing::SpanOperation, set_metric: nil, set_tag: nil, get_tag: nil) } let(:security_engine) { instance_double(Datadog::AppSec::SecurityEngine::Engine, new_runner: waf_runner) } let(:waf_runner) { instance_double(Datadog::AppSec::SecurityEngine::Runner, ruleset_version: nil) } diff --git a/test/datadog/lambda/appsec/response.spec.rb b/test/datadog/lambda/appsec/response.spec.rb new file mode 100644 index 00000000..7159600a --- /dev/null +++ b/test/datadog/lambda/appsec/response.spec.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require 'datadog/lambda/appsec/response' + +RSpec.describe Datadog::Lambda::AppSec::Response do + subject(:response) { described_class.from_normalized(normalized) } + + let(:normalized) do + { + 'status_code' => 200, + 'headers' => {'content-type' => 'application/json'}, + 'body' => '{"ok":true}' + } + end + + describe '#status' do + it { expect(response.status).to eq(200) } + + context 'when status_code is a string' do + let(:normalized) { {'status_code' => '403'} } + + it { expect(response.status).to eq(403) } + end + + context 'when status_code is absent' do + let(:normalized) { {'headers' => {}} } + + it { expect(response.status).to eq(0) } + end + end + + describe '#headers' do + it { expect(response.headers).to eq('content-type' => 'application/json') } + + context 'when headers are absent' do + let(:normalized) { {'status_code' => 204} } + + it { expect(response.headers).to eq({}) } + end + end + + describe '#body' do + it { expect(response.body).to eq('{"ok":true}') } + + context 'when body is absent' do + let(:normalized) { {'status_code' => 204} } + + it { expect(response.body).to be_nil } + end + end +end diff --git a/test/datadog/lambda/appsec/tagging.spec.rb b/test/datadog/lambda/appsec/tagging.spec.rb new file mode 100644 index 00000000..fc269a38 --- /dev/null +++ b/test/datadog/lambda/appsec/tagging.spec.rb @@ -0,0 +1,195 @@ +# frozen_string_literal: true + +require 'datadog/lambda' +require 'datadog/lambda/appsec/tagging' + +RSpec.describe Datadog::Lambda::AppSec::Tagging do + before do + stub_const('Datadog::AppSec::WAF::VERSION::BASE_STRING', '1.30.0') + + allow(Datadog::AppSec::DefaultHeaderTags).to receive(:tag_request) + allow(Datadog::AppSec::DefaultHeaderTags).to receive(:tag_response) + allow(Datadog::Tracing::ClientIp).to receive(:set_client_ip_tag!) + end + + let(:span) do + instance_double(Datadog::Tracing::SpanOperation, set_tag: nil, set_metric: nil, get_tag: nil) + end + let(:trace) { instance_double(Datadog::Tracing::TraceOperation, keep!: nil, set_tag: nil) } + let(:context) do + instance_double( + Datadog::AppSec::Context, + span: span, trace: trace, + waf_runner_ruleset_version: '1.12.0', + waf_runner_known_addresses: ['server.request.headers.no_cookies'] + ) + end + + describe '.tag_request' do + subject(:tag_request) { described_class.tag_request(request, context: context) } + + let(:request) do + instance_double( + Datadog::Lambda::AppSec::Request, + headers: {'user-agent' => 'TestBot'}, remote_addr: '1.2.3.4' + ) + end + + it 'applies default request header tags' do + tag_request + + expect(Datadog::AppSec::DefaultHeaderTags).to have_received(:tag_request).with( + span, kind_of(Datadog::Core::HeaderCollection) + ) + end + + it 'sets the client IP tag from the remote address' do + tag_request + + expect(Datadog::Tracing::ClientIp).to have_received(:set_client_ip_tag!).with( + span, headers: kind_of(Datadog::Core::HeaderCollection), remote_ip: '1.2.3.4' + ) + end + + context 'when the client IP tag is already set' do + before do + allow(span).to receive(:get_tag) + .with(Datadog::Tracing::Metadata::Ext::HTTP::TAG_CLIENT_IP).and_return('9.9.9.9') + end + + it 'does not overwrite the client IP tag' do + tag_request + + expect(Datadog::Tracing::ClientIp).not_to have_received(:set_client_ip_tag!) + end + end + + context 'when the span is missing' do + before { allow(context).to receive(:span).and_return(nil) } + + it 'does not tag anything' do + tag_request + + expect(Datadog::AppSec::DefaultHeaderTags).not_to have_received(:tag_request) + end + end + end + + describe '.tag_response' do + subject(:tag_response) { described_class.tag_response(response, context: context) } + + let(:response) do + instance_double( + Datadog::Lambda::AppSec::Response, + headers: {'content-type' => 'text/html'}, body: 'hello' + ) + end + + it 'applies default response header tags' do + tag_response + + expect(Datadog::AppSec::DefaultHeaderTags).to have_received(:tag_response).with( + span, kind_of(Datadog::Core::HeaderCollection) + ) + end + + it 'sets content-length from the body byte size' do + tag_response + + expect(span).to have_received(:set_tag).with('http.response.headers.content-length', '5') + end + + context 'when a content-length header is present' do + let(:response) do + instance_double( + Datadog::Lambda::AppSec::Response, + headers: {'content-length' => '10'}, body: 'hello' + ) + end + + it 'does not derive content-length from the body' do + tag_response + + expect(span).not_to have_received(:set_tag).with('http.response.headers.content-length', anything) + end + end + + context 'when the span is missing' do + before { allow(context).to receive(:span).and_return(nil) } + + it 'does not tag anything' do + tag_response + + expect(Datadog::AppSec::DefaultHeaderTags).not_to have_received(:tag_response) + end + end + end + + describe '.tag_and_keep' do + subject(:tag_and_keep) { described_class.tag_and_keep(context, cold_start: false) } + + it 'marks the span as appsec-enabled with runtime and WAF version' do + tag_and_keep + + aggregate_failures('appsec span tags') do + expect(span).to have_received(:set_metric).with(Datadog::AppSec::Ext::TAG_APPSEC_ENABLED, 1) + expect(span).to have_received(:set_tag).with('_dd.runtime_family', 'ruby') + expect(span).to have_received(:set_tag).with('_dd.appsec.waf.version', '1.30.0') + end + end + + it 'sets the event rules version from the WAF runner' do + tag_and_keep + + expect(span).to have_received(:set_tag).with('_dd.appsec.event_rules.version', '1.12.0') + end + + it 'does not keep the trace on a warm start' do + tag_and_keep + + aggregate_failures('no cold start tags') do + expect(trace).not_to have_received(:keep!) + expect(span).not_to have_received(:set_tag).with('_dd.appsec.event_rules.addresses', anything) + end + end + + context 'when the ruleset version is not set' do + before { allow(context).to receive(:waf_runner_ruleset_version).and_return(nil) } + + it 'skips the event rules tags' do + tag_and_keep + + expect(span).not_to have_received(:set_tag).with('_dd.appsec.event_rules.version', anything) + end + end + + context 'when cold_start is true' do + subject(:tag_and_keep) { described_class.tag_and_keep(context, cold_start: true) } + + it 'keeps the trace and records known addresses' do + tag_and_keep + + aggregate_failures('cold start tags') do + expect(trace).to have_received(:keep!) + expect(trace).to have_received(:set_tag).with( + Datadog::Tracing::Metadata::Ext::Distributed::TAG_DECISION_MAKER, + Datadog::Tracing::Sampling::Ext::Decision::ASM + ) + expect(span).to have_received(:set_tag).with( + '_dd.appsec.event_rules.addresses', '["server.request.headers.no_cookies"]' + ) + end + end + end + + context 'when the trace is missing' do + before { allow(context).to receive(:trace).and_return(nil) } + + it 'does not tag anything' do + tag_and_keep + + expect(span).not_to have_received(:set_metric) + end + end + end +end diff --git a/test/datadog/lambda/trace/listener.spec.rb b/test/datadog/lambda/trace/listener.spec.rb new file mode 100644 index 00000000..ed5f9169 --- /dev/null +++ b/test/datadog/lambda/trace/listener.spec.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require 'datadog/lambda/trace/listener' + +RSpec.describe Datadog::Trace::Listener do + subject(:listener) do + described_class.new(handler_name: 'h', function_name: 'f', patch_http: false, merge_xray_traces: false) + end + + describe '#with_appsec' do + context 'when a request-phase override is present' do + before { listener.instance_variable_set(:@response_override, {'statusCode' => 403}) } + + it { expect(listener.with_appsec { 'handler' }).to eq({'statusCode' => 403}) } + + it 'does not run the handler' do + expect { |b| listener.with_appsec(&b) }.not_to yield_control + end + end + + context 'when no override is present' do + before { allow(Datadog::Lambda::AppSec).to receive(:catch_interrupt) { |&blk| blk.call } } + + it { expect(listener.with_appsec { 'handler' }).to eq('handler') } + end + end +end