Skip to content

fix(dlp): prevent critical DLP bypass via HTTP compression - #152

Open
TANICE-GAWD wants to merge 1 commit into
runta-dev:mainfrom
TANICE-GAWD:fix/dlp-compression-bypass
Open

fix(dlp): prevent critical DLP bypass via HTTP compression#152
TANICE-GAWD wants to merge 1 commit into
runta-dev:mainfrom
TANICE-GAWD:fix/dlp-compression-bypass

Conversation

@TANICE-GAWD

Copy link
Copy Markdown

Title: Fix critical DLP bypass via HTTP compression

What this fixes

DLP is ClawShell's last line of defense against leaking sensitive data. It scans
request and response bodies and blocks or redacts PII. The problem: it scanned the
raw bytes and nothing decoded gzip. So an attacker could hide the payload inside a
compressed body and walk it straight past DLP, in plain sight.

What could go wrong if left unfixed

This is a full bypass of ClawShell's core security control, and it is trivial to
weaponize. One HTTP header disarms DLP completely. Worse, the attacker is the exact
threat DLP exists to stop: the agent, which can be hijacked by prompt injection,
and which already holds a valid virtual key.

  • Data exfiltration (response): the attacker sends Accept-Encoding: gzip. The
    provider replies gzipped, DLP scans the scrambled bytes, sees nothing, and hands
    it back. The attacker's client unzips it and reads every SSN, card number, and
    secret DLP was supposed to catch.
  • Data smuggling (request): the attacker sends Content-Encoding: gzip with a
    gzipped body. DLP sees noise, waves it through, and the payload leaks upstream
    past every block rule.

It failed open and silent. No error, no log, no trace in /admin/stats. A breach
here would leave nothing behind to find.

Why the old code was vulnerable

filter_hop_by_hop_headers in src/proxy.rs forwarded the attacker's
accept-encoding and content-encoding headers untouched, and reqwest does not
decode gzip. So the scanner never saw anything but ciphered bytes.

The fix

Force DLP to always see plain text. The fix deliberately does not unzip attacker
controlled data, since decompressing untrusted input on a privileged process opens
a zip bomb hole. It shuts the compression down instead.

  1. Force accept-encoding: identity on every request sent upstream, so the
    provider is not allowed to reply compressed.
  2. Reject any request carrying a non-identity Content-Encoding with 400.
  3. If response scanning is on and a reply still comes back compressed, fail closed
    with 502 instead of trusting unscanned bytes.

Testing

Added six tests, all passing. They confirm identity is forced upstream, compressed
requests are rejected with 400, and compressed responses fail closed with 502.

Per CONTRIBUTING.md: cargo test passes (356 + 16 tests). cargo fmt and
cargo clippy --all-targets are clean. No config changed, so no snapshots to
update.

Note

The URL query string is still not scanned by DLP and remains a separate hole,
out of scope for this change. This PR closes the compression bypass only.

@TANICE-GAWD

Copy link
Copy Markdown
Author

@guanlan @ADD-SP
flagging this one for a look. It's a DLP bypass: a single HTTP header disarms PII scanning in both directions. Fix plus tests are ready.
Would appreciate a review when you have a moment :)

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