Skip to content

Output redaction ignores non-secret findings (URLs survive in redacted_text) #122

Description

@chirag-gamer

I was looking at how Guard.scan_output() handles redaction and noticed that redacted_text still contains flagged URLs unchanged.

Repro on dev at 7a8be76:

fromunplug.guardimportGuardresult=Guard().scan_output('Link: http://198.51.100.7/collect')
# result.findings has: urls/ip_literal_url, replacement='[BLOCKED:url]'# result.redacted_text is: 'Link: http://198.51.100.7/collect'# URL is still there, untouched

The URL scanner flags the IP-literal URL and gives it a [BLOCKED:url] replacement, but that replacement never makes it into redacted_text.

I think the issue is in OutputPipeline._redact (output.py:101-102). When a sanitizer exists (and it always does because Guard.__init__ always passes SecretsSanitizer), it just does:

returnself._sanitizer.sanitize(text).clean_text

That sanitizer only knows about registered secrets and generic secret patterns. It never sees the scanner findings at all. So the BasePipeline._redact fallback that would use apply_span_redactions(text, findings, policy) is effectively dead code for the output path.

Registered secrets DO get redacted in the same path, which makes this a bit subtle. I registered a test secret and ran both:

redacted_text='Key: [REDACTED:k] and URL: http://198.51.100.7/x'

Secret gone, URL still there. So the problem is specifically that the sanitizer branch only handles secret detection and ignores everything else (URLs, and probably PII/leakage findings too, though I only tested URLs).

This matters for anything using redacted_text as safe-to-forward output. If a host treats that field as sanitized and sends it downstream, flagged URLs slip through.

Environment: Python 3.14.2, Windows 11, unplug-ai==0.6.0, installed via uv sync --dev from the dev branch.

Relevant code:

  • sdk/src/unplug/pipelines/output.py line 101-102 (_redact sanitizer branch)
  • sdk/src/unplug/core/privacy/secrets.py lines 179-191 (sanitize method)
  • sdk/src/unplug/pipelines/base.py lines 157-167 (_redact fallback that uses findings, currently dead code)

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions