fix(ploeg): decode forge proxy paths and limit them to what the Role needs #171

Merged
ryangr0 merged 1 commit from ryangr0/ploeg-forge-proxy-paths into development 2026-10-03 11:54:07 +00:00 AGit
Owner

The forge token proxy checked the escaped path for a literal "/../", a
trailing "/.." and "%2e%2e" only, so mixed forms such as
"/api/v1/repos/o/r/%2e./other/pulls" passed and were forwarded with the
Run's real token. Every API subpath under the repository was allowed, so a
writer harness could merge, delete, change settings, hooks, collaborators,
branch protection or keys.

The proxy now decodes each path segment and refuses any dot segment,
encoded separator (%2f, %5c), double encoding, control character or empty
segment. The only encoded separator accepted is GitLab's project ID, which
must equal the Run's project path exactly. It then enforces a per-Role
allowlist of methods and endpoints: smart-HTTP git fetch (and push for a
writer), reading and opening or updating the pull or merge request, reading
and posting comments, and reading commit statuses. Everything else gets 403
and is never forwarded.

The allowlist is derived from what the writer prompt asks agents to do (push, open/update PR or MR, read PR state, comment, read statuses). A harness that improvises other repo endpoints (contents, branches, labels, Forgejo pulls/{n}/comments, GitLab discussions) now gets 403; widening is one line per endpoint. The reader allowlist is defined and tested but unused: readers still get no token.

Verified with mise run verify on the pinned toolchain (all gates passed).

Ticket: https://vikunja.webgrip.dev/tasks/1748

🤖 Generated with Claude Code

The forge token proxy checked the escaped path for a literal "/../", a trailing "/.." and "%2e%2e" only, so mixed forms such as "/api/v1/repos/o/r/%2e./other/pulls" passed and were forwarded with the Run's real token. Every API subpath under the repository was allowed, so a writer harness could merge, delete, change settings, hooks, collaborators, branch protection or keys. The proxy now decodes each path segment and refuses any dot segment, encoded separator (%2f, %5c), double encoding, control character or empty segment. The only encoded separator accepted is GitLab's project ID, which must equal the Run's project path exactly. It then enforces a per-Role allowlist of methods and endpoints: smart-HTTP git fetch (and push for a writer), reading and opening or updating the pull or merge request, reading and posting comments, and reading commit statuses. Everything else gets 403 and is never forwarded. The allowlist is derived from what the writer prompt asks agents to do (push, open/update PR or MR, read PR state, comment, read statuses). A harness that improvises other repo endpoints (contents, branches, labels, Forgejo pulls/{n}/comments, GitLab discussions) now gets 403; widening is one line per endpoint. The reader allowlist is defined and tested but unused: readers still get no token. Verified with `mise run verify` on the pinned toolchain (all gates passed). Ticket: https://vikunja.webgrip.dev/tasks/1748 🤖 Generated with [Claude Code](https://claude.com/claude-code)
fix(ploeg): decode forge proxy paths and limit them to what the Role needs
Some checks failed
[Workflow] On Pull Request / warnings (pull_request) Has been cancelled
[Workflow] On Pull Request / release-policy (pull_request) Has been cancelled
[Workflow] On Pull Request / checks (pull_request) Has been cancelled
1be4a650c6
The forge token proxy checked the escaped path for a literal "/../", a
trailing "/.." and "%2e%2e" only, so mixed forms such as
"/api/v1/repos/o/r/%2e./other/pulls" passed and were forwarded with the
Run's real token. Every API subpath under the repository was allowed, so a
writer harness could merge, delete, change settings, hooks, collaborators,
branch protection or keys.

The proxy now decodes each path segment and refuses any dot segment,
encoded separator (%2f, %5c), double encoding, control character or empty
segment. The only encoded separator accepted is GitLab's project ID, which
must equal the Run's project path exactly. It then enforces a per-Role
allowlist of methods and endpoints: smart-HTTP git fetch (and push for a
writer), reading and opening or updating the pull or merge request, reading
and posting comments, and reading commit statuses. Everything else gets 403
and is never forwarded.

VIK-1748

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
ryangr0 merged commit 7ed6a2d9d0 into development 2026-10-03 11:54:07 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
webgrip/unfold!171
No description provided.