Skip to content

feat(security): PII/PHI redaction for telemetry + EU AI Act Article 15 export (#398)#442

Merged
sreerevanth merged 4 commits into
sreerevanth:mainfrom
Prateeks16:feat/pii-phi-redaction-398
Jun 22, 2026
Merged

feat(security): PII/PHI redaction for telemetry + EU AI Act Article 15 export (#398)#442
sreerevanth merged 4 commits into
sreerevanth:mainfrom
Prateeks16:feat/pii-phi-redaction-398

Conversation

@Prateeks16

@Prateeks16 Prateeks16 commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Closes #398

Summary

Implements the two CMP-003/CMP-004 deliverables from the issue: auto-redaction of PII/PHI in telemetry before it is persisted, and an EU AI Act Article 15 conformity export endpoint.

The compliance/governance layer already exists under agentwatch/governance/ (gdpr.py, hipaa.py, eu_ai_act.py, compliance_reporter.py + the /governance/compliance-report endpoint). The missing piece — and the focus of this PR — is the redaction layer and wiring it into the telemetry path.

What's added

  • agentwatch/security/redaction.py — a Redactor that masks PII and PHI as [REDACTED]:
    • Uses Microsoft Presidio (presidio-analyzer/presidio-anonymizer) for NER-based detection when installed (new optional redaction extra), and otherwise falls back to the regex detectors already maintained by the GDPR (CMP-001) and HIPAA (CMP-003) engines — so there's a single source of truth for the patterns and no hard dependency on the heavyweight Presidio stack.
    • Helpers: redact(), redact_payload() (recursive), redact_tool_call().
  • core/watcher.py — opt-in GenericAdapter(redact=True) scrubs each tool call's raw_command + arguments before the event is published and persisted.
  • api/server.pyGET /api/v1/governance/eu-ai-act-report returns the Article 15 technical documentation + conformity assessment as JSON, mapping AgentWatch's safety telemetry (risk scoring, red-team harness, blast-radius) to the Article 15 requirements.

Acceptance criteria

  • PII and PHI (SSNs, medical diagnoses, …) are masked as [REDACTED] before persistence — covered by redaction.py + the watcher opt-in.
  • Compliance report endpoint generates required evidence — the existing /governance/compliance-report plus the new /governance/eu-ai-act-report Article 15 export.

Design notes

  • Redaction is opt-in on the watcher to avoid imposing the cost (and Presidio model load) on every run; enable per-adapter with redact=True.
  • Reuses the governance regex patterns instead of duplicating them; Presidio is strictly optional (pip install agentwatch[redaction]).

Testing

tests/test_redaction.py: PII masking (SSN/email/phone), PHI masking (MRN/diagnosis/ICD), recursive payload redaction, redact_tool_call, the watcher opt-in (on/off), and the EU AI Act endpoint via TestClient.

pytest tests/test_redaction.py tests/test_watcher.py tests/test_compliance.py  -> all pass
ruff check / format (changed files)                                            -> clean

Note on scope / process

CONTRIBUTING flags security/compliance features for Discord discussion first — flagging here; happy to adjust. The issue's suggested paths (agentwatch/compliance/eu_ai_act.py) map to the existing agentwatch/governance/ module, which this PR builds on rather than duplicating.

The repo-wide ruff check . gate has pre-existing failures in unrelated test files on main; this PR's changed files are lint-clean.

Summary by CodeRabbit

  • New Features
    • Added opt-in PII/PHI redaction for telemetry, masking sensitive tool-call content before it’s published or persisted.
    • Added an authenticated EU AI Act Article 15 conformity export endpoint for governance reporting.
  • Documentation
    • Updated the changelog and compliance status documentation to reflect the new redaction and governance reporting capabilities.

Copilot AI review requested due to automatic review settings June 19, 2026 08:47
@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1149f436-6527-4d6d-a8db-e0c9ff833159

📥 Commits

Reviewing files that changed from the base of the PR and between 4229482 and dfc6d64.

📒 Files selected for processing (7)
  • CHANGELOG.md
  • MASTERLIST_STATUS.md
  • agentwatch/api/server.py
  • agentwatch/core/watcher.py
  • agentwatch/security/redaction.py
  • pyproject.toml
  • tests/test_redaction.py
✅ Files skipped from review due to trivial changes (2)
  • MASTERLIST_STATUS.md
  • CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (5)
  • agentwatch/api/server.py
  • pyproject.toml
  • agentwatch/core/watcher.py
  • tests/test_redaction.py
  • agentwatch/security/redaction.py

📝 Walkthrough

Walkthrough

Adds agentwatch/security/redaction.py with a Redactor class that masks PII/PHI using Presidio (optional redaction extra) or a regex fallback sourced from the existing GDPR/HIPAA governance modules. Exposes pattern accessors from governance modules, wires opt-in redaction into GenericAdapter and the watch() API so safety checks run on raw payloads while published events carry scrubbed data. Introduces a GET /api/v1/governance/eu-ai-act-report Article 15 conformity export endpoint.

Changes

PII/PHI Redaction and EU AI Act Compliance Export

Layer / File(s) Summary
GDPR/HIPAA pattern accessors
agentwatch/governance/gdpr.py, agentwatch/governance/hipaa.py
Adds pii_patterns() and phi_patterns() public accessors returning copies of the internal compiled regex+label tuples, exported via each module's __all__.
Redaction engine and optional dependencies
agentwatch/security/redaction.py, pyproject.toml
Introduces the Redactor class with Presidio and regex fallback backends, MASK constant, combined _FALLBACK_PATTERNS from GDPR/HIPAA providers, recursive redact_payload, singleton default_redactor, and redact/redact_payload/redact_tool_call wrappers. Adds presidio-analyzer/presidio-anonymizer as redaction optional dependencies.
GenericAdapter opt-in redaction integration
agentwatch/core/watcher.py
Adds redact: bool = False to GenericAdapter.__init__, introduces _maybe_redact and _redact_event helpers, and updates both async and sync tool-call paths to evaluate safety on raw payloads while publishing redacted TOOL_CALL events when enabled.
Top-level watch() API redact parameter
agentwatch/core/watcher.py
Adds redact: bool = False parameter to public watch() function and forwards it to GenericAdapter for the generic-adapter fallback path.
EU AI Act Article 15 endpoint
agentwatch/api/server.py
Adds authenticated GET /api/v1/governance/eu-ai-act-report handler that derives robustness/oversight evidence from live safety engine state, builds TechnicalDocumentation, generates decision log entries from recent sessions, and returns JSON conformity report.
Tests, changelog, and compliance status
tests/test_redaction.py, CHANGELOG.md, MASTERLIST_STATUS.md
Adds 163-line test suite covering Redactor unit tests, GenericAdapter redaction integration, safety-before-redaction ordering, and the Article 15 endpoint HTTP validation. Updates changelog and compliance status table for CMP-003/CMP-004.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant GenericAdapter
    participant SafetyEngine
    participant EventBus
    participant Redactor

    rect rgba(135, 206, 235, 0.5)
        Note over Client,Redactor: Tool-call redaction flow (when redact=True)
        Client->>GenericAdapter: invoke tool (raw ToolCallData)
        GenericAdapter->>SafetyEngine: evaluate raw ToolCallData
        SafetyEngine-->>GenericAdapter: safety decision
        GenericAdapter->>Redactor: redact_event (if redact=True)
        Redactor-->>GenericAdapter: AgentEvent with scrubbed payload
        GenericAdapter->>EventBus: publish TOOL_CALL event (redacted)
    end

    rect rgba(144, 238, 144, 0.5)
        Note over Client,EventBus: Article 15 conformity export
        Client->>GenericAdapter: GET /api/v1/governance/eu-ai-act-report
        GenericAdapter->>SafetyEngine: stats() + policy
        SafetyEngine-->>GenericAdapter: robustness evidence, sessions
        GenericAdapter-->>Client: JSON (TechnicalDocumentation, telemetry)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

  • sreerevanth/AgentWatch#70: Modified agentwatch/core/watcher.py tool-call event publishing flow; this PR further modifies the same code paths to inject the optional redaction step before event publication.

Suggested labels

Hard, SSoC26

Suggested reviewers

  • sreerevanth

Poem

🐇 Hop hop, the secrets are masked today,
[REDACTED] where the PII lay,
Presidio checks, or regex sweeps the floor,
Article 15 reports knock on the law's door.
Safety checks first, then the scrubber's call —
A careful rabbit keeps the data small! 🛡️

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.84% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: PII/PHI redaction for telemetry and EU AI Act Article 15 export, matching the changeset scope and primary deliverables.
Linked Issues check ✅ Passed The PR fully implements both coding requirements from issue #398: PII/PHI redaction is implemented with Presidio/regex fallback, and EU AI Act Article 15 endpoint generates runtime-derived conformity reports.
Out of Scope Changes check ✅ Passed All changes are directly aligned with issue #398 requirements. Documentation updates (CHANGELOG.md, MASTERLIST_STATUS.md) and dependency additions (pyproject.toml) are appropriate supporting changes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

github-actions Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

🧪 PR Test Results

Check Result
Tests (pytest tests/) ✅ success
Lint (ruff check .) ❌ failure
Coverage (agentwatch) 73.49%

Python 3.12 · commit dfc6d64

Copilot AI 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.

Pull request overview

Adds an opt-in telemetry redaction layer to scrub PII/PHI before persistence and introduces an EU AI Act Article 15 JSON export endpoint under the existing governance module.

Changes:

  • Introduce agentwatch.security.redaction with Presidio (optional) + regex fallback redaction helpers for text, nested payloads, and tool calls.
  • Wire redaction into GenericAdapter via redact=True, and add GET /api/v1/governance/eu-ai-act-report.
  • Add a redaction optional dependency extra plus tests and status/changelog updates.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
agentwatch/security/redaction.py New redaction module implementing Presidio/regex PII/PHI masking utilities.
agentwatch/core/watcher.py Adds opt-in redaction hook for tool-call telemetry in the watcher adapter.
agentwatch/api/server.py Adds EU AI Act Article 15 report export endpoint.
pyproject.toml Adds optional redaction extra for Presidio dependencies.
tests/test_redaction.py Adds unit/integration tests for redaction behavior, watcher integration, and the new endpoint.
MASTERLIST_STATUS.md Updates compliance phase entries to reflect added deliverables/tests.
CHANGELOG.md Documents the new redaction feature and report endpoint under Unreleased.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 309 to 312
# ── Safety gate (async path — full check_event with approval) ──
if is_tool_like:
tool_call = _build_tool_call_data(method_name, args, kwargs)
tool_call = self._maybe_redact(_build_tool_call_data(method_name, args, kwargs))
safety_event = AgentEvent(
Comment on lines 373 to 377
# ── Safety gate (sync path — pattern match only, no approval) ──
if is_tool_like:
tool_call = _build_tool_call_data(method_name, args, kwargs)
tool_call = self._maybe_redact(_build_tool_call_data(method_name, args, kwargs))
try:
blocked, reasons = self._safety_engine.check_tool_call_sync(tool_call)
Comment thread agentwatch/api/server.py Outdated
Comment on lines +1080 to +1092
"""EU AI Act Article 15 conformity export (CMP-004).

Maps AgentWatch's safety telemetry to the Article 15 requirements and
returns the technical documentation plus a conformity assessment as JSON.
"""
from agentwatch.governance.eu_ai_act import EUAIActPackage, TechnicalDocumentation

doc = TechnicalDocumentation(
system_name="AgentWatch-monitored AI system",
intended_purpose="Observability, safety, and reliability layer for AI agents",
risk_category="high",
data_governance={"pii_phi_redaction": "enabled", "retention": "policy-driven"},
robustness_evidence=[
Comment thread agentwatch/security/redaction.py Outdated
Comment on lines +19 to +21
from agentwatch.governance.gdpr import _PII_PATTERNS
from agentwatch.governance.hipaa import _PHI_PATTERNS

@sreerevanth

Copy link
Copy Markdown
Owner

@Prateeks16
Thanks for the contribution.

The redaction functionality is useful and the overall direction makes sense. However, there are a few issues that need to be addressed before merge:

  • Safety checks should run against the raw tool call data. Redaction currently occurs before safety evaluation, which may hide signals needed by the SafetyEngine and alter block/allow decisions.
  • The EU AI Act export currently reports compliance-related fields that appear to be static rather than derived from runtime configuration or telemetry.
  • Please avoid relying on private governance module internals where possible and expose public accessors/constants instead.

Once these items are addressed, I'll take another look.

@Prateeks16

Copy link
Copy Markdown
Contributor Author

Thanks for the review — all three addressed in the latest push:

  1. Safety now runs on raw data. Redaction no longer happens before the safety check. The watcher evaluates the raw tool call, then scrubs PII/PHI only when building the event that's published/persisted (new _redact_event helper, both sync and async paths). Added a regression test asserting the SafetyEngine sees the unredacted command while the published TOOL_CALL event is redacted.

  2. EU AI Act export is now runtime-derived. The Article 15 endpoint pulls data governance, accuracy metrics, robustness evidence, human-oversight description, and the decision log (record-keeping) from live safety telemetry (safety_engine.stats()), the active policy, and recent sessions — no more static literals. Response now includes a telemetry block.

  3. No more private internals. gdpr and hipaa expose public pii_patterns() / phi_patterns() accessors; the redactor consumes those instead of _PII_PATTERNS / _PHI_PATTERNS.

Full suite green except one pre-existing CLI test (test_export_invalid_format) unrelated to this PR — it reads result.stderr, fixed separately in #449.

@sreerevanth

Copy link
Copy Markdown
Owner

Thanks for addressing the review feedback. The remaining blocker appears to be the merge conflict in CHANGELOG.md. Please resolve the conflict and rebase onto the latest main branch. Once that's done I'll do a final pass.

…reerevanth#398)

CMP-003/004 — auto-redact PII/PHI before telemetry is persisted, and surface an
EU AI Act Article 15 conformity export.

- agentwatch/security/redaction.py: a Redactor that masks PII/PHI as [REDACTED].
  Uses Microsoft Presidio when installed (new optional `redaction` extra),
  otherwise falls back to the GDPR (CMP-001) and HIPAA (CMP-003) regex
  detectors — reusing their patterns, no hard dependency. Provides
  redact()/redact_payload()/redact_tool_call() helpers.
- core/watcher.py: opt-in `GenericAdapter(redact=True)` scrubs tool-call
  payloads (raw_command + arguments) before events are published/persisted.
- api/server.py: GET /api/v1/governance/eu-ai-act-report returns the Article 15
  technical documentation + conformity assessment as JSON.
- Tests: PII/PHI masking, payload recursion, tool-call redaction, the watcher
  opt-in, and the EU AI Act endpoint.
- Docs: CHANGELOG, MASTERLIST (CMP-003/004), and the `redaction` extra.
…export, public patterns

Maintainer feedback on sreerevanth#398:

1. Safety must evaluate the raw tool call, not a redacted copy. Redaction ran
   before the safety check, masking signals (paths/secrets/identifiers) the
   SafetyEngine relies on and potentially altering block/allow decisions. The
   watcher now checks the raw payload and scrubs PII/PHI only when building the
   event that is published/persisted (new _redact_event helper). Added a
   regression test asserting safety sees the raw command while the published
   event is redacted.

2. The EU AI Act Article 15 export reported static literals. It now derives
   data governance, accuracy metrics, robustness evidence, human-oversight
   description, and record-keeping (decision log) from live safety telemetry
   and the active policy.

3. Stop importing private governance internals. gdpr/hipaa now expose public
   pii_patterns() / phi_patterns() accessors; the redactor uses those.
@Prateeks16 Prateeks16 force-pushed the feat/pii-phi-redaction-398 branch from d2b2452 to 4229482 Compare June 21, 2026 16:20
@Prateeks16

Copy link
Copy Markdown
Contributor Author

Rebased onto the latest main and resolved the CHANGELOG.md conflict (kept both the #399 and #398 entries). The review fixes are intact and tests pass — ready for your final pass.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 3

🧹 Nitpick comments (1)
agentwatch/core/watcher.py (1)

620-627: 💤 Low value

Consider exposing redact parameter in watch() for API completeness.

The GenericAdapter now accepts redact=True, but the watch() convenience function doesn't expose this option, requiring users to drop down to GenericAdapter directly for redaction. For discoverability, consider adding the parameter here:

 def watch(
     agent: Any,
     *,
     session_id: str | None = None,
     agent_id: str | None = None,
     event_bus: EventBus | None = None,
+    redact: bool = False,
 ) -> Any:

And passing it through:

         adapter = GenericAdapter(
             agent,
             framework=framework,
             framework_label=label,
             event_bus=bus,
             session_id=session_id,
             agent_id=agent_id,
+            redact=redact,
         )

Not blocking since the PR scope explicitly targets GenericAdapter(redact=True).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@agentwatch/core/watcher.py` around lines 620 - 627, The watch() function does
not expose the redact parameter that GenericAdapter now supports, limiting API
discoverability. Add a redact parameter to the watch() function signature with a
sensible default value, then pass this redact parameter through to the
GenericAdapter instantiation where other parameters like agent, framework,
event_bus, session_id, and agent_id are being passed.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@agentwatch/api/server.py`:
- Around line 1137-1156: The code caps decision-log ingestion to the first 50
sessions with the slice `sessions[:50]` in the for loop, but the telemetry
dictionary reports `len(sessions)` for the sessions_considered field, which
represents the full session count rather than the count actually processed. To
fix this inconsistency, either store the sliced sessions in a variable like
`processed_sessions = sessions[:50]` and use that for both the loop and the
sessions_considered count, or directly change the sessions_considered value to
use `len(sessions[:50])` instead of `len(sessions)` to match what the conformity
assessment actually consumed.

In `@agentwatch/security/redaction.py`:
- Around line 87-88: In the redact_payload method, the dictionary redaction
logic on lines 87-88 only redacts the values but leaves keys unchanged, which
can leak sensitive information. Modify the dictionary comprehension to apply the
redact_payload method to both keys and values so that sensitive information in
dictionary keys is also properly redacted and does not appear in the telemetry
output.

In `@tests/test_redaction.py`:
- Around line 133-136: The test function test_eu_ai_act_report_endpoint is
calling an authenticated route at "/api/v1/governance/eu-ai-act-report" without
properly handling API key authentication. This makes the test
environment-sensitive and prone to failure based on AGENTWATCH_API_KEY
configuration. Fix this by either mocking the AGENTWATCH_API_KEY environment
variable to a known test value before making the request, or by providing the
required authentication headers in the client.get() call to ensure the test
passes consistently regardless of the environment configuration.

---

Nitpick comments:
In `@agentwatch/core/watcher.py`:
- Around line 620-627: The watch() function does not expose the redact parameter
that GenericAdapter now supports, limiting API discoverability. Add a redact
parameter to the watch() function signature with a sensible default value, then
pass this redact parameter through to the GenericAdapter instantiation where
other parameters like agent, framework, event_bus, session_id, and agent_id are
being passed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 51e5beea-f930-45b4-b2b3-01dc453de1db

📥 Commits

Reviewing files that changed from the base of the PR and between c1080e9 and 4229482.

📒 Files selected for processing (9)
  • CHANGELOG.md
  • MASTERLIST_STATUS.md
  • agentwatch/api/server.py
  • agentwatch/core/watcher.py
  • agentwatch/governance/gdpr.py
  • agentwatch/governance/hipaa.py
  • agentwatch/security/redaction.py
  • pyproject.toml
  • tests/test_redaction.py

Comment thread agentwatch/api/server.py Outdated
Comment thread agentwatch/security/redaction.py Outdated
Comment thread tests/test_redaction.py Outdated
Prateeks16 and others added 2 commits June 21, 2026 22:06
… count, watch(redact), deterministic test

- redact_payload now scrubs dict keys as well as values (a key can itself be
  sensitive, e.g. an email used as a map key).
- EU AI Act export: report sessions_considered as the count actually fed to the
  decision log (sessions_used = sessions[:50]) instead of the full list length.
- Expose redact on the watch() convenience function, forwarded to the generic
  adapter, so the feature is reachable without dropping to GenericAdapter.
- Pin _API_KEY off in the endpoint test so it doesn't depend on a stray
  AGENTWATCH_API_KEY in the environment.
@sreerevanth sreerevanth merged commit 4850148 into sreerevanth:main Jun 22, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Premium] CMP-003 & CMP-004: HIPAA Compliance & EU AI Act Package

3 participants