Skip to content

fix(gitlab): harden API redirects and CSRF compatibility - #46

Open
jonschumaker wants to merge 3 commits into
unraid:mainfrom
jonschumaker:agent/gitlab-api-hardening
Open

fix(gitlab): harden API redirects and CSRF compatibility#46
jonschumaker wants to merge 3 commits into
unraid:mainfrom
jonschumaker:agent/gitlab-api-hardening

Conversation

@jonschumaker

@jonschumaker jonschumaker commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Summary

  • prevent the GitLab PRIVATE-TOKEN from reaching redirect targets, even when a user curl configuration enables redirect following
  • require a final 2xx status before API bodies or pagination headers can affect dashboard telemetry
  • enumerate the complete space/tab/newline-delimited GITLAB_PROJECTS value without pathname expansion and safely retain valid dot/underscore-prefixed GitLab paths
  • support the CSRF prevalidation state used by Unraid 6.12–7.2 as well as the retained-token state used by Unraid 7.3

This carries forward Eli Bosley's closed fork PR #3 with authorship preserved, plus the follow-up fixes and regression coverage found during validation.

Root cause

Dropping curl's location flag blocks the normal redirect path, but curl's fail mode still treats 3xx responses as successful and a user-level .curlrc can turn location following back on. That could both expose the custom GitLab token and turn a redirect body into false queue/stat data.

The endpoint's platform CSRF path also assumed that Unraid's preloader always retained $csrf_token. Unraid 6.12–7.2 validates and consumes the request token without creating that local variable; Unraid 7.3 retains it.

Behavior

  • curl is invoked with -q first, never follows redirects, and accepts only status codes in the 2xx class
  • redirect or API failures produce the existing unavailable telemetry sentinel
  • malformed telemetry-only project paths are ignored without shell expansion or fleet disruption; valid entries after embedded newlines are preserved
  • a platform-supplied CSRF variable must match; the legacy consumed-token state remains accepted only behind Unraid's platform prevalidation function

Validation

  • bash tests/run-linux-checks.sh
  • official GitLab Runner generated-config parse fallback
  • mocked HTTP 300–399 responses and every redirect-enabling curl option form
  • token-redaction, multi-project aggregation, mixed newline/tab input, real glob-match, path-shape, and API-unavailable regression cases
  • PHP CSRF tests for Unraid 6.12–7.2, Unraid 7.3, standalone execution, spoofed values, and non-scalar values
  • installed and validated the content-addressed dev package on Unraid 7.3.0
  • disposable Docker-executor validation completed with no leftover resources
  • real private GitLab jobs completed successfully while the farm autoscaled across three isolated DinD slots

Eli Bosley (elibosley) and others added 2 commits August 6, 2026 09:56
…ect paths

Two narrow hardening fixes on top of the GitLab provider work.

gitlab_api/gitlab_api_capture passed --location while supplying the API
token through `header = "PRIVATE-TOKEN: ..."`. curl strips only the
Authorization header on a cross-host redirect, so a 3xx from a
self-managed instance would resend the token to whatever host it named.
These are plain /api/v4 GETs against the configured base URL and have no
reason to follow a redirect off it, so drop -L.

GITLAB_PROJECTS was iterated as `for project in $GITLAB_PROJECTS`, which
is subject to pathname expansion as well as word splitting. A plausible
entry like `group/*` expands against the process CWD, so the advisory
queue/stats/public-visibility scans would query whatever directory names
happened to match. Split the list with `read -a` instead and emit only
well-formed namespace/project paths. The field is telemetry-only and does
not define runner scope, so a malformed entry is skipped rather than
failing validation and blocking a fleet.

gitlab_stats_refresh reads its project list on fd 3 because its
per-project status scan is itself a `while read` on stdin.
@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The change updates platform CSRF validation for legacy states and non-scalar values. It also centralizes GitLab API requests, rejects redirects, validates monitored projects, and updates queue/statistics processing and test coverage.

Changes

CSRF validation

Layer / File(s) Summary
CSRF validation and coverage
src/usr/local/emhttp/plugins/ci-runner-farm/include/exec.php, tests/exec-csrf.sh
Platform validation now permits an absent local token and validates supplied values. Tests cover platform, legacy, standalone, and non-scalar token cases.

GitLab provider hardening

Layer / File(s) Summary
GitLab API request handling
src/usr/local/emhttp/plugins/ci-runner-farm/include/providers/gitlab.sh, tests/provider-mocks.sh
API calls use shared validation, temporary response files, optional CA support, disabled redirects, and 2xx-only success handling. Mocks cover statuses, redirects, pagination, and aggregation.
Validated project processing
src/usr/local/emhttp/plugins/ci-runner-farm/include/providers/gitlab.sh, tests/gitlab-policy.sh
Project inputs are parsed literally and validated. Public-project, queue, and statistics loops consume validated paths. Tests cover valid, invalid, glob-like, relative, and empty inputs.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant GitLabProvider
  participant gitlab_api_request
  participant Curl
  GitLabProvider->>gitlab_api_request: Send method, path, body, and headers
  gitlab_api_request->>Curl: Execute validated request without redirects
  Curl-->>gitlab_api_request: Return status, headers, and body
  gitlab_api_request-->>GitLabProvider: Return 2xx response or failure
Loading

Possibly related PRs

Suggested reviewers: elibosley

Poem

I checked the token by moonlit light,
And kept unsafe project paths from sight.
GitLab replies now show their status clear,
Redirects stop at the proper frontier.
The rabbit tests hop, green and bright.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title uses the required conventional commit format and accurately describes the GitLab API and CSRF compatibility changes.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
✨ Simplify code
  • Create PR with simplified code

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/usr/local/emhttp/plugins/ci-runner-farm/include/providers/gitlab.sh`:
- Around line 360-368: Update gitlab_projects_list to split the complete
GITLAB_PROJECTS value on default-IFS whitespace without truncating at the first
newline or allowing pathname expansion, while preserving validation and output
behavior. Add a regression case in tests/gitlab-policy.sh covering multiple
newline-delimited project entries and verifying that each valid project is
emitted.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a5b41d23-7014-48ee-97de-d51c2732ebb9

📥 Commits

Reviewing files that changed from the base of the PR and between 78aad46 and 96ebb87.

📒 Files selected for processing (5)
  • src/usr/local/emhttp/plugins/ci-runner-farm/include/exec.php
  • src/usr/local/emhttp/plugins/ci-runner-farm/include/providers/gitlab.sh
  • tests/exec-csrf.sh
  • tests/gitlab-policy.sh
  • tests/provider-mocks.sh

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.

3 participants