fix: allow-list git_url before install_extension spawns git clone#168
Merged
jmaxdev merged 2 commits intoTrixtyAI:mainfrom Apr 21, 2026
Merged
Conversation
|
Thanks for the contribution! I'll review it as soon as possible. If you have still changes, please mark this PR as draft and all reviews will be cancelled. Tests reviews will be re-run only when the PR is marked as ready for review. |
There was a problem hiding this comment.
Pull request overview
Hardens extension installation by validating marketplace-provided git_url before invoking git clone, and adding defensive Git protocol configuration to reduce transport-helper abuse risk.
Changes:
- Add
validate_git_clone_urlwith a stricthttps://+ host allowlist + path/character constraints. - Invoke
git clonewith-c protocol.*.allow=neverflags to disable risky transports at runtime. - Add unit tests covering accepted/rejected URL cases.
Comments suppressed due to low confidence (1)
apps/desktop/src-tauri/src/extensions.rs:344
target_dir.to_str().unwrap()can panic if the path contains non-UTF8 data. Prefer passing the path as anOsStr/Pathargument (e.g., build theCommandwith.arg(&git_url)and.arg(&target_dir)), or return a proper error if UTF-8 conversion fails instead of unwrapping.
"1",
&git_url,
target_dir.to_str().unwrap(),
])
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
d8cc1cf to
eb34cbe
Compare
matiaspalmac
added a commit
to matiaspalmac/ide
that referenced
this pull request
Apr 21, 2026
Addresses review feedback on TrixtyAI#168: `id` was forwarded straight into `ext_dir.join(&id)` across install/uninstall/update/is_active/toggle/read_script. A compromised catalog entry could therefore set `id = "..\..\..\Windows\foo"` and cause `git clone` (or `remove_dir_all`, or a file read) to operate **outside** the app-data extensions sandbox. Added `validate_extension_id` that enforces: - Non-empty, not `.`, not `..`. - No path separators (`/`, `\`) and no `..` substring. - No leading `-` (so the id can never be reparsed as a flag elsewhere). - Characters restricted to ASCII alphanumeric + `-`, `_`, `.` — the safe marketplace-slug charset that every current catalog entry already satisfies. Applied to every Tauri command that takes an `id: String`: `install_extension`, `uninstall_extension`, `update_extension`, `is_extension_active`, `toggle_extension_state`, `read_extension_script`. The string-only commands (`is_active`, `toggle_state`) don't join `id` to a path today, but validating them keeps a malicious catalog entry from poisoning `disabled_extensions.json` with invalid entries that later code may still path-join. Added 5 unit tests covering accept/reject cases including the explicit traversal shapes (`../evil`, `..\\..\\Windows`, `foo..bar`).
install_extension forwarded the caller-supplied git_url straight to git clone. Any scheme went through — file://, Windows UNC, ssh://, git://, and ext:: transport helpers — which meant a compromised catalog entry could trigger arbitrary code at install time via git helpers, symlink tricks, or credential-embedding URLs. Added validate_git_clone_url before the clone call. It enforces: - Strict https:// scheme (case-sensitive). - Host in a small allowlist (github.com, gitlab.com, bitbucket.org). Every legitimate catalog entry today already satisfies this, and resolveGitRepoUrl on the frontend produces exactly this shape. - Path is exactly `<owner>/<repo>` or `<owner>/<repo>.git`, with ASCII-safe segments that cannot start with `-` (no flag injection via --upload-pack and friends). - No credentials (`@`), no transport-helper syntax (`::`), no whitespace or control characters. As belt-and-suspenders, the clone invocation now also passes `-c protocol.ext.allow=never -c protocol.file.allow=never -c protocol.git.allow=never` so git itself refuses those transports even if the URL check is ever bypassed. The allow-listed https host is unaffected. Added 12 unit tests covering the accept/reject matrix.
Addresses review feedback on TrixtyAI#168: `id` was forwarded straight into `ext_dir.join(&id)` across install/uninstall/update/is_active/toggle/read_script. A compromised catalog entry could therefore set `id = "..\..\..\Windows\foo"` and cause `git clone` (or `remove_dir_all`, or a file read) to operate **outside** the app-data extensions sandbox. Added `validate_extension_id` that enforces: - Non-empty, not `.`, not `..`. - No path separators (`/`, `\`) and no `..` substring. - No leading `-` (so the id can never be reparsed as a flag elsewhere). - Characters restricted to ASCII alphanumeric + `-`, `_`, `.` — the safe marketplace-slug charset that every current catalog entry already satisfies. Applied to every Tauri command that takes an `id: String`: `install_extension`, `uninstall_extension`, `update_extension`, `is_extension_active`, `toggle_extension_state`, `read_extension_script`. The string-only commands (`is_active`, `toggle_state`) don't join `id` to a path today, but validating them keeps a malicious catalog entry from poisoning `disabled_extensions.json` with invalid entries that later code may still path-join. Added 5 unit tests covering accept/reject cases including the explicit traversal shapes (`../evil`, `..\\..\\Windows`, `foo..bar`).
1790178 to
30fbcec
Compare
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.
[Fix]: Allow-list
git_urlbeforeinstall_extensionspawnsgit cloneDescription
install_extensionforwarded the caller-suppliedgit_urlstraightto
git clone. Any scheme went through —file://, Windows UNC,ssh://,git://, and the especially dangerousext::transporthelpers. A compromised catalog entry could therefore trigger arbitrary
code at install time via git helpers, symlink tricks, or
credential-embedding URLs. Combined with the plain-HTTP registry fixed
in #162 and the fragile
.replacederivation fixed in #157, this wasthe final gate between a MITM and code execution on the user's
machine.
Change
apps/desktop/src-tauri/src/extensions.rs— newvalidate_git_clone_urlis called beforeinstall_extensionbuilds a target directory, and the clone invocation now runs with
defensive
git -cflags.URL validation
https://scheme (case-sensitive). CatchesHTTP://,http://,file://,ssh://,git://, UNC\\host\share, etc.github.com,gitlab.com,bitbucket.org.Every legitimate catalog entry today already satisfies this, and
the frontend helper
resolveGitRepoUrlproduces exactly thisshape.
<owner>/<repo>or<owner>/<repo>.git.-,_,.; no leading-(prevents--upload-pack/--config=flag injection throughthe path).
@in theauthority section (
https://user@github.com/...,https://github.com@evil.example/...).::anywhere in theURL.
Clone hardening
Clone is now invoked as:
The allow-listed
httpshost is unaffected. If anything ever getspast the URL check, git itself refuses those transports at runtime.
Tests
Added
git_url_validation_testswith 12 cases covering theaccept/reject matrix.
cargo test git_url_validation→ 12/12 pass.Trade-offs
Catalog entries on other git hosts (codeberg, sourcehut, self-hosted
instances) would be rejected. That matches the marketplace's
current scope; widening it later is a one-line diff.
.gitsuffix required. I considered requiring.giton therepo segment, but GitHub/GitLab both redirect
…/owner/repoto the.gitform for git clients, andresolveGitRepoUrlon thefrontend already appends
.gitwhen it normalizes URLs. Bothshapes are accepted.
update_extension/uninstall_extensionnot touched. Thoseoperate on the already-cloned directory and don't take a URL
argument, so they're not part of the
git cloneattack surfacethe issue describes. Hardening them (e.g. adding the same
-cflags to
git pull) is a separate improvement.Verification
cargo check,cargo clippy --tests -- -D warnings→ clean.cargo test git_url_validation→ 12 passed.addon on github.com/TrixtyAI) → clone still succeeds.
git_url_validation_tests.Related Issue
Fixes #55
Checklist