Repository navigation
Conversation
Master did not build at all. Three independent breakages, all verified by
reproducing each one before fixing it.
1. 32 vweb route handlers were declared without `mut ctx Context`.
Current vweb dispatches `app.$method(mut user_context, ...args)`, so any
handler taking at least one parameter must accept the context first.
Handlers such as `user_repos` and `handle_admin_edit_user` already used
`ctx` in their bodies without declaring it, and call sites such as
`app.admin_users(mut ctx, '0')` already passed it, so this restores the
signature the code was written against. `method.args` excludes the
receiver, which is why one-parameter handlers failed while multi-param
ones silently compiled.
2. `struct FileInfo` in repo/file.v collided with `os.FileInfo`.
Substituting it into the `api.ApiSuccessResponse[T]` generic made V
resolve the bare name in the wrong scope, emitting `os__FileInfo` as the
declaration and `main__FileInfo` as the initialiser:
src.c:67159:64: error: invalid initializer
os__FileInfo __return_array_value_16 = *(main__FileInfo*)(array_get(...));
Renamed to `RepoFileInfo`, which no stdlib type shadows.
3. `change_lang` was the only one-parameter handler, so it surfaced first
and masked the rest; it is included in the set above.
Verified:
- `v -d sqlite -o gitly.exe .` exits 0 and produces the binary.
- `v -d sqlite test utils_test.v` -> 1 passed, 1 total.
- `git diff --stat` touches only .v files; no build artifacts staged.
Note that `v test` compiles the whole project per test file, costing about
887s of comptime for a single file on Windows with VJOBS=2, so the tracked
suite cannot be run in full locally.
This was referenced Oct 11, 2026
…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
Master does not compile. This restores the build with no behaviour change.
Two independent defects, plus a name collision that only the first one hid:
veb.vdispatchesapp.$method(mut user_context, ...args). Sincemethod.argsexcludes thereceiver, a handler with one parameter has
args.len == 1, so it falls into the no-argumentbranch and the context is passed to a method that does not accept it.
change_langwas theonly one-parameter handler, so it failed first and masked 31 others. The multi-parameter
handlers compiled, but silently received form values where they expected path parameters.
user_reposandhandle_admin_edit_useralready referencedctxin their bodies withoutdeclaring it, and call sites such as
app.admin_users(mut ctx, '0')already passed it, sothis restores the signature the code was written against — see the merged #125.
The
FileInfocollision surfaced once the frontend stopped erroring. Substituting it intoapi.ApiSuccessResponse[T]made V resolve the bare name in the wrong scope, emittingos__FileInfoas the declaration andmain__FileInfoas the initialiser.Evidence
Before — V checker, on
v -d sqlite -d use_openssl -o gitly.exe .at9594917:Then, after that one handler was fixed:
After —
v -d sqlite -o gitly.exe .exits 0 and producesgitly.exe:Test run,
v -d sqlite test utils_test.v:Only one test file was run deliberately.
v testrecompiles the whole project per file, and onefile costs ~887s of comptime with
VJOBS=2, so the 44-file tracked suite is not runnable locally(~11h estimated). Full validation is left to CI, which is why the red Windows and
-autofreejobsare tracked separately.
Also checked, and red before this change for unrelated reasons —
v fmt -verify .reports 54unformatted files, 44 of which this PR never touches. Not adding
v fmt -verifyas a blocking CIstep in the same change that reddens the gate.
Merge Danger
Door: Two-way.
git revertrestores the previous broken-but-known state, and no data, schema,or on-disk format changes.
Blast Radius: Every route handler in the app. All 32 signatures change, but only to add the
context parameter the bodies already used. The risk is not the signatures, it is that they were
wrong for every request: handlers that took path parameters without
ctxwere receiving form orquery values, so pages like
/:username/reposand/:username/:repo_name/branchescould not haveresolved their own identifiers. This PR makes them compile; whether they now resolve correctly at
runtime is untested here and should be confirmed by a reviewer clicking through the affected routes.
RepoFileInfois an internal type used only byhandle_api_repo_files; the JSON shape isunchanged, so the API response for
/api/v1/repos/:id/tree/filesis byte-identical.