fix(dashboard): apply allowed_roots check to file:// URLs in add_remote_skill (CWE-22)#315
Closed
sebastiondev wants to merge 1 commit into
Closed
fix(dashboard): apply allowed_roots check to file:// URLs in add_remote_skill (CWE-22)#315sebastiondev wants to merge 1 commit into
sebastiondev wants to merge 1 commit into
Conversation
…WE-22) The file:// branch in add_remote_skill() read any local file without the path-traversal guard that the bare-path branch already applied. Resolve the path and enforce the same OCLAW_HOME / project-root allowed_roots check so file:// URLs cannot escape the permitted directories. Adds tests/test_cwe22_file_url.py with three cases: - file:// outside allowed_roots → rejected - file:// inside OCLAW_HOME → still works - file:///etc/passwd → rejected
|
Closing this to reduce the open-PR pile-up — we have multiple outstanding security contributions to this repo and that volume is not fair on your review queue. Keeping #318 (CWE-918: block SSRF via generic webhook URL (CWE-918)) as the primary one to focus attention on. Happy to revisit this finding separately later if it is still relevant. Apologies for the noise. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
add_remote_skill()indashboard/server.pyaccepts skill source URLs in several forms (http(s)://, file://, absolute/relative path). The bare-path branch correctly checks the resolved path againstallowed_roots(OCLAW_HOMEand the project root), but thefile://branch had no such check. As a result, afile:///etc/passwd-style URL was read directly and the contents were imported as a "skill" into the workspace.dashboard/server.py→add_remote_skill()(theelif source_url.startswith('file://')branch, around line 350)source_url→pathlib.Path(source_url[7:])→.read_text()with no allow-list check.Fix
Mirror the guard already used by the sibling bare-path branch onto the
file://branch:Rationale: keeps both branches consistent, uses the same
allowed_rootsalready trusted elsewhere in the file, andresolve()collapses..segments before the prefix check so traversal payloads likefile:///tmp/../etc/passwdare normalized first.The diff is minimal (4 added lines plus the
.resolve()call), no behavior change for legitimatefile://URLs that point insideOCLAW_HOMEor the project tree.Tests
Added
tests/test_cwe22_file_url.pywith three regression tests:test_file_url_path_traversal_blocked—file://URL pointing outside allowed roots is rejected.test_file_url_within_allowed_roots_works— legitimatefile://URL insideOCLAW_HOMEstill works.test_file_url_etc_passwd_blocked—file:///etc/passwdis rejected.Result locally:
Exploitability
The dashboard server currently prints
认证未配置,所有 API 公开访问when no auth is configured, which is the default. In that mode, anyone who can reach the dashboard port (default127.0.0.1, but operators commonly bind to0.0.0.0or expose it via tunnels) can calladd_remote_skillwith afile://URL and read any local file whose contents parse as SKILL.md frontmatter —/etc/passwd, SSH keys, env files, application configs, etc. Even with auth enabled, the same call can be reached via CSRF against an authenticated user if CORS/SameSite settings are permissive, or by any local process running as a different user able to talk to the loopback port.Adversarial review
Before submitting, we tried to disprove this. We checked whether the bare-path branch's existing
allowed_rootscheck would catchfile://inputs first — it doesn't, dispatch is by URL scheme sofile://skips it entirely. We checked whether auth is required by default — it isn't; the server explicitly logs that all APIs are public when no auth is configured. We checked whether the read is sandboxed at the OS level — there's no such sandbox;pathlib.Path.read_text()runs with full server privileges. The fix is the smallest change that closes the gap and matches the pattern already used in the same function.cc @lewiswigmore