Skip to content

fix: make gitly compile with current V (32 veb handlers missing ctx, FileInfo collision) - #292

Open
metif12 wants to merge 2 commits into
vlang:masterfrom
metif12:master
Open

metif12 wants to merge 2 commits into
vlang:masterfrom
metif12:master

Conversation

@metif12

@metif12 metif12 commented Oct 11, 2026

Copy link
Copy Markdown

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:

  repo/file.v
- struct FileInfo {          # collides with os.FileInfo
+ struct RepoFileInfo {
  repo/repo_routes.v (and 8 other files)
- pub fn (mut app App) user_repos(username string) veb.Result {
+ pub fn (mut app App) user_repos(mut ctx Context, username string) veb.Result {

veb.v dispatches app.$method(mut user_context, ...args). Since method.args excludes the
receiver, a handler with one parameter has args.len == 1, so it falls into the no-argument
branch and the context is passed to a method that does not accept it. change_lang was the
only 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_repos and handle_admin_edit_user already referenced 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 — see the merged #125.

The FileInfo collision surfaced once the frontend stopped erroring. Substituting it into
api.ApiSuccessResponse[T] made V resolve the bare name in the wrong scope, emitting
os__FileInfo as the declaration and main__FileInfo as the initialiser.

Evidence

Before — V checker, on v -d sqlite -d use_openssl -o gitly.exe . at 9594917:

C:/Users/xman/v/vlib/veb/veb.v:539:12: error: expected 2 arguments to method App.change_lang, but got 1

Then, after that one handler was fixed:

src.c:67159:64: error: invalid initializer
  os__FileInfo __return_array_value_16 = *(main__FileInfo*)(array_get(result, __return_array_idx_15));

After — v -d sqlite -o gitly.exe . exits 0 and produces gitly.exe:

EXIT=0
True

Test run, v -d sqlite test utils_test.v:

OK     182.329 ms D:/MyProjects/gitly/utils_test.v
Summary for all V _test.v files: 1 passed, 1 total. Elapsed time: 886790 ms, on 1 job. Comptime: 886574 ms. Runtime: 182 ms.

Only one test file was run deliberately. v test recompiles the whole project per file, and one
file 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 -autofree jobs
are tracked separately.

Also checked, and red before this change for unrelated reasons — v fmt -verify . reports 54
unformatted files, 44 of which this PR never touches. Not adding v fmt -verify as a blocking CI
step in the same change that reddens the gate.

Merge Danger

Door: Two-way. git revert restores 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 ctx were receiving form or
query values, so pages like /:username/repos and /:username/:repo_name/branches could not have
resolved 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.

RepoFileInfo is an internal type used only by handle_api_repo_files; the JSON shape is
unchanged, so the API response for /api/v1/repos/:id/tree/files is byte-identical.

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.
…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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant