Skip to content

fix(vercel): refuse empty and dot-only identifiers in URL paths - #41

Merged
M3gA-Mind merged 3 commits into
tinyhumansai:mainfrom
M3gA-Mind:fix/vercel-dot-segment-ids
Oct 23, 2026
Merged

M3gA-Mind merged 3 commits into
tinyhumansai:mainfrom
M3gA-Mind:fix/vercel-dot-segment-ids

Conversation

@M3gA-Mind

@M3gA-Mind M3gA-Mind commented Oct 23, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

The Vercel adapter builds its request paths with encode_segment, which percent-escapes everything outside RFC 3986's unreserved set but leaves . unescaped. So a site or deployment id of .. became a dot-segment: find_site("..") requested /v9/projects/.. (which resolves to /v9/), and set_env("..", …) posted to /v10/projects/../env, i.e. /v10/env, an authenticated request to a different endpoint than the one named. Escaping the dots would not help: the URL parser decodes %2E%2E back into a dot-segment. An empty id addressed the collection (/v9/projects/). The ids can come from a model's tool arguments. The doc comment on PATH_SEGMENT also claimed the escaping prevented .., which it did not.

Fix, at the one function all nine path uses go through: encode_segment returns Error::InvalidIdentifier for "", "." and "..", and each call site propagates it with ?, so nothing is sent. InvalidIdentifier is a new variant on the (#[non_exhaustive]) shared error enum, as the error module's guidance asks ("add a variant rather than encoding new context into an existing message"). The doc comment now says what the escaping does and does not do.

Related issue

None.

API or behavior changes

  • New Error::InvalidIdentifier ("identifier must not be empty, . or .."). The enum is #[non_exhaustive], so this is additive.
  • Vercel calls taking a site, project or deployment id return it for "", . or .. instead of sending the request.

Validation

  • rustfmt --edition 2024 --check on the touched files
  • cargo clippy --all-targets --all-features -- -D warnings: clean
  • N/A: cargo build --all-targets --all-features: left to CI
  • N/A: cargo test --all-features: only the regression tests ran locally (below); CI runs the suite

CARGO_INCREMENTAL=0 cargo test --lib -- dot_only_identifiers_are_refused

  • Before the fix: FAILED. 0 passed; 1 failed. find_site("") returned Ok after sending a request.
  • After the fix: ok. 3 passed; 0 failed (with the existing a_path_shaped_site_name_cannot_redirect_the_request and a_missing_site_is_not_an_error).

Tests

New providers/vercel/dot_segment_tests.rs (sibling; mod_tests.rs is already long): for "", "." and "..", find_site, set_env, deployment and list_domains each return InvalidIdentifier, and the mock provider receives no request.

Documentation

The PATH_SEGMENT / encode_segment doc comments are corrected.

Checklist

  • The change is focused on one logical change
  • No new #[allow(...)], #[ignore], or relaxed lints (the new test file carries the same unwrap_used/expect_used allow as the existing test files)
  • No secrets, tokens, or .env contents in the diff or the description

Inherited CI red

module-ci / supply-chain fails on this PR because of RustSec advisories against the existing dependency tree (cargo-deny reports RUSTSEC-2026-0346 through 0349, 0367 through 0369, 0381 and 0382), including RUSTSEC-2026-0346 (Debug impls for client session values expose TLS secrets) and RUSTSEC-2026-0347 (buffered application data sent to the outer server after ECH rejection) in rustls. Both were published after main's last CI run, and this PR does not change dependencies. Fixing it needs a rustls bump on main.

encode_segment leaves dots unescaped, so a site or deployment id of '..'
became a dot-segment and addressed the parent route; escaping the dots
would not help because the URL parser decodes %2E%2E the same way. An
empty id addressed the collection. encode_segment now returns
Error::InvalidIdentifier for '', '.' and '..', before any request, and
the doc comment no longer claims '..' protection it did not have.
@coderabbitai

coderabbitai Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: a0b8b284-b038-420a-b703-d5c0d35aecff
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@tinysweeper

tinysweeper Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Tiny Sweeper review

Reviewed head: dd556242af62009d761caeb6eaaf86e862c127f8

Verdict: Ready for maintainer review · Priority: none
Coverage: Reviewed 3 of 3 files.
Pull request purpose (title): fix(vercel): refuse…

Diagnostics and supporting details

Full summary

Pull request purpose (title): fix(vercel): refuse empty and dot-only identifiers in URL paths. This pull request closes a path-resolution gap in the Vercel adapter: `encode_segment` left `.` unescaped, so identifiers of `..` or an empty string produced dot-segments that addressed the parent or collection route of an authenticated request. `encode_segment` now returns a new `Error::InvalidIdentifier` for `""`, `"."` and `".."`, and all nine path-construction call sites in the Vercel `Host` implementation propagate it with `?`, so nothing is sent. The `PATH_SEGMENT`/`encode_segment` doc comments are corrected, and a new sibling test file exercises the affected methods against a mock server.

Review snapshot

Change surface Files Review signal Count
Production 2 Active findings 0
Tests 1 Noted findings 0
Documentation 0 Resolved findings 0
Configuration 0 Pending checks/questions 0

Completeness: Complete
Test assessment: No supported feature-to-test mapping was available; this does not mean tests are absent or passed.

What changed

The shared error enum gains an additive `InvalidIdentifier` variant. `encode_segment` changes from returning a displayable encoder to returning a `Result`, rejecting `""`, `"."` and `".."` before encoding. Its nine call sites in the Vercel `Host` implementation (project lookup, env set/get, deployment and events lookup, promote, add/list domains) now use `?` to propagate the error. Documentation on the escape set and encoder is updated to state that escaping alone does not prevent dot-segments, and a new `dot_segment_tests.rs` module is registered behind `#[cfg(test)]`.

Features

  • Added — New Error::InvalidIdentifier variant: Gives callers a typed way to distinguish a refused identifier from transport or provider errors; the description characterizes it as additive. (crates/tinyhosts-bus/src/error/mod.rs pub enum Error {)
  • Modified — Fallible encode_segment rejecting empty and dot-only identifiers: Prevents `""`, `"."` and `".."` from reaching a Vercel URL path, where they would address the collection or parent route rather than the named endpoint; callers receive an error instead of an authenticated request going to a different endpoint. (src/providers/vercel/mod.rs fn buckets(dimension: &str, data: &Value) -> Vec<AnalyticsBucket> {, src/providers/vercel/mod.rs fn env_targets(targets: &[DeploymentTarget]) -> Vec<&'static str> {)
  • Internal refactor — Propagate encode_segment errors through Vercel Host methods: Every request path assembled in the Vercel `Host` implementation now fails fast on a bad identifier, so no redirected request is issued. (src/providers/vercel/mod.rs impl Host for Vercel {)

Tests

No supported feature-to-test mapping was produced. Test execution is not inferred.

Findings

No active actionable findings.

Before merge

None.

Agent review details

critique

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Lane summary: Reviewed 3 files; 0 findings.

security

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Lane summary: Reviewed 3 files; 0 findings.

tests

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Positive: The tests lane answered, reporting the new error branch and variant are covered across four operations with a no-request assertion, and no defect.
  • Lane summary: The change hardens `encode_segment` so an empty, `.` or `..` identifier returns `Error::InvalidIdentifier` before any request is built, and threads the new `?` through every Vercel call site; the new `dot_segment_tests.rs` exercises the error path across four operations and asserts no request reached the mock. I found no defect: the new error variant has a test, the error path is covered, and the existing path-shaping test still pins the escaping behaviour.

commits

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Positive: The commits lane answered that no sensitive content appears in committed files.
  • Lane summary: No sensitive content in committed files.

description

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Positive: The description lane answered that the description matches the diff, citing the fallible `encode_segment` at new `InvalidIdentifier`, the test file, the corrected doc comments and the inherited CI failure, with no description defect.
  • Lane summary: The description matches the diff: it presents the empty/dot-segment change at encode_segment and the new InvalidIdentifier error variant accurately, including the test file, the corrected doc comments, and the inherited CI failure. I found no description defect to report.
Evidence and run details
  • Models: ladder/vectors-oai3, deepseek-v4.1-flash
  • Spend: $0.000415
  • Tokens: 234769 input · 3621 output · 26112 cached · 232 embedding
  • Updated: 2026-10-23T12:31:03Z
Head State Pass summary
dadf2f6c94c3 ready for maintainer review 0 active finding(s), 0 resolved finding(s) (at 2026-10-23T00:25:40Z)
537c0602cb2a ready for maintainer review 0 active finding(s), 0 resolved finding(s) (at 2026-10-23T12:08:55Z)
dd556242af62 ready for maintainer review 0 active finding(s), 0 resolved finding(s) (at 2026-10-23T12:31:03Z)

tinysweeper 0.1.2

@tinysweeper tinysweeper Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tinysweeper found nothing blocking. Approving.

Diagnostics

Total spend: $0.0008

@M3gA-Mind
M3gA-Mind merged commit 8897312 into tinyhumansai:main Oct 23, 2026
20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant