Skip to content

AI Guard: end-to-end tests for SDS redaction - #7385

Open
avara1986 wants to merge 8 commits into
mainfrom
alberto.vara/ai-guard-redaction-tests
Open

AI Guard: end-to-end tests for SDS redaction#7385
avara1986 wants to merge 8 commits into
mainfrom
alberto.vara/ai-guard-redaction-tests

Conversation

@avara1986

@avara1986 avara1986 commented Jul 27, 2026

Copy link
Copy Markdown
Member

Motivation

This is a proof of concept. No tracer implements the AI Guard redaction contract yet, so every new test is marked missing_feature in all manifests. The goal is to agree on the shape of the cross-language assertions before libraries start implementing.

Jira: APPSEC-68867
RFC: SDS for AI Guard
Python Implementation: DataDog/dd-trace-py#19360

What this PR does

Adds three test classes to tests/ai_guard/test_ai_guard_sdk.py:

  • Test_Redaction — the backend returns a top-level redaction_replacements array of {path, replacement} pairs; the tracer must overwrite each path verbatim and store the redacted messages in the ai_guard meta struct. Assertions run against the meta struct because that is the cross-language, cross-provider surface. Covered shapes: single value, multiple messages with one finding, one message with several findings, mixed, tool result content, tool call arguments (must stay valid JSON), and system prompt.
  • Test_NoRedaction — benign payloads (plain message, benign tool-call conversation) come back with no redaction_replacements and unmodified messages.
  • Test_RedactionInSDKResponse — the SDK evaluate() response exposes the redaction_replacements contract, independent of the sds_findings detection metadata, which must still be present.

Keeping fixtures in sync

Scenarios (messages, expected replacements, raw sensitive values) live in tests/ai_guard/redaction_scenarios.json. The matching VCR cassettes under utils/build/docker/vcr/cassettes/aiguard/ are generated from that same file by utils/scripts/gen_redaction_cassettes.py, which computes the cassette filename hash from the request body — so the payloads the tests send and the responses the mock backend replays cannot drift. Regenerate both after editing a scenario.

Notes for reviewers

  • All new tests are missing_feature for cpp_httpd, cpp_nginx, dotnet, golang, java, nodejs, php, python, ruby.
  • The redaction_replacements field name and the path grammar (messages[0].content, messages[1].tool_calls[0].function.arguments) are the main things worth reviewing — they're taken from the RFC and encoded in _resolve_path.
  • Also includes a one-line .gitignore addition for a local tooling directory (.omc/).

🤖 Generated with Claude Code

Adds a proof-of-concept test suite for AI Guard sensitive-data redaction:

- Test_Redaction: asserts the tracer applies the backend
  redaction_replacements to the message payload stored in the ai_guard
  meta struct (single value, multi-message, multi-finding, mixed, tool
  result, tool arguments, system prompt).
- Test_NoRedaction: benign payloads are left untouched.
- Test_RedactionInSDKResponse: the SDK evaluate() response exposes the
  {path, replacement} contract alongside sds_findings.

Scenarios live in tests/ai_guard/redaction_scenarios.json and the
matching VCR cassettes are generated from it by
utils/scripts/gen_redaction_cassettes.py, so payloads and expected
replacements cannot drift.

Marked missing_feature in every manifest: no tracer implements the
redaction contract yet.

APPSEC-68867

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

CODEOWNERS have been resolved as:

tests/ai_guard/redaction_scenarios.json                                 @DataDog/k9-ai-guard @DataDog/system-tests-core
utils/build/docker/vcr/cassettes/aiguard/aiguard_evaluate_post_044c0044.json  @DataDog/k9-ai-guard @DataDog/system-tests-core
utils/build/docker/vcr/cassettes/aiguard/aiguard_evaluate_post_13cd8690.json  @DataDog/k9-ai-guard @DataDog/system-tests-core
utils/build/docker/vcr/cassettes/aiguard/aiguard_evaluate_post_171fbe88.json  @DataDog/k9-ai-guard @DataDog/system-tests-core
utils/build/docker/vcr/cassettes/aiguard/aiguard_evaluate_post_1e019487.json  @DataDog/k9-ai-guard @DataDog/system-tests-core
utils/build/docker/vcr/cassettes/aiguard/aiguard_evaluate_post_27197f80.json  @DataDog/k9-ai-guard @DataDog/system-tests-core
utils/build/docker/vcr/cassettes/aiguard/aiguard_evaluate_post_2a2bc7c5.json  @DataDog/k9-ai-guard @DataDog/system-tests-core
utils/build/docker/vcr/cassettes/aiguard/aiguard_evaluate_post_2e624e80.json  @DataDog/k9-ai-guard @DataDog/system-tests-core
utils/build/docker/vcr/cassettes/aiguard/aiguard_evaluate_post_2f61a849.json  @DataDog/k9-ai-guard @DataDog/system-tests-core
utils/build/docker/vcr/cassettes/aiguard/aiguard_evaluate_post_350bf191.json  @DataDog/k9-ai-guard @DataDog/system-tests-core
utils/build/docker/vcr/cassettes/aiguard/aiguard_evaluate_post_4289ced0.json  @DataDog/k9-ai-guard @DataDog/system-tests-core
utils/build/docker/vcr/cassettes/aiguard/aiguard_evaluate_post_44aefe7d.json  @DataDog/k9-ai-guard @DataDog/system-tests-core
utils/build/docker/vcr/cassettes/aiguard/aiguard_evaluate_post_5254e743.json  @DataDog/k9-ai-guard @DataDog/system-tests-core
utils/build/docker/vcr/cassettes/aiguard/aiguard_evaluate_post_611df616.json  @DataDog/k9-ai-guard @DataDog/system-tests-core
utils/build/docker/vcr/cassettes/aiguard/aiguard_evaluate_post_7ac75cfc.json  @DataDog/k9-ai-guard @DataDog/system-tests-core
utils/build/docker/vcr/cassettes/aiguard/aiguard_evaluate_post_7c2c34af.json  @DataDog/k9-ai-guard @DataDog/system-tests-core
utils/build/docker/vcr/cassettes/aiguard/aiguard_evaluate_post_8c66ae36.json  @DataDog/k9-ai-guard @DataDog/system-tests-core
utils/build/docker/vcr/cassettes/aiguard/aiguard_evaluate_post_969c2372.json  @DataDog/k9-ai-guard @DataDog/system-tests-core
utils/build/docker/vcr/cassettes/aiguard/aiguard_evaluate_post_b094b6c9.json  @DataDog/k9-ai-guard @DataDog/system-tests-core
utils/build/docker/vcr/cassettes/aiguard/aiguard_evaluate_post_b576eddc.json  @DataDog/k9-ai-guard @DataDog/system-tests-core
utils/build/docker/vcr/cassettes/aiguard/aiguard_evaluate_post_bcbe58c9.json  @DataDog/k9-ai-guard @DataDog/system-tests-core
utils/build/docker/vcr/cassettes/aiguard/aiguard_evaluate_post_d9f7be56.json  @DataDog/k9-ai-guard @DataDog/system-tests-core
utils/build/docker/vcr/cassettes/aiguard/aiguard_evaluate_post_e773c0de.json  @DataDog/k9-ai-guard @DataDog/system-tests-core
utils/build/docker/vcr/cassettes/aiguard/aiguard_evaluate_post_f548d24a.json  @DataDog/k9-ai-guard @DataDog/system-tests-core
utils/build/docker/vcr/cassettes/aiguard/aiguard_evaluate_post_f674ea5e.json  @DataDog/k9-ai-guard @DataDog/system-tests-core
utils/build/docker/vcr/cassettes/aiguard/aiguard_evaluate_post_fc2eedde.json  @DataDog/k9-ai-guard @DataDog/system-tests-core
utils/scripts/gen_redaction_cassettes.py                                @DataDog/system-tests-core
.github/workflows/run-end-to-end.yml                                    @DataDog/system-tests-core
.gitignore                                                              @DataDog/system-tests-core
docs/understand/scenarios/ai_guard.md                                   @DataDog/k9-ai-guard @DataDog/system-tests-core
docs/understand/weblogs/end-to-end_weblog.md                            @DataDog/system-tests-core
format.sh                                                               @DataDog/system-tests-core
manifests/cpp_httpd.yml                                                 @DataDog/dd-trace-cpp
manifests/cpp_nginx.yml                                                 @DataDog/dd-trace-cpp
manifests/dotnet.yml                                                    @DataDog/apm-dotnet @DataDog/asm-dotnet
manifests/golang.yml                                                    @DataDog/dd-trace-go-guild
manifests/java.yml                                                      @DataDog/asm-java @DataDog/apm-java
manifests/nodejs.yml                                                    @DataDog/dd-trace-js
manifests/php.yml                                                       @DataDog/apm-php @DataDog/asm-php
manifests/python.yml                                                    @DataDog/apm-python @DataDog/asm-python
manifests/ruby.yml                                                      @DataDog/ruby-guild @DataDog/asm-ruby
tests/ai_guard/test_ai_guard_sdk.py                                     @DataDog/k9-ai-guard @DataDog/system-tests-core
tests/test_the_test/test_minimal_number_of_scenarios.py                 @DataDog/system-tests-core
utils/_context/_scenarios/__init__.py                                   @DataDog/system-tests-core

@datadog-official

datadog-official Bot commented Jul 27, 2026

Copy link
Copy Markdown

Pipelines  Tests

Unblock PR with BitsAI

⚠️ Warnings

🚦 3 Pipeline jobs failed

DataDog/system-tests | php-app.arm64.DOC: [public.ecr.aws/lts/ubuntu:22.04, linux/arm64, 7.2]   View in Datadog   GitLab

DataDog/system-tests | Ubuntu_24_amd64.MUL: [test-app-java-multicontainer]   View in Datadog   GitLab

Testing the test | all-jobs-are-green   View in Datadog   GitHub Actions

See error Check run failed due to CI checks or statuses failures.

ℹ️ Info

No other issues found (see more)

🧪 All tests passed
❄️ No new flaky tests detected

Useful? React with 👍 / 👎

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 2defd78 | Docs | Datadog PR Page | Give us feedback!

avara1986 and others added 3 commits July 30, 2026 10:19
…path

Extends the SDS redaction end-to-end tests to the surfaces the redaction RFC
makes normative, aligned with the reference implementation in dd-trace-py#19360.

Metrics:
- Test_AIGuardTelemetryRedacted asserts the new `redacted` tag on the
  ai_guard.requests metric, alongside the existing action/block/error tags.
- Every redaction assertion now also checks the ai_guard.redacted span tag.
- Fix the telemetry lookup: AI Guard metrics are emitted in the appsec
  namespace as ai_guard.requests, not in an ai_guard namespace as requests.
  Both identities are accepted so the tests survive the planned namespace move.
- Set the telemetry heartbeat on the scenarios that read metrics mid-run;
  metrics are only sent at the heartbeat, 60s by default.

Kill-switch:
- New AI_GUARD_REDACTION_DISABLED scenario (DD_AI_GUARD_REDACTION_ENABLED=false)
  hosting Test_RedactionDisabled and Test_RedactionDisabledTelemetry. Neither
  the span tag nor the telemetry tag may be emitted, so an absent tag ("off")
  stays distinguishable from false ("on, nothing redacted").

Blocked path:
- Test_RedactionOnBlock sends a redacting scenario with blocking enabled: the
  span must still report the redaction and the abort error must carry the
  redacted messages. The flask weblog now exposes those messages on its 403.

Scenarios and cassettes stay generated together by gen_redaction_cassettes.py,
whose reference implementation of the RFC algorithm cross-checks every expected
outcome. All new classes are declared missing_feature in every manifest.

Also restores the scenario reference in tests/appsec/smoke_tests, which pointed
at a scenario that does not exist.

RFC: https://datadoghq.atlassian.net/browse/APPSEC-68806

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…-redaction-tests

# Conflicts:
#	manifests/cpp_httpd.yml
#	manifests/cpp_nginx.yml
#	manifests/dotnet.yml
#	manifests/golang.yml
#	manifests/java.yml
#	manifests/nodejs.yml
#	manifests/php.yml
#	manifests/python.yml
#	manifests/ruby.yml
@avara1986 avara1986 changed the title [PoC] AI Guard: end-to-end tests for SDS redaction (APPSEC-68867) AI Guard: end-to-end tests for SDS redaction Jul 31, 2026
@avara1986
avara1986 marked this pull request as ready for review August 12, 2026 13:13
@avara1986
avara1986 requested review from a team as code owners August 12, 2026 13:13
@avara1986
avara1986 requested review from christophe-papazian, claponcet and sabrenner and removed request for a team August 12, 2026 13:13
@avara1986
avara1986 requested review from PerfectSlayer, dromanol and xlamorlette-datadog and removed request for a team August 12, 2026 13:13

@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: 2d0cf6add9

ℹ️ 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 thread tests/ai_guard/test_ai_guard_sdk.py Outdated

@christophe-papazian christophe-papazian left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed the fixture machinery first: regeneration is deterministic (no drift), and I re-derived all 31 cassette filenames from their request bodies, including the 6 previously recorded ones — the hash formula is correct. mypy/ruff clean, all 9 manifests complete, CI wiring matches AI_GUARD_TELEMETRY.

The issues below are mostly about the test oracles. Blocking ones IMO: the truncation/cassette mismatch on Test_AIGuardTelemetryRedacted, and the cassette deletion in the generator.

(Reviewed with Claude Code + codex.)

Comment thread tests/ai_guard/test_ai_guard_sdk.py Outdated
Comment thread tests/ai_guard/test_ai_guard_sdk.py
Comment thread tests/ai_guard/test_ai_guard_sdk.py
Comment thread tests/ai_guard/test_ai_guard_sdk.py
Comment thread tests/ai_guard/test_ai_guard_sdk.py
Comment thread utils/scripts/gen_redaction_cassettes.py Outdated
Comment thread utils/scripts/gen_redaction_cassettes.py Outdated
Comment thread utils/scripts/gen_redaction_cassettes.py Outdated
Comment thread docs/understand/scenarios/ai_guard.md Outdated
Comment thread docs/understand/scenarios/ai_guard.md

@cbeauchesne cbeauchesne left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

From framework usage, AGTM

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.

3 participants