Skip to content

[python] Add APPSEC_OTLP_EXPORT scenarios - #7578

Draft
RamyElkest wants to merge 9 commits into
mainfrom
ramy.elkest/add-appsec-otlp-export-scenarios-APPSEC-68836
Draft

[python] Add APPSEC_OTLP_EXPORT scenarios#7578
RamyElkest wants to merge 9 commits into
mainfrom
ramy.elkest/add-appsec-otlp-export-scenarios-APPSEC-68836

Conversation

@RamyElkest

@RamyElkest RamyElkest commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Summary

Validates AAP for DD Tracer exporting traces via OTLP + DD Agent

Closes APPSEC-68836

What's New

Two Scenarios

Scenario Ruleset Purpose
APPSEC_OTLP_EXPORT Blocking rules (31) WAF blocking, fingerprinting, trace tagging, security response IDs
APPSEC_OTLP_EXPORT_DEFAULT_RULES Default ruleset (199) Alpha detection, obfuscator, header collection, payment events

Both use: include_opentelemetry=True + include_agent=True + rc_api_enabled=True + appsec_enabled=True

OTLP-to-Datadog Span Adapter (_datadog_library_trace_otlp.py)

Wraps OTLP spans into the DataDogLibrarySpan interface so existing AppSec tests work unchanged:

  • Flatten nested resourceSpansscopeSpansspans
  • Coerce string attribute values to int/float for metrics
  • Decode base64+msgpack appsec trigger data
  • Map startTimeUnixNano/endTimeUnixNanostart/duration
  • Coerce _dd.top_level and _sampling_priority_v1 for RID matching

Library Interface Fallback (_library/core.py)

  • get_traces() falls back to interfaces.open_telemetry when no Datadog-format traces found
  • get_appsec_events() decodes base64 appsec data via _decode_appsec_data()

Test Results

Scenario Passed Failed XFailed
APPSEC_OTLP_EXPORT 91 0 4
APPSEC_OTLP_EXPORT_DEFAULT_RULES 18 0 0
Total 109 0 4

CI Integration

  • Scenarios added to .github/workflows/run-end-to-end.yml
  • Part of tracer_release group — runs on every tracer release PR
  • Dynamic selection — triggered when test files with @scenarios.appsec_otlp_export are modified

Files Changed

  • New: utils/dd_types/_datadog_library_trace_otlp.py — OTLP span adapter
  • New: tests/appsec/test_appsec_otlp_spike.py — spike validation tests
  • Modified: utils/_context/_scenarios/__init__.py — 2 new scenario definitions
  • Modified: utils/interfaces/_library/core.py — OTLP fallback + appsec decoder
  • Modified: utils/dd_types/__init__.py — export new classes
  • Modified: 13 test files in tests/appsec/@scenarios decorators
  • Modified: .github/workflows/run-end-to-end.yml — CI steps
  • Modified: tests/test_the_test/test_group_rules.py — exclusion list

… (APPSEC-68836)

Validate M2 topology: DD Tracer exporting traces via OTLP + DD Agent for Remote Config.

New scenarios:
- APPSEC_OTLP_EXPORT: blocking ruleset, tests WAF blocking + detection
- APPSEC_OTLP_EXPORT_DEFAULT_RULES: default ruleset, tests alpha/obfuscator/payment

OTLP-to-Datadog span adapter:
- DataDogLibraryTraceOTLP/DataDogLibrarySpanOTLP wraps OTLP spans
- Numeric coercion for metric keys (string -> int/float)
- Base64+msgpack decoding for appsec trigger data
- Timestamp mapping (startTimeUnixNano -> start/duration)
- RID matching via _dd.top_level coercion

Library interface fallback:
- get_traces() falls back to interfaces.open_telemetry when no DD-format traces
- get_appsec_events() decodes base64 appsec data from OTLP

Test results: 109 passed, 0 failed, 4 xfailed across both scenarios.

CI: scenarios run on tracer release PRs and when test files are modified.
@RamyElkest
RamyElkest requested review from a team as code owners August 25, 2026 11:42
@github-actions

github-actions Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

CODEOWNERS have been resolved as:

docs/plans/aap-native-otel-collector-weblogs.md                         @DataDog/system-tests-core
tests/appsec/smoke_tests/test_otlp_export.py                            @DataDog/asm-libraries @DataDog/system-tests-core
utils/dd_types/_datadog_library_trace_otlp.py                           @DataDog/system-tests-core
.github/workflows/run-end-to-end.yml                                    @DataDog/system-tests-core
tests/appsec/iast/sink/test_hardcoded_passwords.py                      @DataDog/asm-libraries @DataDog/system-tests-core
tests/appsec/iast/sink/test_hardcoded_secrets.py                        @DataDog/asm-libraries @DataDog/system-tests-core
tests/appsec/iast/sink/test_hsts_missing_header.py                      @DataDog/asm-libraries @DataDog/system-tests-core
tests/appsec/iast/sink/test_insecure_auth_protocol.py                   @DataDog/asm-libraries @DataDog/system-tests-core
tests/appsec/iast/sink/test_trust_boundary_violation.py                 @DataDog/asm-libraries @DataDog/system-tests-core
tests/appsec/iast/sink/test_xcontent_sniffing.py                        @DataDog/asm-libraries @DataDog/system-tests-core
tests/appsec/test_agentic_onboarding.py                                 @DataDog/asm-libraries @DataDog/system-tests-core
tests/appsec/test_alpha.py                                              @DataDog/asm-libraries @DataDog/system-tests-core
tests/appsec/test_automated_login_events.py                             @DataDog/asm-libraries @DataDog/system-tests-core
tests/appsec/test_automated_payment_events.py                           @DataDog/asm-libraries @DataDog/system-tests-core
tests/appsec/test_automated_user_and_session_tracking.py                @DataDog/asm-libraries @DataDog/system-tests-core
tests/appsec/test_blocking_addresses.py                                 @DataDog/asm-libraries @DataDog/system-tests-core
tests/appsec/test_conf.py                                               @DataDog/asm-libraries @DataDog/system-tests-core
tests/appsec/test_event_tracking.py                                     @DataDog/asm-libraries @DataDog/system-tests-core
tests/appsec/test_fingerprinting.py                                     @DataDog/asm-libraries @DataDog/system-tests-core
tests/appsec/test_identify.py                                           @DataDog/asm-libraries @DataDog/system-tests-core
tests/appsec/test_metastruct.py                                         @DataDog/asm-libraries @DataDog/system-tests-core
tests/appsec/test_reports.py                                            @DataDog/asm-libraries @DataDog/system-tests-core
tests/appsec/test_shell_execution.py                                    @DataDog/asm-libraries @DataDog/system-tests-core
tests/appsec/test_span_tags_headers.py                                  @DataDog/asm-libraries @DataDog/system-tests-core
tests/appsec/test_trace_tagging.py                                      @DataDog/asm-libraries @DataDog/system-tests-core
tests/appsec/test_traces.py                                             @DataDog/asm-libraries @DataDog/system-tests-core
tests/appsec/test_versions.py                                           @DataDog/asm-libraries @DataDog/system-tests-core
tests/appsec/waf/test_addresses.py                                      @DataDog/asm-libraries @DataDog/system-tests-core
tests/appsec/waf/test_blocking.py                                       @DataDog/asm-libraries @DataDog/system-tests-core
tests/appsec/waf/test_blocking_security_response_id.py                  @DataDog/asm-libraries @DataDog/system-tests-core
tests/appsec/waf/test_miscs.py                                          @DataDog/asm-libraries @DataDog/system-tests-core
tests/appsec/waf/test_rules.py                                          @DataDog/asm-libraries @DataDog/system-tests-core
tests/appsec/waf/test_truncation.py                                     @DataDog/asm-libraries @DataDog/system-tests-core
tests/test_the_test/test_group_rules.py                                 @DataDog/system-tests-core
utils/_context/_scenarios/__init__.py                                   @DataDog/system-tests-core
utils/dd_types/__init__.py                                              @DataDog/system-tests-core
utils/interfaces/_library/core.py                                       @DataDog/system-tests-core

@RamyElkest RamyElkest changed the title Add APPSEC_OTLP_EXPORT scenarios for M2 topology (APPSEC-68836) Add APPSEC_OTLP_EXPORT scenarios Aug 25, 2026
@datadog-official

datadog-official Bot commented Aug 25, 2026

Copy link
Copy Markdown

Pipelines  Tests

Unblock PR with BitsAI

⚠️ Warnings

Your PR has failed checks. Please review the issues below and take necessary action before merging.

🚦 130 Pipeline jobs failed

Testing the test | System Tests (cpp_nginx, dev) / End-to-end #1 / nginx 1 — ❌ 1 test failed · 🔧 Needs a code fix, caused by this PR

View more details · View in GitHub Actions

AttributeError: 'DdTraceEndToEndScenario' object has no attribute 'postgres_container' in tests/otel_postgres_metrics_e2e/test_postgres_metrics.py

❌ pytest.internal[nginx] from system_tests_suite
internal error

Traceback (most recent call last):
  File "/home/runner/work/system-tests/system-tests/venv/lib/python3.12/site-packages/_pytest/main.py", line 660, in perform_collect
    hook.pytest_collection_modifyitems(
  File "/home/runner/work/system-tests/system-tests/venv/lib/python3.12/site-packages/pluggy/_hooks.py", line 265, in __call__
    return self._hookexec(self.name, self.get_hookimpls(), kwargs, firstresult)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/work/system-tests/system-tests/venv/lib/python3.12/site-packages/pluggy/_manager.py", line 80, in _hookexec
    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
...
Testing the test | System Tests (dotnet, dev) / End-to-end #2 / poc 2 — ❌ 1 test failed · 🔧 Needs a code fix, caused by this PR

View more details · View in GitHub Actions

AssertionError: Please add scenario group TELEMETRY to scenario APPSEC_OTLP_EXPORT during test setup in tests/debugger/test_debugger_telemetry.py

❌ pytest.internal[poc] from system_tests_suite
internal error

Traceback (most recent call last):
  File "/home/runner/work/system-tests/system-tests/venv/lib/python3.12/site-packages/_pytest/main.py", line 660, in perform_collect
    hook.pytest_collection_modifyitems(
  File "/home/runner/work/system-tests/system-tests/venv/lib/python3.12/site-packages/pluggy/_hooks.py", line 265, in __call__
    return self._hookexec(self.name, self.get_hookimpls(), kwargs, firstresult)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/work/system-tests/system-tests/venv/lib/python3.12/site-packages/pluggy/_manager.py", line 80, in _hookexec
    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
...
Testing the test | System Tests (dotnet, prod) / End-to-end #2 / uds 2 — ❌ 1 test failed · 🔧 Needs a code fix, caused by this PR

View more details · View in GitHub Actions

AssertionError: Please add scenario group TELEMETRY to scenario APPSEC_OTLP_EXPORT.

❌ pytest.internal[uds] from system_tests_suite
internal error

Traceback (most recent call last):
  File "/home/runner/work/system-tests/system-tests/venv/lib/python3.12/site-packages/_pytest/main.py", line 660, in perform_collect
    hook.pytest_collection_modifyitems(
  File "/home/runner/work/system-tests/system-tests/venv/lib/python3.12/site-packages/pluggy/_hooks.py", line 265, in __call__
    return self._hookexec(self.name, self.get_hookimpls(), kwargs, firstresult)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/work/system-tests/system-tests/venv/lib/python3.12/site-packages/pluggy/_manager.py", line 80, in _hookexec
    return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
...

View all 130 failed jobs.

📋 Copy fix prompt
CI on my pull request is failing. Help me find and fix the root cause of each failing job below — they were flagged as caused by changes in this PR, so focus on the diff. For each job, explain the failure and propose a fix.

Before you start, set up the Datadog software-delivery tooling so you can
query the CI data yourself:

1. Check whether you already have the Datadog software-delivery MCP tools
   (e.g. a `search_datadog_ci_pipeline_events` tool) and the `unblock-pr` skill.
2. If either is missing, STOP and ask me for permission before installing
   anything. Do not install or run anything until I have said yes.
3. Only with my explicit approval, set up the Datadog software-delivery MCP
   server and skills by following:
     https://docs.datadoghq.com/getting_started/software_delivery_mcp_tools/
   then restart so the skill is picked up.
4. If I decline, skip all of the above and work from the context below alone.

Then run /unblock-pr — it will pull the CI data itself. The job context below is what we already know.

If /unblock-pr is not available — because I declined the setup above, or it did not install — work from the context below instead.

Datadog has already classified this failure as caused by changes in this PR.
Take that as given and work the fix:

1. Locate the change. Diff this branch against its base and find the change
   that produces this error. Explain the mechanism, don't just name a file:
     git fetch origin && git diff $(git merge-base origin/main HEAD)...HEAD
2. Reproduce it locally. Run the failing job's command or test before
   proposing anything.
3. Propose the smallest fix that addresses the root cause — not a workaround,
   not a broadened assertion, not a disabled or skipped test.
4. Re-run the same command to confirm, and say exactly what you ran.
5. If the failure turns out to be intermittent rather than deterministic, say
   so plainly instead of "fixing" it — that is a flaky test, and patching it
   hides the problem.

If the right move is to re-run the job rather than change code, use the job
link in the context below. For GitHub Actions: `gh run rerun <run-id> --failed`,
where the run ID is the number after `/runs/` in that URL (not the trailing
number, which is the job ID).

Branch: ramy.elkest/add-appsec-otlp-export-scenarios-APPSEC-68836

Testing the test | System Tests (cpp_nginx, dev) / End-to-end #1 / nginx 1
Commit: 20cfed2a282d57c98bf9e62600a6524c2ddeb793
Error (code / test):
AttributeError: 'DdTraceEndToEndScenario' object has no attribute 'postgres_container' in tests/otel_postgres_metrics_e2e/test_postgres_metrics.py
CI job: https://github.com/DataDog/system-tests/actions/runs/32857138798/job/97954479010

Testing the test | System Tests (dotnet, dev) / End-to-end #2 / poc 2
Commit: 20cfed2a282d57c98bf9e62600a6524c2ddeb793
Error (code / test):
AssertionError: Please add scenario group TELEMETRY to scenario APPSEC_OTLP_EXPORT during test setup in tests/debugger/test_debugger_telemetry.py
CI job: https://github.com/DataDog/system-tests/actions/runs/32857138798/job/97954474152

Testing the test | System Tests (dotnet, prod) / End-to-end #2 / uds 2
Commit: 20cfed2a282d57c98bf9e62600a6524c2ddeb793
Error (code / test):
AssertionError: Please add scenario group TELEMETRY to scenario APPSEC_OTLP_EXPORT.
CI job: https://github.com/DataDog/system-tests/actions/runs/32857138798/job/97954478368

Plus 122 more failing jobs not shown here.

ℹ️ Info

No other issues found (see more)

❄️ No new flaky tests detected

🔄 Datadog auto-retried 157 jobs - 1 passed on retry View in Datadog

Useful? React with 👍 / 👎

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 60792d6 | Docs | View more details | Give us feedback!

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e75e9866a5

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +1431 to +1432
"OTEL_EXPORTER_OTLP_TRACES_ENDPOINT": f"http://proxy:{ProxyPorts.open_telemetry_weblog}/v1/traces",
"OTEL_EXPORTER_OTLP_TRACES_HEADERS": "dd-protocol=otlp,dd-otlp-path=agent",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Route the OTLP data plane through the collector

In both new scenarios, dd-otlp-path=agent makes the proxy forward /v1/traces directly to agent:4318 (utils/proxy/core.py lines 227–237); the scenario also never creates an OpenTelemetryCollectorContainer. Consequently these tests validate DD Tracer → DD Agent OTLP ingestion, not the advertised M2 topology through a native OTel Collector, so collector-specific AppSec regressions remain completely untested.

Useful? React with 👍 / 👎.

Comment thread tests/appsec/test_appsec_otlp_spike.py Outdated
Comment on lines +59 to +60
@scenarios.appsec_otlp_export
class Test_AppSec_OTLP_Blocking:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Associate every new test class with a feature

All four classes added in this file have only the scenario decorator and no @features.<feature> or @features.not_reported decorator. This violates the repository's required feature association and causes tests/test_the_test/test_features.py::test_all_class_has_feature_decorator to reject the collected suite, in addition to omitting these tests from feature reporting. .cursor/rules/repository-structure.mdcL83-L86

Useful? React with 👍 / 👎.

Comment on lines +237 to +241
spans = []
for scope_span in resource_span.get("scopeSpans", []):
for raw_span in scope_span.get("spans", []):
spans.append(DataDogLibrarySpanOTLP(self, raw_span, resource_attrs))
self.spans = spans

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Split resource spans by trace ID

An OTLP ResourceSpans groups spans by resource and may contain spans from multiple trace IDs, but this constructor wraps every span in the resource as one DataDogLibraryTraceOTLP and derives its trace ID from the first span. Whenever the exporter batches requests, later spans therefore report the wrong span["trace_id"], and callers using full_trace=True or iterating the returned trace receive unrelated traces; group the spans by traceId before constructing each trace.

Useful? React with 👍 / 👎.

Comment on lines +251 to +254
def trace_id_as_int(self) -> int:
tid = self.trace_id
if isinstance(tid, str) and tid:
return int(tid, 16) & 0xFFFFFFFFFFFFFFFF

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Decode binary-protobuf trace IDs before integer conversion

For OTLP sent as binary protobuf, MessageToDict represents the 16-byte traceId as base64 rather than hexadecimal (the existing _trace_id_to_hex helper in tests/otel/test_tracing_otlp.py explicitly handles both encodings). Because these scenarios do not force JSON transport, trace_id_as_int raises ValueError whenever trace_id_equals() or another existing trace-ID validator encounters a normal binary OTLP payload; decode the base64 form before applying the lower-64-bit mask.

Useful? React with 👍 / 👎.

Comment thread tests/appsec/test_appsec_otlp_spike.py Outdated
Comment on lines +31 to +35
for data in interfaces.open_telemetry.get_data(path_filters=["/v1/traces", "/api/v0.2/traces"]):
content = data.get("request", {}).get("content", {})
text = json.dumps(content)
for m in re.finditer(r'"([^"]*appsec[^"]*)"\s*:', text, re.IGNORECASE):
appsec_keys.add(m.group(1))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Restrict spike assertions to the request under test

Both OTLP search helpers scan every payload accumulated by interfaces.open_telemetry instead of filtering by the RID from each test's self.r. After any earlier setup exports an AppSec event, later tests can pass using that stale span even when their own request was not exported or lacks the required attributes, masking request-specific regressions; pass the current response into these helpers and inspect only its matching OTLP spans.

Useful? React with 👍 / 👎.

Comment on lines +125 to +128
if not trace_found:
# Fallback: try OTLP data from interfaces.open_telemetry
# This allows tests to work when traces are exported via OTLP instead of Datadog agent protocol
yield from self._get_traces_from_otlp(rid)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Require OTLP traces instead of preferring native traces

The OTLP adapter is used only when no matching Datadog-protocol trace was found. If a tracer ignores the new OTLP exporter setting, lacks support for it, or emits both formats, every reused AppSec assertion silently validates the ordinary native trace and the scenario remains green without proving that any AppSec data survived OTLP export; these OTLP-specific scenarios should prefer or explicitly require a matching trace from interfaces.open_telemetry.

Useful? React with 👍 / 👎.

@RamyElkest
RamyElkest marked this pull request as draft August 25, 2026 11:56
…nts, IAST, metastruct (APPSEC-68836)

Added @scenarios.appsec_otlp_export_default_rules to 73 additional test classes:
- WAF rules (test_rules.py): 13 classes — Scanners, SQLI, XSS, LFI, SSRF, etc.
- WAF addresses (test_addresses.py): 12 classes — UrlQuery, Headers, Cookies, BodyJson, etc.
- Login events (test_event_tracking.py, test_automated_login_events.py): 9 classes
- User identification (test_identify.py): 1 class
- IAST (test_hardcoded_secrets.py, test_hsts_missing_header.py, etc.): 16 classes
- Metastruct (test_metastruct.py): 2 classes
- Misc (test_miscs.py, test_truncation.py, test_conf.py): 5 classes
- Reports (test_reports.py): 2 classes
- Agentic onboarding: 1 class

Adapter fixes:
- Added 'iast' and '_dd.iast.json' to _APPSEC_DATA_KEYS for base64 decoding
- Added 'service' field mapping from OTLP resource attribute 'service.name'

Test results: 177 passed, 25 skipped, 36 xfailed, 0 failed.
@RamyElkest RamyElkest changed the title Add APPSEC_OTLP_EXPORT scenarios [DO NOT MERGE] Add APPSEC_OTLP_EXPORT scenarios Aug 25, 2026
Added tests/appsec/smoke_tests/test_otlp_export.py with:
- Threats smoke tests (WAF attack detection via agent interface)
- UserEvents smoke tests (user login event tracking via agent interface)

Both run in APPSEC_OTLP_EXPORT and APPSEC_OTLP_EXPORT_DEFAULT_RULES.
RASP, RemoteConfig, and ApiSecurity smoke tests require additional scenario
configuration not yet available in OTLP export scenarios.

Test results: 4 smoke tests passed, 0 failed.
- Fix mypy list invariance: type annotate spans as list[DataDogLibrarySpan]
- Add @features.not_reported to spike test classes (required by test_all_class_has_feature_decorator)
@RamyElkest RamyElkest changed the title [DO NOT MERGE] Add APPSEC_OTLP_EXPORT scenarios [PYTHON] Add APPSEC_OTLP_EXPORT scenarios Aug 25, 2026
@RamyElkest RamyElkest changed the title [PYTHON] Add APPSEC_OTLP_EXPORT scenarios [python] Add APPSEC_OTLP_EXPORT scenarios Aug 25, 2026
The spike tests were diagnostic tools to validate the OTLP adapter during development.
They fail on non-Python languages (Ruby, Node.js) that don't support OTLP export.
The real test coverage is in the 177 tests added to existing test files.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant