Skip to content

fix(approval): redact non-canonically-cased home paths in scrub_paths#5042

Open
mysma-9403 wants to merge 2 commits into
tinyhumansai:mainfrom
mysma-9403:fix/approval-redact-case-insensitive-home
Open

fix(approval): redact non-canonically-cased home paths in scrub_paths#5042
mysma-9403 wants to merge 2 commits into
tinyhumansai:mainfrom
mysma-9403:fix/approval-redact-case-insensitive-home

Conversation

@mysma-9403

@mysma-9403 mysma-9403 commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Fix a PII leak: scrub_paths returned non-canonically-cased home paths (e.g. c:\users\alice\…, /HOME/alice/…) verbatim, leaking the OS username into the durable approval audit surface.
  • Make the fast-path bailout guard case-insensitive so it matches the (already case-insensitive) match_home_prefix matcher it guards.
  • Add regression tests for lowercase / uppercase / mixed-case home paths and a non-path string.

Problem

scrub_paths redacts absolute home paths to <HOME> before an approval summary is persisted into the durable pending_approvals table and broadcast on the event bus (DomainEvent::ApprovalRequested). The module docstring promises this output cannot leak the user's username on multi-tenant log shipping.

It opens with a fast-path bailout:

if !input.contains("Users") && !input.contains("home") {
    return input.to_string();
}

This guard is case-sensitive, but the matcher it guards (match_home_prefix) is deliberately case-insensitive (eq_ignore_ascii_case). So a path like c:\users\alice\work (lowercase users) or /HOME/alice/work (uppercase) contains neither "Users" nor "home" literally, hits the early return, and is emitted unredacted — leaking the username into a surface the module guarantees is username-free. Path-bearing keys (cwd/path/dir/command/file) are not in SENSITIVE_KEYS, so scrub_paths is their only redaction boundary. The existing tests only cover canonical C:\Users\ / /Users/ / /home/, so the gap was untested.

Solution

Lower-case the input once and test the guard against it, so the fast-path can only skip strings the matcher genuinely could not redact:

let lower = input.to_ascii_lowercase();
if !lower.contains("users") && !lower.contains("home") {
    return input.to_string();
}

match_home_prefix only ever matches strings containing users or home (case-insensitively), so this guard is now exactly as permissive as the matcher — no false bailout, and unrelated strings still return verbatim (one allocation on the common no-path path). Canonical casings that already redacted are unaffected.

Submission Checklist

  • Tests added or updated (happy path + at least one failure / edge case) — lowercase_windows_home_path_is_scrubbed, uppercase_linux_home_path_is_scrubbed, mixed_case_home_path_is_scrubbed (the previously-leaking cases), and non_path_string_without_home_marker_is_unchanged (guard still bails correctly).
  • Diff coverage ≥ 80% — the changed guard line is exercised by both the new leak-path tests and the unchanged-string test. cargo test -p openhuman --lib approval::redact passes.
  • Coverage matrix updated — N/A: security bug fix, no user-visible feature change.
  • All affected feature IDs listed under ## RelatedN/A.
  • No new external network dependencies introduced.
  • Manual smoke checklist updated — N/A: does not touch a release-cut surface.
  • Linked issue closed via Closes #NNN — no existing issue; found by inspection.

Impact

  • Security / privacy: stops the OS username leaking into the durable pending_approvals audit row and the ApprovalRequested event for non-canonically-cased home paths. No behaviour change for canonical paths or non-path strings; no schema/migration.

Related

  • Closes:
  • Follow-up PR(s)/TODOs:

AI Authored PR Metadata

Linear Issue

  • Key: N/A
  • URL: N/A

Commit & Branch

  • Branch: fix/approval-redact-case-insensitive-home
  • Commit SHA: a0ea9cb4c

Validation Run

  • pnpm --filter openhuman-app format:check — N/A (no frontend change)
  • pnpm typecheck — N/A (no frontend change)
  • Focused tests: cargo test -p openhuman --lib approval::redact
  • Rust fmt/check (if changed): cargo fmt
  • Tauri fmt/check (if changed): N/A (core-only change)

Behavior Changes

  • Intended behavior change: non-canonically-cased home paths are now redacted to <HOME> like their canonical forms.
  • User-visible effect: none beyond stronger redaction (no username leak).

Parity Contract

  • Legacy behavior preserved: canonical /Users/, /home/, C:\Users\ paths redact exactly as before; non-path strings still return verbatim via the (now case-insensitive) fast-path guard.
  • Guard/fallback/dispatch parity checks: guard permissiveness now matches match_home_prefix exactly.

Summary by CodeRabbit

  • Bug Fixes
    • Improved redaction of home-directory paths regardless of capitalization.
    • Fixed cases where Windows and Linux paths with mixed-case home markers could be exposed.
    • Preserved unrelated text that does not contain a home-directory path.

scrub_paths guards its redaction walk with a fast-path bailout that returned
verbatim when the input contained neither "Users" nor "home". That check was
case-sensitive while the matcher it guards (match_home_prefix) is
case-insensitive (eq_ignore_ascii_case), so paths like c:\users\alice\work or
/HOME/alice/work short-circuited unredacted, leaking the OS username into the
durable pending_approvals audit row and the ApprovalRequested event — a surface
the module docstring promises is username-free. cwd/path/dir/command/file are
not in SENSITIVE_KEYS, so scrub_paths is their only redaction boundary, and the
existing tests only covered canonical C:\Users\ / /Users/ / /home/ casings.

Lower-case the input once and test the guard against it, so the fast-path is
exactly as permissive as match_home_prefix (which only matches strings holding
'users'/'home' case-insensitively). Canonical casings redact as before; unrelated
strings still return verbatim.

Tests: lowercase_windows / uppercase_linux / mixed_case home paths now redact to
<HOME>; a non-path string still returns unchanged through the guard.
@mysma-9403
mysma-9403 requested a review from a team July 18, 2026 13:00
@coderabbitai

coderabbitai Bot commented Jul 18, 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

Run ID: 4b8de218-c01a-4b72-b592-2760022b3a3d

📥 Commits

Reviewing files that changed from the base of the PR and between a0ea9cb and 837c1a8.

📒 Files selected for processing (1)
  • src/openhuman/approval/redact.rs

📝 Walkthrough

Walkthrough

scrub_paths now detects users and home case-insensitively before scanning paths. Tests cover lowercase, uppercase, and mixed-case home paths, plus unrelated strings that remain unchanged.

Changes

Redaction casing coverage

Layer / File(s) Summary
Case-insensitive path guard and tests
src/openhuman/approval/redact.rs
The early path-scrubbing guard recognizes home markers regardless of casing, with regression tests for Windows, Linux, mixed-case, and non-home strings.

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested reviewers: senamakel

Poem

I’m a rabbit guarding paths in the night,
Catching /HOME and c:\users just right.
Mixed-case trails no longer flee,
While plain commands stay wild and free.
Hop, hop—redaction works happily!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: case-insensitive home-path redaction in scrub_paths.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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.

@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: 1

🤖 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 `@src/openhuman/approval/redact.rs`:
- Around line 134-135: Replace the unconditional to_ascii_lowercase allocation
in the approval redaction check with a zero-allocation, case-insensitive
sliding-window scan over input’s bytes, exiting as soon as “users” or “home” is
found. Preserve the existing bailout condition while avoiding a full lowercase
copy for large strings.
🪄 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

Run ID: 6fd49f3e-42e0-4e01-acef-14ec34bbc0fd

📥 Commits

Reviewing files that changed from the base of the PR and between d13a559 and a0ea9cb.

📒 Files selected for processing (1)
  • src/openhuman/approval/redact.rs

Comment thread src/openhuman/approval/redact.rs Outdated
… a lowercase copy

The case-insensitive fast-path guard allocated a full lowercase copy of the
input via to_ascii_lowercase() on every call. Tool arguments can be large
(source or file contents), so that is an O(N) allocation on every string field
processed, even for the common no-path case that bails immediately.

Replace it with a zero-allocation sliding-window scan over the bytes
(windows(n).any(|w| w.eq_ignore_ascii_case(..))). windows(n) yields nothing
when the input is shorter than the needle, so short strings bail without a
panic. Case-insensitivity — and the redaction behaviour the tests lock in — is
unchanged. Per CodeRabbit review on tinyhumansai#5042.
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