Skip to content

fix(security): harden untrusted-input sinks (find file-write, source links, deep links)#4158

Open
obchain wants to merge 3 commits into
tinyhumansai:mainfrom
obchain:fix/security-harden-untrusted-sinks
Open

fix(security): harden untrusted-input sinks (find file-write, source links, deep links)#4158
obchain wants to merge 3 commits into
tinyhumansai:mainfrom
obchain:fix/security-harden-untrusted-sinks

Conversation

@obchain

@obchain obchain commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Harden three places where untrusted input reaches a sensitive sink, each by routing it through the guard the surrounding code already uses elsewhere. Defense-in-depth; no behaviour change for legitimate input.
  • Rust coreclassify_command now treats find -fprintf/-fprint/-fprint0/-fls as Write (approval-gated), alongside the existing -delete.
  • Frontend — the agent-process "sources" panel only links http(s) URLs; notification deep_link/deepLink is honored only when it is a relative in-app path.

Problem

  • find file-write actions bypassed the approval gate. find is allowlisted and read-only by default. -exec/-execdir/-ok/-okdir are blocked and -delete is classified Write, but the -fprintf/-fprint/-fprint0/-fls primaries — which write find's output to a named file at an arbitrary path — were classified Read, so they ran with no approval prompt even at the default Supervised tier. That is an arbitrary-path write that side-steps the gated file-write tools and their workspace confinement (e.g. appending to a shell rc file).
  • Agent-source links rendered an unvalidated scheme. extractAgentSources builds the "sources visited" list from the model's raw url tool-call argument (prompt-injection-influenceable) and renders it as an <a href>. The scheme was not checked, so a javascript:/data:/file: value could reach the anchor (CSP blocks script execution, so the practical risk is forced navigation — this closes the gap regardless).
  • Notification deep links navigated to an unvalidated target. resolveIntegrationRoute/resolveSystemRoute returned the core-supplied deep_link/deepLink verbatim into navigate(). That value derives from untrusted inbound provider content; HashRouter contains most abuse, but nothing kept the value a relative in-app route.

Solution

  • classify_segment (src/openhuman/security/policy/policy_command.rs): add -fprintf/-fprint/-fprint0/-fls to the findWrite match, mirroring -delete. Now approval-gated at Supervised.
  • extractAgentSources (app/src/utils/toolTimelineFormatting.ts): new isHttpUrl filter — only http(s) URLs become source rows.
  • notificationRouter.ts: new isSafeInAppPath (single leading /, no //, no backslash); unsafe values fall through to the provider/category default and are logged as ignored.
  • Each change is a self-contained commit with happy-path + hostile-input tests.

Submission Checklist

  • Tests added or updated (happy path + at least one failure / edge case) — Rust classify_find_file_write_actions_are_write (Write actions + read-only search); Vitest extractAgentSources (http(s) vs javascript:/data:/file:) and notificationRouter unsafe-deep_link cases.
  • Diff coverage ≥ 80% — every changed line is exercised by the new tests (pnpm debug unit on the two suites: 64 passed; focused cargo test on the policy module: 157 passed incl. the new case).
  • Coverage matrix updated — N/A: defense-in-depth hardening, no added/removed/renamed feature rows.
  • All affected feature IDs from the matrix are listed under ## Related — N/A: no matrix feature rows affected.
  • No new external network dependencies introduced — none added.
  • Manual smoke checklist updated if this touches release-cut surfaces — N/A: no release-cut surface changed.
  • Linked issue closed via Closes #NNN — N/A: hardening surfaced by an internal security review; no tracking issue.

Impact

  • Desktop/web. Security-positive: closes one approval-gate bypass (find file-write) and two untrusted-URL/route hardening gaps. No change for legitimate input — benign find searches stay Read, http(s) sources still link, relative in-app deep links still navigate.

Related

  • Closes: N/A (internal security hardening; no tracking issue)
  • Follow-up PR(s)/TODOs: none

AI Authored PR Metadata (required for Codex/Linear PRs)

Linear Issue

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

Commit & Branch

  • Branch: fix/security-harden-untrusted-sinks
  • Commit SHA: cca877d

Validation Run

  • pnpm --filter openhuman-app format:check — Prettier clean on changed files
  • pnpm typecheck — ESLint clean on changed files; no type errors in touched modules
  • Focused tests: notificationRouter + toolTimelineFormatting Vitest (64 passed); cargo test policy module (157 passed)
  • Rust fmt/check (if changed): cargo fmt --check clean on the two policy files
  • Tauri fmt/check (if changed): N/A — no Tauri-shell files changed

Validation Blocked

  • command: N/A
  • error: N/A
  • impact: N/A

Behavior Changes

  • Intended behavior change: find file-write/delete actions require approval at Supervised; non-http(s) agent sources and non-relative notification deep links are dropped.
  • User-visible effect: none for legitimate input; a malformed source/deep-link is silently ignored instead of acted on.

Parity Contract

  • Legacy behavior preserved: benign find searches stay Read; http(s) sources and relative in-app deep links behave exactly as before.
  • Guard/fallback/dispatch parity checks: unsafe deep links fall through to the existing provider/category default resolution.

Duplicate / Superseded PR Handling

  • Duplicate PR(s): none
  • Canonical PR: this PR
  • Resolution: N/A

Summary by CodeRabbit

  • Bug Fixes
    • Safer routing now ignores suspicious or invalid deep links and falls back to the app’s default pages.
    • Timeline source links now only include valid web URLs, reducing broken or unsafe links.
    • Command handling more accurately treats file-writing find actions as write operations, improving safety checks.

obchain added 3 commits June 26, 2026 13:13
find -fprintf/-fprint/-fprint0/-fls write output to a named file — an arbitrary-path write that side-steps the gated file-write tools and their workspace confinement. They were classified Read (no approval prompt), so classify them Write alongside -delete so Supervised gates them.
extractAgentSources renders the model's raw tool-call url arg as an anchor href. That arg is prompt-injection-influenceable, so gate it to http(s) — a javascript:/data:/file: value must never reach an <a href>.
deep_link/deepLink derive from untrusted inbound provider content and are fed straight to navigate(). Honor them only when they are a relative in-app path (single leading slash, no //, no backslash); unsafe values fall through to the provider/category default.
@obchain obchain requested a review from a team June 26, 2026 07:45
@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The PR tightens deep-link handling in notification routing, filters agent source URLs to http(s), and expands Rust find policy classification to treat file-writing actions as writes.

Changes

Notification route hardening

Layer / File(s) Summary
Deep-link validation and fallback routes
app/src/lib/notificationRouter.ts, app/src/lib/notificationRouter.test.ts
Deep-link routing now accepts only safe in-app paths and falls back to the existing provider or category routes for unsafe values; tests cover rejected integration and system deep links.

Timeline source URL filtering

Layer / File(s) Summary
HTTP(S) source filtering
app/src/utils/toolTimelineFormatting.ts, app/src/utils/__tests__/toolTimelineFormatting.test.ts
extractAgentSources now keeps only valid http(s) URLs, and tests cover hostname titling, deduplication, scheme rejection, and non-URL tools.

Find command write classification

Layer / File(s) Summary
find output actions as writes
src/openhuman/security/policy/policy_command.rs, src/openhuman/security/policy/policy_tests.rs
find segments using -fprintf, -fprint, -fprint0, or -fls now classify as CommandClass::Write, with tests covering those actions and plain -print.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested labels

bug, rust-core

Poem

A bunny hopped through links so sly,
and nibbled bad URLs goodbye.
/chat stayed snug, the burrow bright,
while find learned writes and got them right.
🐰✨

🚥 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 accurately summarizes the main security hardening changes across find writes, source links, and deep links.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.


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.

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

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +796 to +799
| "-fprintf"
| "-fprint"
| "-fprint0"
| "-fls"

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 quoted find write predicates

Because classify_command builds args with split_whitespace() and never removes shell quotes, a command like find . '-fprint' /tmp/out leaves the arg as '-fprint' and misses these exact matches, so it is classified as Read; the shell then strips the quotes before invoking GNU find, whose help lists -fprintf FILE, -fprint0 FILE, -fprint FILE, and -fls FILE as file-writing actions. In read-only/supervised contexts this lets the newly guarded arbitrary-file-write path run without the intended block or prompt, so normalize quoted args (or use a shell lexer) before matching these predicates.

Useful? React with 👍 / 👎.

@coderabbitai coderabbitai Bot added bug rust-core Core Rust runtime in src/: CLI, core_server, shared infrastructure. labels Jun 26, 2026

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

🧹 Nitpick comments (1)
app/src/utils/__tests__/toolTimelineFormatting.test.ts (1)

275-309: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Move this suite next to toolTimelineFormatting.ts.

This adds new coverage in app/src/utils/__tests__/toolTimelineFormatting.test.ts, but the repo rule for app/src/**/*.test.{ts,tsx} is to co-locate tests alongside the source file. Please move this suite to app/src/utils/toolTimelineFormatting.test.ts instead of growing the __tests__ location further.

As per coding guidelines, app/src/**/*.test.{ts,tsx}: Co-locate unit tests as *.test.ts(x) under app/src/** alongside source files.

🤖 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 `@app/src/utils/__tests__/toolTimelineFormatting.test.ts` around lines 275 -
309, The new extractAgentSources coverage is placed in the shared __tests__
folder, but it should be co-located with the source utility per the app/src test
layout rule. Move the describe('extractAgentSources') suite from the current
__tests__ file into toolTimelineFormatting.test.ts alongside
toolTimelineFormatting.ts, and keep the same assertions and helpers so the
behavior stays covered in the colocated test file.

Source: Coding guidelines

🤖 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.

Nitpick comments:
In `@app/src/utils/__tests__/toolTimelineFormatting.test.ts`:
- Around line 275-309: The new extractAgentSources coverage is placed in the
shared __tests__ folder, but it should be co-located with the source utility per
the app/src test layout rule. Move the describe('extractAgentSources') suite
from the current __tests__ file into toolTimelineFormatting.test.ts alongside
toolTimelineFormatting.ts, and keep the same assertions and helpers so the
behavior stays covered in the colocated test file.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 16481101-a52b-4f45-a782-0e9b64208ccf

📥 Commits

Reviewing files that changed from the base of the PR and between ebba7b6 and cca877d.

📒 Files selected for processing (6)
  • app/src/lib/notificationRouter.test.ts
  • app/src/lib/notificationRouter.ts
  • app/src/utils/__tests__/toolTimelineFormatting.test.ts
  • app/src/utils/toolTimelineFormatting.ts
  • src/openhuman/security/policy/policy_command.rs
  • src/openhuman/security/policy/policy_tests.rs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug rust-core Core Rust runtime in src/: CLI, core_server, shared infrastructure.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant