Skip to content

fix(lab): reject file URI privacy bypasses - #3432

Merged
lidge-jun merged 4 commits into
lidge-jun:devfrom
luvs01:agent/fix-lab-file-uri-admission-20260904
Sep 4, 2026
Merged

fix(lab): reject file URI privacy bypasses#3432
lidge-jun merged 4 commits into
lidge-jun:devfrom
luvs01:agent/fix-lab-file-uri-admission-20260904

Conversation

@luvs01

@luvs01 luvs01 commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Summary

  • reject any standalone case-insensitive file: scheme, including separator-less, forward-slash, backslash, and mixed-separator forms, before Compatibility Lab event data is admitted
  • normalize ASCII URL whitespace (tab, CR, LF) before the scheme test, so fi\nle:///etc/passwd and fil\te:///etc/passwd can no longer slide past admission and become valid file:/// URLs downstream
  • cover empty, relative, localhost, remote, UNC-style, and drive-qualified forms while preserving non-file schemes such as profile://
  • keep the change limited to the existing privacy admission helper and its focused regression test

Motivation

The existing raw path detectors did not recognize every URI-encoded or Windows-style file: path. Values such as file:///etc/passwd, file://server/share/secret, and file:\\server\share\secret could therefore pass event privacy admission and enter retained lab evidence.

Per maintainer review, the same bypass class was still reachable through whitespace inside the scheme: WHATWG URL parsing strips tabs, CRs, and LFs, so "fi\nle:///etc/passwd" normalized into a valid file:/// URL while the raw string missed FILE_URI_RE. The admission test now runs against both the original value and its whitespace-stripped form, which keeps delimiter-prefixed detection intact while closing the split-scheme forms.

Validation

All commands were run in an isolated worktree on the rebased head 2b0653f4a3297353ef45ebdb3404259fda838230, whose base is current dev 5364ce01d2156e8784bf2be42944ef983aa27b33.

  • bun test --isolate ./tests/lab-post-merge-hardening.test.ts — 8 pass, 0 fail, 48 expect() calls (includes the 7 new tab/CR/LF regressions)
  • bun run typecheck — passed
  • git diff --check 5364ce01d2..HEAD — passed
  • git diff --stat 5364ce01d2..HEAD — 2 files changed, 25 insertions(+), 1 deletion(-), limited to src/lab/events/limits.ts and tests/lab-post-merge-hardening.test.ts
  • the pre-rebase and post-rebase diffs hash identically (SHA-256 20C045BF054DACA4DD9E7931A7D590E76DE6EA90F1FFB8E4280D7586E0FE9312), so the rebase carried no content change
  • two independent reviews — no remaining actionable findings

bun run privacy:scan currently fails on this branch, but the failure is not from this PR. A clean checkout of dev at 5364ce01d2 reproduces the identical failure:

Privacy scan failed:
tests/oauth-manual-code.test.ts:63 meta-api-key: <redacted>

That file and line come from #3437, not from either file changed here. On the previous base 20011a1c48 the same scan passed against this change set.

Review readiness checklist

This PR stays in draft until every box below is ticked. Tick all four boxes once the requirements are met:

  • All CI tests are green on my local testing.

  • I pushed my PR to the latest dev commit.

  • I resolved all correct Codex and CodeRabbit findings.

  • My PR is ready for review.

Summary by CodeRabbit

  • Bug Fixes
    • Improved privacy validation to reject file URL variants and filesystem path bypasses, including mixed-case, control-character, Windows, and network-share forms.
    • Continued allowing non-file URI schemes, such as HTTPS and profile URLs.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 4, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-04T05:42:44.568367Z 77025dd Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Deterministic PR hygiene checks passed.

@github-actions github-actions Bot added the bug Something isn't working label Sep 4, 2026
@github-actions

github-actions Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

✅ READY

  • all PR quality gates passed; the review readiness checklist is complete.

Review readiness checklist

  • ✅ All CI tests are green on my local testing.
  • ✅ I pushed my PR to the latest dev commit.
  • ✅ I resolved all correct Codex and CodeRabbit findings.
  • ✅ My PR is ready for review.

4/4 boxes ticked.

This pull request is already Ready for Review.
The review-ready label marks this PR as ready; review automation runs independently.
Maintainers: @lidge-jun @Ingwannu

@github-actions
github-actions Bot marked this pull request as draft September 4, 2026 05:04
@coderabbitai

coderabbitai Bot commented Sep 4, 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Team

Run ID: 52acb7ac-22b8-4ce7-9a87-d29c79eefae5

📥 Commits

Reviewing files that changed from the base of the PR and between 77025dd and 2b0653f.

📒 Files selected for processing (2)
  • src/lab/events/limits.ts
  • tests/lab-post-merge-hardening.test.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.


📝 Walkthrough

Walkthrough

The event structure validator now rejects case-insensitive file: URI forms as raw filesystem paths, including forms with ASCII URL whitespace. Tests cover local, host-qualified, embedded, relative, and Windows-style forms. Non-file URI schemes remain accepted.

Changes

Filesystem path validation

Layer / File(s) Summary
File URI detection and rejection
src/lab/events/limits.ts
Adds ASCII_URL_WHITESPACE_RE and FILE_URI_RE at lines 36–37. String validation at lines 85–86 rejects matching values with the existing raw_path error.
Filesystem path regression coverage
tests/lab-post-merge-hardening.test.ts
Renames the test at line 183. Lines 195–210 add file URI, Windows-style, and control-character rejection cases. Lines 219–222 confirm that https:// and profile:/// values remain accepted.

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

Merge Risk: ⚪ Minimal · up to 2b065

Lab event validation now blocks file URI path variants before event retention while continuing to allow tested non-file URI schemes. No actionable current-head merge risk remains.

Suggested reviewers: wibias

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 2 functions across 2 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: rejecting file URI privacy bypasses in the Lab validation logic and regression tests.
  • Fix all pre-merge checks with AI
✨ 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.

@lidge-jun

Copy link
Copy Markdown
Owner

리뷰 · 우선순위 64 / 80

설명

이 PR은 Compatibility Lab이 이벤트를 JSONL로 남기기 전에 돌리는 개인정보 검사에서, 지금까지 빠지던 file:// 형태의 로컬 경로를 막습니다. 현재 dev HEAD 072df52ebsrc/lab/events/limits.ts를 보면 문자열 값에 대해 Windows 드라이브 경로(C:\...), 날것 POSIX 경로(RAW_POSIX_PATH_RE), \Users\만 거부합니다. 그래서 file:///etc/passwd처럼 URI로 감싼 로컬 경로는 정규식에 안 걸리고 그대로 통과합니다. Lab 이벤트는 src/lab/events/validate.ts에서 enforceEventStructureLimits를 거친 뒤 증거로 남기므로, 이 구멍은 사용자 집 경로·공유 경로가 실험 로그에 남을 수 있다는 뜻입니다. 최근 dev는 계정 카드 뱃지·통합 UI 쪽을 고치고 있지만, Lab CL-00 개인정보 천장도 그대로 지켜야 하는 불변 조건입니다. 이 PR은 그 천장만 한 줄 보강하고, 같은 파일의 회귀 테스트만 넓힙니다. types.ts/config.ts 분할과 겹치지 않습니다.

변경은 두 곳뿐입니다. limits.ts에 대소문자 무시 FILE_URI_RE를 추가하고, 기존 raw_path 판정에 FILE_URI_RE.test(value)를 한 줄 넣습니다. 정규식은 (?:^|[^A-Za-z0-9+.-])file://라서 문자열 맨 앞이거나 URI 스킴에 쓰이는 문자 뒤에만 file://를 잡습니다. 그래서 xfile:// 같은 가짜는 안 잡고, detail_file:///...처럼 앞에 붙은 값은 잡습니다. 테스트 쪽은 이름을 “filesystem path bypass”로 바꾸고, file:///etc/passwd, FiLe://..., file://localhost/..., file://server/share/..., detail_file://...을 거부 목록에 넣습니다. 반대로 https://example.com/pathprofile:///etc/passwd는 그대로 통과시키는지도 확인합니다. 본문 검증(집중 테스트 8개, typecheck, privacy:scan)도 이 범위와 맞습니다.

라인 단위로 보면 방향은 맞습니다. 다만 file:// 두 슬래시만 보므로 RFC에서 허용하는 file:/etc/passwd 한 슬래시 형태는 여전히 통과할 수 있습니다. 퍼센트 인코딩된 file%3A%2F%2F...도 디코딩 전에 검사하면 놓칩니다. 지금 Lab 입력이 보통 디코딩된 문자열이라는 전제면 실사용 위험은 작지만, 우회를 완전히 닫는 수준은 아닙니다. 에러 코드는 기존과 같이 raw_path를 쓰므로 호출 쪽 분류는 깨지지 않습니다. CI 중 enforce-target·hygiene·label은 이미 초록이고 CodeRabbit만 아직 진행 중입니다.

라인 src/lab/events/limits.ts FILE_URI_RE - file://만 잡아서 file:/etc/passwd 한 슬래시 형태는 여전히 통과할 수 있다
라인 tests/lab-post-merge-hardening.test.ts 허용 목록 - profile://·https:// 통과 확인은 좋지만 file:/ 한 슬래시·퍼센트 인코딩 우회 케이스는 없다
경로 enforceEventStructureLimits - 거부 코드가 계속 raw_path라서 URI와 날경로를 구분하지 않는다(지금은 괜찮고, 나중에 로그를 나눌 때만 신경 쓰면 된다)

메인테이너의 판단이 필요한 지점

  • 이번 PR에서 file:/ 한 슬래시와 퍼센트 인코딩까지 같이 막을지, 아니면 file://만 막고 후속으로 둘지
  • Lab 입력 경로에 URL 디코드가 있는지(없다면 인코딩 우회 우선순위는 낮음)
  • 이슈 번호가 본문에 없으면 머지 후 관련 Lab privacy 이슈를 손으로 닫을지

너의 추천
CI(특히 제품 테스트·gates)가 초록이면 dev에 머지하세요. 범위가 좁고 실제 구멍이 있습니다. 한 슬래시·퍼센트 인코딩은 막지 말라고 하면 후속 이슈 한 줄만 남기면 됩니다. types/config 분할 때문에 닫을 PR이 아닙니다.

이 댓글은 grok-bot이 작성했습니다

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

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/lab/events/limits.ts Outdated

const RAW_POSIX_PATH_RE =
/(?:^|[^A-Za-z0-9._~/])\/(?:(?=$|[^A-Za-z0-9._~/])|(?!\/)(?![ \t\r\n])(?:\/|[^/\0\r\n]+)+\/?(?=$|[^A-Za-z0-9._~/]))/u;
const FILE_URI_RE = /(?:^|[^A-Za-z0-9+.-])file:\/\//i;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Reject backslash-form file URLs

When event text contains a Windows-style value such as file:\\server\share\secret or file:\C:\secret\data, Bun's WHATWG URL parser normalizes it to file://server/share/secret or file:///C:/secret/data, but this regex matches only forward slashes. In enforceEventStructureLimits, these forms also evade the existing Windows checks because the drive prefix is not at the start and the path need not contain \Users\, so a raw local or UNC path can still enter retained Lab evidence. Recognize backslashes as file-URL separators as well and add both local-drive and remote-share regression cases.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in 320f4e6. FILE_URI_RE now accepts one or more forward or backslash separators after file:, covering both UNC-style and drive-qualified forms. Added String.raw regressions for both cases; the focused 8-test file, typecheck, privacy scan, and diff check all pass.

@github-actions
github-actions Bot marked this pull request as ready for review September 4, 2026 05:18
@luvs01

luvs01 commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@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: 320f4e6545

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/lab/events/limits.ts Outdated

const RAW_POSIX_PATH_RE =
/(?:^|[^A-Za-z0-9._~/])\/(?:(?=$|[^A-Za-z0-9._~/])|(?!\/)(?![ \t\r\n])(?:\/|[^/\0\r\n]+)+\/?(?=$|[^A-Za-z0-9._~/]))/u;
const FILE_URI_RE = /(?:^|[^A-Za-z0-9+.-])file:[\\/]+/i;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Reject separator-less file URLs

When an event contains a WHATWG-valid value such as file:C:\private\secret or file:etc/passwd, FILE_URI_RE does not match because it requires a slash or backslash immediately after the colon; the other path checks also allow these forms, while Bun normalizes them to file:///C:/private/secret and file:///etc/passwd. The fresh gap left after the prior backslash fix is specifically this separator-less file: syntax, so it can still put a local filesystem path into retained Lab evidence. Match the file: scheme without requiring a separator and add separator-less Windows and POSIX regression cases.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Addressed in 77025dd. FILE_URI_RE now matches the standalone file: scheme without requiring a separator. Added separator-less Windows and POSIX regressions while retaining the scheme-boundary negatives; the focused 8-test file (41 expectations), typecheck, privacy scan, and diff check all pass.

@github-actions
github-actions Bot marked this pull request as draft September 4, 2026 05:32
@luvs01

luvs01 commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@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: 77025ddfe1

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/lab/events/limits.ts

const RAW_POSIX_PATH_RE =
/(?:^|[^A-Za-z0-9._~/])\/(?:(?=$|[^A-Za-z0-9._~/])|(?!\/)(?![ \t\r\n])(?:\/|[^/\0\r\n]+)+\/?(?=$|[^A-Za-z0-9._~/]))/u;
const FILE_URI_RE = /(?:^|[^A-Za-z0-9+.-])file:/i;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Normalize URL whitespace before matching file schemes

When an event string contains embedded ASCII URL whitespace, such as fi\nle:///etc/passwd, fil\te:///etc/passwd, or file\n:///etc/passwd, this regex does not match and the existing POSIX-path check also misses the repeated slash form. Bun's WHATWG URL parser normalizes each of these to file:///etc/passwd, so the follow-up slash and separator-less fixes still allow a file-URL-encoded local path into retained Lab evidence. Strip tabs, CR, and LF according to URL preprocessing before applying FILE_URI_RE, and add focused regression cases for these forms.

Useful? React with 👍 / 👎.

@lidge-jun

Copy link
Copy Markdown
Owner

Triaged in the 260904 bug-backlog closeout.

The direction is right and the scope is appropriately narrow, but the privacy boundary is still passable. Whitespace inside the scheme normalizes away before the URL is parsed, so "fi\nle:///etc/passwd" becomes a valid file:/// URL while sliding past FILE_URI_RE in src/lab/events/limits.ts. Tabs behave the same way. That is the same class of bypass this change exists to close, just spelled differently.

To land: strip or normalize URL whitespace before testing the scheme, and add regressions for the tab and newline forms alongside the POSIX, Windows, UNC, and separator-less cases already covered in tests/lab-post-merge-hardening.test.ts. Then tick the readiness boxes and the gate will mark it ready.

@luvs01
luvs01 force-pushed the agent/fix-lab-file-uri-admission-20260904 branch from 77025dd to 2b0653f Compare September 4, 2026 08:10
@luvs01
luvs01 marked this pull request as ready for review September 4, 2026 08:11
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@github-actions
github-actions Bot marked this pull request as draft September 4, 2026 08:11
@github-actions
github-actions Bot marked this pull request as ready for review September 4, 2026 08:13
@luvs01

luvs01 commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

Thanks — the whitespace bypass is addressed and the PR is now rebased and ready for review.

What changed in the new fourth commit 2b0653f4a3297353ef45ebdb3404259fda838230:

  • src/lab/events/limits.ts strips ASCII URL whitespace (/[\t\r\n]/g) and runs FILE_URI_RE against both the original value and the stripped form. Testing both keeps the delimiter-prefixed detection that the earlier commits rely on, while closing the split-scheme forms that WHATWG URL parsing would normalize back into a valid file:/// URL.
  • tests/lab-post-merge-hardening.test.ts adds 7 regressions next to the existing POSIX, Windows, UNC, and separator-less cases: fi\nle:///etc/passwd, fil\te:///etc/passwd, file\r:///etc/passwd, file\n:///etc/passwd, detail\nfile:///etc/passwd, detail\rfile:etc/passwd, and detail\tfile:C:\private\secret.

The change is 2 production lines and 7 test lines; no other file is touched.

Rebased from 072df52e onto current dev 5364ce01d2156e8784bf2be42944ef983aa27b33 in an isolated worktree. The pre-rebase and post-rebase diffs hash identically (SHA-256 20C045BF054DACA4DD9E7931A7D590E76DE6EA90F1FFB8E4280D7586E0FE9312), so the rebase carried no content change.

Local gates on the rebased head:

  • bun test --isolate ./tests/lab-post-merge-hardening.test.ts — 8 pass, 0 fail, 48 expect() calls
  • bun run typecheck — passed
  • git diff --check 5364ce01d2..HEAD — passed

One thing worth flagging separately: bun run privacy:scan fails on this branch, but it is not caused by this PR. A clean checkout of dev at 5364ce01d2 reproduces the identical failure:

Privacy scan failed:
tests/oauth-manual-code.test.ts:63 meta-api-key: <redacted>

That is the literal Muse Code key added by #3437, so the scanner flags it on dev itself. On the previous base 20011a1c48 the same scan passed against this change set. Happy to open a separate issue for it if you would like.

@lidge-jun lidge-jun left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Approving. The whitespace bypass I raised in triage is closed, and closed at the right layer.

Verified on this head: ASCII_URL_WHITESPACE_RE strips tab/CR/LF before the scheme test, so fi\nle:///etc/passwd, fil\te:... and file\r:... are all rejected as raw_path — I ran those three directly against enforceEventStructureLimits rather than trusting the test names. https://example.com/path and profile:///etc/passwd still pass, so the scheme boundary in FILE_URI_RE is not over-matching on a substring.

bun test tests/lab-post-merge-hardening.test.ts is 8 pass / 0 fail, and the negative cases are asserted rather than only the positive ones. Thanks for the quick turnaround on the finding.

@lidge-jun
lidge-jun merged commit 60b196e into lidge-jun:dev Sep 4, 2026
22 of 28 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working review-ready

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants