Repository navigation
Conversation
…real threat model Audit of the reported command injection attack surface, done against the current tree rather than the 2023 reports, which cite a `src/` layout that no longer exists. Findings: - Every external command now runs through an argument vector. `os.exec` is called with an array in all four sites in `git/git.v`, and `Git.exec_in_dir_with_env` sets `p.set_args(...)` directly. Nothing concatenates user input into a string that is later handed to a shell. - `split_command` is purely lexical: it word-splits and unquotes, and does no command substitution, variable expansion, or globbing. Its result is passed as argv, so it cannot introduce shell metacharacters. - `Git.exec_shell` was the one remaining `/bin/sh -c` wrapper and had zero callers anywhere in the repository. Removed, with a note in `git/git.v` telling future authors to add an argument-array helper instead of a string one, so the footgun does not get reintroduced. - `Repo.git()` guards against `&` and `;`, which suggests a shell is involved. It is not. Replaced with a comment stating the guard is not a security control, and naming the actual residual risk: argument injection. A ref beginning with `-` can still be read by git as an option, and `split_command` breaks a ref containing a space into several arguments. The callers that interpolate a caller-supplied ref are in `pr_routes.v` and `repo/branch.v`. Behaviour is deliberately unchanged here; fixing it means moving those to `Git.exec_in_dir_with_env` with a `--` separator, which is a larger change. Private-repository visibility (vlang#247) is already enforced. `App.can_read_repo` in `repo/repo.v` returns 404 for a private repo unless the requester is logged in and has at least reporter access, and every repo-scoped read route calls it before running a git command or returning repo data. `repo/repo_routes.v` `tree()` is one of those callers. Regression tests already exist in `api/endpoints_test.v` and `pr_authorization_test.v`. SECURITY.md is new. The reporter of vlang#246 asked for a security policy in 2023 and the repository still has none, so this is the part of that report that is still open. It covers private disclosure, the supported-versions position, the scope of untrusted input, and the known boundaries that are deliberate design decisions rather than oversights, so a future report can be triaged against them instead of rediscovering them.
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
Audit of the reported command-injection surface, done against the current tree rather than the 2023 reports, which cite a
src/layout that no longer exists.Plus
SECURITY.md, which the reporter of #246 asked for in 2023 and which the repository still does not have.Evidence
The 2023 report's cited files do not exist.
src/repo_routes.v,src/commit_routes.v, andsrc/repo_service.vare all gone; the tree was restructured and the helpers are now ingit/git.vandrepo/repo.v. So the report cannot be acted on as written and has to be re-derived.Every external command runs through argv. All four
os.execcall sites ingit/git.vpass an array:split_command(git/git.v:218) is purely lexical — word splitting and unquoting, with no command substitution, variable expansion, or globbing — and its result goes to argv.exec_shellhad zero callers. A repository-wide search forexec_shellreturns exactly one match, its own definition:Private-repository visibility (#247) is already fixed.
repo/repo.v:1796:That check is called at 43 sites across every repo-scoped read route, including
tree()inrepo/repo_routes.v:753, which is the route the report described (/:username/:repo_name). Regression tests already exist inapi/endpoints_test.v:919(test_api_v1_private_repo_visibility_from_other_user, which asserts 404 for a private repo from another authenticated user, and 200 for the owner) andpr_authorization_test.v.Build still passes after the removal:
Merge Danger
Door: Two-way.
exec_shellhad no callers, so removing it cannot change behaviour; the rest is comments and a new file. Reverting is clean.Blast Radius: Small, but one thing deserves scrutiny.
I deliberately did not fix the residual issue I found, and named it in the code instead.
Repo.git()guards against∧, which reads like a shell guard but is not one, because there is no shell. The actual residual risk is argument injection: a ref name beginning with-can still be interpreted by git as an option, andsplit_commandsplits on whitespace, so a ref containing a space becomes several arguments. The callers that interpolate a caller-supplied ref arepr_routes.v:859/pr_routes.v:886(log ${base}..${head},diff ${base}...${head}) andrepo/branch.v:67.Fixing that properly means moving those callers onto
Git.exec_in_dir_with_envwith an argument array and a--separator, which is a behavioural change I cannot test here beyond a compile. It is a separate change and should be reviewed on its own merits, not smuggled into a cleanup.