Skip to content

feat(gitlab): a hub inside the group of its targets (security.writer_on_hub: guard) - #27

Merged
AlexeyShalaev merged 1 commit into
masterfrom
feat/writer-on-hub
Oct 9, 2026
Merged

AlexeyShalaev merged 1 commit into
masterfrom
feat/writer-on-hub

Conversation

@AlexeyShalaev

Copy link
Copy Markdown
Member

Closes #24.

A GitLab hub can now live in the group that holds its targets. The writer, a member of that group, then reaches the hub, and GitLab cannot lower an inherited role on one project. With security.writer_on_hub: guard in hub.yml, touchmark verifies that the writer cannot get content onto the hub's default branch, instead of requiring that it cannot see the hub. The default, refuse, keeps today's behaviour, messages included.

One addition to the issue: the CI file comes from the default branch

A merge request pipeline runs the CI file of its source branch. A writer that pushes to a maintainer's merge request could rewrite .gitlab-ci.yml to drop the plan that refuses its push, and "Pipelines must succeed" would pass. So the guard also requires the hub's CI/CD configuration file to be <file>@<hub path>:<default branch> (or <file>@<hub path>).

Checked live on CE 18.11.12:

  • with the pin, a writer's push of its own CI file ran main's jobs, and the pipeline failed;
  • without the pin, it ran the writer's file, and the pipeline passed;
  • a [skip ci] push left the merge blocked;
  • Developers and Reporters see ci_config_path and the merge checks;
  • protected tags are shown to Maintainers only (403 to the writer), so doctor --hub-token grades them.

What changes

  • Config. security.writer_on_hub: refuse (default) or guard, in the schema and the reference.

  • doctor. Under guard, a writer that sees the hub gets hub-guard instead of hub-hidden, from the new optional platform.HubGuard (GitLab):

    • fail:
      • the writer is Maintainer or above;
      • the default branch is unprotected;
      • a matching rule lets the writer push or merge (most permissive rule, wildcards, user allows);
      • the CI file is not pinned;
      • merges do not wait for a successful pipeline, or count a skipped one;
    • warn: other protected branches, or protected tags, the writer may push to or create;
    • unknown: a group allow whose members the writer cannot read.

    Other drivers report unknown. doctor --hub-token grades the same settings with a maintainer's token.

  • plan. In a GitLab hub merge request pipeline whose default branch's hub.yml says guard, plan exits 2 when:

    • the merge request was opened by the writer or one of its known_authors;
    • either of them pushed to its source branch since the branch was created;
    • plan cannot tell (fail closed).

    The pushes come from the source project's push events (MergeRequestPushes, the new optional platform.MergeRequestAuditor), which name who pushed, also after someone else re-runs the pipeline. The setting, writer and known authors come from the default branch, so the merge request cannot turn the guard off.

  • setup gitlab. Under guard:

    • a hub inside the group of targets is accepted;
    • writer-guard replaces writer-hidden (fails for a Maintainer writer);
    • merge-checks turns on Pipelines must succeed and off skipped pipelines;
    • ci-config pins the CI file, keeping a custom file name;
    • the final check grades hub-guard.

Tests

  • Unit:
    • the GitLab driver against the fake, one test per condition: wildcard rule, user allow, group allow, Developers merging, unprotected default, protected tag, unreadable tags, settings;
    • the push events (before and after the branch's creation, an unseen fork);
    • doctor's aggregation and the hub-token grading;
    • plan's guard (author, pusher, unknown pushes, the merge request's own hub.yml turning it off, the default branch turning it off, no auditor, an anonymous reader);
    • setup in guard mode, and PinnedCIConfig.
  • Live: TestWriterOnHub (e2e GitLab).
    • doctor gives hub-guard ok.
    • The person's merge request plans clean.
    • The writer's push of its own CI file to it is caught by main's plan.
    • A merge request the writer opens is caught.
    • Unpinning the CI file fails hub-guard.

Docs

  • the security model ("A writer on the hub");
  • the agents page;
  • getting started on GitLab;
  • the setup and doctor guides;
  • the threat model (a new row, and T3);
  • e2e.

…on_hub: guard)

On GitLab a member of a group inherits its projects, so a hub kept in the
group that holds its targets is reached by the writer. With
security.writer_on_hub: guard, touchmark verifies that the writer cannot
get content onto the hub's default branch instead of requiring that it
cannot see the hub; refuse, the default, keeps today's behaviour.

doctor grades hub-guard instead of hub-hidden for a writer that sees the
hub (platform.HubGuard, GitLab): the writer below Maintainer, the default
branch protected with no push or merge for it under the most permissive
matching rule, merges that wait for a pipeline that succeeded and not a
skipped one, and the CI file read from the default branch
(ci_config_path <file>@<hub path>:<default branch>). Without that pin a
writer pushing to a maintainer's merge request rewrites the CI file to
drop the plan (checked live on CE 18.11). Other protected branches and
tags the writer may use warn; doctor --hub-token grades the settings with
a maintainer's token. Other drivers report hub-guard unknown.

plan in a GitLab hub merge request pipeline exits 2 when the writer or a
known author opened the merge request or pushed to its source branch
since the branch was created, read from the push events, which name the
pusher (platform.MergeRequestAuditor), and when it cannot tell. The
setting, the writer and the known authors come from the default branch's
hub.yml.

setup gitlab accepts a hub inside the group of targets under guard, checks
the writer's role (writer-guard), and sets the merge checks and the CI
file pin. TestWriterOnHub runs it live. Closes #24.
@AlexeyShalaev
AlexeyShalaev merged commit 2e4805f into master Oct 9, 2026
22 checks passed
@AlexeyShalaev
AlexeyShalaev deleted the feat/writer-on-hub branch October 9, 2026 15:46
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.

[Feature]: a GitLab hub inside the targets' group (security.writer_on_hub: guard)

1 participant