sec: GitLab pipelines-must-succeed + Maintainer approval (AC-04) #696

Closed
opened 2026-08-28 09:24:23 +00:00 by PlasticDigits · 0 comments
PlasticDigits commented 2026-08-28 09:24:23 +00:00 (Migrated from gitlab.com)

Gate

P1 supply-chain. Internal audit INTERNAL_GROK46_1787908099 AC-04 / INF-28 (re-verified 2026-08-28 via glab api). Sister of FE-01 (Chain B): a red CI on a public repo with forks enabled can still merge to main.

This is a GitLab project-settings ticket plus a short in-repo note. Not a contract change.

Current codebase / project settings

glab api projects/PlasticDigits%2Fcl8y-dex-terraclassic (2026-08-28):

only_allow_merge_if_pipeline_succeeds=False
only_allow_merge_if_all_discussions_are_resolved=False
forking_access_level=enabled
visibility=public
approvals_before_merge=None

.gitlab-ci.yml already runs on MRs: gitleaks, path-filtered cargo-audit, test-contracts, test-indexer-lib, test-indexer-integration, test-frontend, test-frontend-build. gitleaks already runs on every MR (not path-filtered), so every MR can have a pipeline.

main is protected (Maintainers-only push/merge, no force-push) — keep that. Fork MRs can still run in this project (INF-06). CI caches are not branch-scoped (INF-05; out of scope here unless cheap).

Why the new implementation is needed

All CI security gates are advisory if a Maintainer can merge a red (or skipped) pipeline. That is the foothold for Chain B (poisoned frontend env, dependency, or indexer change). Enabling “pipelines must succeed” + one approval rule makes the existing jobs actually gate main.

Constraints / guardrails

  1. Do not require Playwright / LocalTerra e2e in this ticket (still local-only; separate TST-02).
  2. Do not turn off public visibility or forking in this ticket (product choice). Fork pipelines must keep using protected/masked variables only (already assumed).
  3. Break-glass: document how Maintainers merge when CI is down (GitLab “merge when pipeline succeeds” vs temporary setting flip). Record the owner. Do not add a permanent “skip CI” label that bypasses the gate.
  4. Empty / docs-only MRs: because gitleaks is always-on, a pipeline should exist. Confirm “skipped pipelines treated as successful” does not reopen a no-pipeline merge hole. If GitLab would block MRs with no jobs, keep the always-on gitleaks job.
  5. Self-approval: merge_requests_author_approval should stay off if you add an approval rule. Prefer 1 Maintainer approval from someone other than the author.
  6. Do not set approvals_before_merge via a random API experiment on production without a Maintainer present. Prefer GitLab UI + record the resulting JSON on this issue.
  7. In-repo: a short note in docs/security-model.md or docs/supply-chain-security.md that main requires a green pipeline. No emails in the issue or commit.

Relevant files

Path Role
.gitlab-ci.yml Confirm always-on gitleaks so every MR has a pipeline
docs/supply-chain-security.md Record the merge gate
docs/security-model.md One line under off-chain / deploy trust
GitLab Settings → Merge requests Pipelines must succeed; approval rules
GitLab Settings → Repository → Protected branches Unchanged (main)
  1. Maintainer: enable Pipelines must succeed on the project.
  2. Add an approval rule: 1 approval from role Maintainer, prevent author approval.
  3. Optionally enable all threads must be resolved (nice-to-have, not required to close).
  4. Paste glab api projects/PlasticDigits%2Fcl8y-dex-terraclassic snippet on this issue after the change (only_allow_merge_if_pipeline_succeeds=true).
  5. Docs: one paragraph that merge to main requires green CI + approval; break-glass is a temporary settings flip by two Maintainers.
  6. make verify-issue-<N> can only grep the docs + a recorded settings snippet in docs/ (do not bake a live API token into CI). Optional: a script that reads the public project API (no token) and asserts the boolean — project visibility is public so only_allow_merge_if_pipeline_succeeds may be visible; if the field is not public, skip the script and keep the paste.

Acceptance criteria

  • only_allow_merge_if_pipeline_succeeds is true (API or UI screenshot on this issue).
  • At least one Maintainer approval is required; author cannot satisfy it alone.
  • gitleaks (or another cheap job) still runs on every merge_request_event so docs-only MRs have a pipeline.
  • Break-glass written in docs/supply-chain-security.md (no secrets).
  • A docs-only MR cannot merge with a failed gitleaks job.
  • make verify-issue-<N> documents how an agent re-checks the setting (glab api).

Test plan — happy / bad path

Path Expect
MR with green test + security stages Merge allowed after 1 Maintainer approval
MR with failed test-frontend Merge button blocked
Docs-only MR gitleaks runs; green → merge after approval
Author tries to approve own MR Not counted
main direct push Still rejected (protected branch)

Test plan — attack / abuse vectors

Vector Expect
Fork MR with failing tests Cannot merge without green pipeline + Maintainer
Maintainer merges red pipeline Blocked by the new setting
Disable gitleaks on the MR via YAML in the same MR The pipeline that runs is the MR’s YAML — if they delete gitleaks, other path-filtered jobs may not run; require that at least one default-branch-protected rule still applies, or accept that a Maintainer review of .gitlab-ci.yml diffs is the control (state this in the docs)
Stolen Developer account Cannot merge to main (protected); cannot approve as Maintainer
CI cache poison (INF-05) Out of scope; note only

Verification criteria

  1. Comment on this issue with the post-change glab api JSON fields listed above.
  2. Open a throwaway MR that fails a cheap job (or use an existing red MR) and confirm merge is blocked.
  3. Docs paragraph present; make verify-issue-<N> lists the glab re-check command.
  4. Audit AC-04 / INF-28 closable when the setting is true.
## Gate **P1 supply-chain.** Internal audit `INTERNAL_GROK46_1787908099` **AC-04** / INF-28 (re-verified 2026-08-28 via `glab api`). Sister of FE-01 (Chain B): a red CI on a public repo with forks enabled can still merge to `main`. This is a **GitLab project-settings** ticket plus a short in-repo note. Not a contract change. ## Current codebase / project settings `glab api projects/PlasticDigits%2Fcl8y-dex-terraclassic` (2026-08-28): ``` only_allow_merge_if_pipeline_succeeds=False only_allow_merge_if_all_discussions_are_resolved=False forking_access_level=enabled visibility=public approvals_before_merge=None ``` `.gitlab-ci.yml` already runs on MRs: `gitleaks`, path-filtered `cargo-audit`, `test-contracts`, `test-indexer-lib`, `test-indexer-integration`, `test-frontend`, `test-frontend-build`. **gitleaks already runs on every MR** (not path-filtered), so every MR can have a pipeline. `main` is protected (Maintainers-only push/merge, no force-push) — keep that. Fork MRs can still run in this project (INF-06). CI caches are not branch-scoped (INF-05; out of scope here unless cheap). ## Why the new implementation is needed All CI security gates are advisory if a Maintainer can merge a red (or skipped) pipeline. That is the foothold for Chain B (poisoned frontend env, dependency, or indexer change). Enabling “pipelines must succeed” + one approval rule makes the existing jobs actually gate `main`. ## Constraints / guardrails 1. **Do not** require Playwright / LocalTerra e2e in this ticket (still local-only; separate TST-02). 2. **Do not** turn off public visibility or forking in this ticket (product choice). Fork pipelines must keep using **protected/masked** variables only (already assumed). 3. **Break-glass:** document how Maintainers merge when CI is down (GitLab “merge when pipeline succeeds” vs temporary setting flip). Record the owner. Do not add a permanent “skip CI” label that bypasses the gate. 4. **Empty / docs-only MRs:** because `gitleaks` is always-on, a pipeline should exist. Confirm “skipped pipelines treated as successful” does not reopen a no-pipeline merge hole. If GitLab would block MRs with no jobs, keep the always-on gitleaks job. 5. **Self-approval:** `merge_requests_author_approval` should stay off if you add an approval rule. Prefer **1 Maintainer approval** from someone other than the author. 6. **Do not** set `approvals_before_merge` via a random API experiment on production without a Maintainer present. Prefer GitLab UI + record the resulting JSON on this issue. 7. In-repo: a short note in `docs/security-model.md` or `docs/supply-chain-security.md` that `main` requires a green pipeline. No emails in the issue or commit. ## Relevant files | Path | Role | |------|------| | `.gitlab-ci.yml` | Confirm always-on `gitleaks` so every MR has a pipeline | | `docs/supply-chain-security.md` | Record the merge gate | | `docs/security-model.md` | One line under off-chain / deploy trust | | GitLab **Settings → Merge requests** | Pipelines must succeed; approval rules | | GitLab **Settings → Repository → Protected branches** | Unchanged (`main`) | ## Recommended direction 1. Maintainer: enable **Pipelines must succeed** on the project. 2. Add an approval rule: **1** approval from role **Maintainer**, prevent author approval. 3. Optionally enable **all threads must be resolved** (nice-to-have, not required to close). 4. Paste `glab api projects/PlasticDigits%2Fcl8y-dex-terraclassic` snippet on this issue after the change (`only_allow_merge_if_pipeline_succeeds=true`). 5. Docs: one paragraph that merge to `main` requires green CI + approval; break-glass is a temporary settings flip by two Maintainers. 6. `make verify-issue-<N>` can only grep the docs + a recorded settings snippet in `docs/` (do **not** bake a live API token into CI). Optional: a script that **reads** the public project API (no token) and asserts the boolean — project visibility is public so `only_allow_merge_if_pipeline_succeeds` may be visible; if the field is not public, skip the script and keep the paste. ## Acceptance criteria - [ ] `only_allow_merge_if_pipeline_succeeds` is `true` (API or UI screenshot on this issue). - [ ] At least one Maintainer approval is required; author cannot satisfy it alone. - [ ] `gitleaks` (or another cheap job) still runs on every `merge_request_event` so docs-only MRs have a pipeline. - [ ] Break-glass written in `docs/supply-chain-security.md` (no secrets). - [ ] A docs-only MR cannot merge with a **failed** gitleaks job. - [ ] `make verify-issue-<N>` documents how an agent re-checks the setting (`glab api`). ## Test plan — happy / bad path | Path | Expect | |------|--------| | MR with green `test` + `security` stages | Merge allowed after 1 Maintainer approval | | MR with failed `test-frontend` | Merge button blocked | | Docs-only MR | gitleaks runs; green → merge after approval | | Author tries to approve own MR | Not counted | | `main` direct push | Still rejected (protected branch) | ## Test plan — attack / abuse vectors | Vector | Expect | |--------|--------| | Fork MR with failing tests | Cannot merge without green pipeline + Maintainer | | Maintainer merges red pipeline | Blocked by the new setting | | Disable gitleaks on the MR via YAML in the same MR | The pipeline that runs is the MR’s YAML — if they delete gitleaks, other path-filtered jobs may not run; **require** that at least one default-branch-protected rule still applies, or accept that a Maintainer review of `.gitlab-ci.yml` diffs is the control (state this in the docs) | | Stolen Developer account | Cannot merge to `main` (protected); cannot approve as Maintainer | | CI cache poison (INF-05) | Out of scope; note only | ## Verification criteria 1. Comment on this issue with the post-change `glab api` JSON fields listed above. 2. Open a throwaway MR that fails a cheap job (or use an existing red MR) and confirm merge is blocked. 3. Docs paragraph present; `make verify-issue-<N>` lists the `glab` re-check command. 4. Audit AC-04 / INF-28 closable when the setting is true.
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-08-29 12:28:46 +00:00
Sign in to join this conversation.
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
code/cl8y-dex-terraclassic#696
No description provided.