From bdf58e07d1f2b253da86ce4951343c3f0be0afc5 Mon Sep 17 00:00:00 2001 From: donpetry-bot <281750570+donpetry-bot@users.noreply.github.com> Date: Wed, 22 Jul 2026 16:13:36 +0000 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20implement=20issue=20#875=20?= =?UTF-8?q?=E2=80=94=20[#850]=20templates=20drop=20the=20S7635=20NOSONAR?= =?UTF-8?q?=20marker=20on=20secrets:=20inherit=20(dev-lead/add-to-project/?= =?UTF-8?q?auto-rebase/dependabot-automerge)=20=E2=80=94=20restore=20+=20g?= =?UTF-8?q?uard?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- standards/ci-standards.md | 18 ++-- standards/workflows/auto-rebase.yml | 2 +- standards/workflows/dependabot-automerge.yml | 2 +- standards/workflows/dev-lead.yml | 2 +- .../s7635-secrets-inherit.bats | 82 +++++++++++++++++++ 5 files changed, 97 insertions(+), 9 deletions(-) create mode 100644 test/scripts/standards-templates/s7635-secrets-inherit.bats diff --git a/standards/ci-standards.md b/standards/ci-standards.md index a58ae81e6..666819d95 100644 --- a/standards/ci-standards.md +++ b/standards/ci-standards.md @@ -732,12 +732,18 @@ first time it is **added** to a SonarCloud-gated repo. (The legacy per-file also works and is accepted during transition, but the inline marker is canonical.) Caller-stub templates that use `secrets: inherit` and therefore carry the S7635 -marker: `initiative-planner.yml`, `idea-triage.yml`, `idea-enhancer.yml`. -**Pending:** `dev-lead.yml`, `auto-rebase.yml`, `dependabot-automerge.yml`, and -`pr-review-mention.yml` also use `secrets: inherit` but do not yet carry the -marker; because they are already deployed fleet-wide (so re-syncing them fans out -broadly) the marker is being added to them under a separate rollout — until then a -**new** adoption of one of those four needs the legacy per-file S7635 exemption. +marker: `initiative-planner.yml`, `idea-triage.yml`, `idea-enhancer.yml`, +`persona-mention.yml`, `dev-lead.yml`, `auto-rebase.yml`, +`dependabot-automerge.yml`. Every template with a `secrets: inherit` line now +carries the marker; the +[`s7635-secrets-inherit.bats`](https://github.com/petry-projects/.github/blob/main/test/scripts/standards-templates/s7635-secrets-inherit.bats) +template guard fails CI if any such line drops it (mirrors the S7637 v-form pin +guard). + +Stubs that pass secrets **explicitly** (least privilege — no `secrets: inherit` +line, e.g. `add-to-project.yml`, `pr-review-mention.yml`) never trip S7635 and +carry **no** marker. Do not add one: the marker suppresses S7635 on a +`secrets: inherit` line, and there is none to suppress. ### 4. Secret Scanning (`ci.yml` — gitleaks job) diff --git a/standards/workflows/auto-rebase.yml b/standards/workflows/auto-rebase.yml index 78eb4dea2..ec4140e23 100644 --- a/standards/workflows/auto-rebase.yml +++ b/standards/workflows/auto-rebase.yml @@ -51,4 +51,4 @@ jobs: contents: write # update-branch via GITHUB_TOKEN (may touch .github/workflows/) pull-requests: write # post comments on PRs uses: petry-projects/.github/.github/workflows/auto-rebase-reusable.yml@auto-rebase/v2-stable # NOSONAR(githubactions:S7637) first-party channel ref - secrets: inherit + secrets: inherit # NOSONAR(githubactions:S7635) first-party trusted reusable diff --git a/standards/workflows/dependabot-automerge.yml b/standards/workflows/dependabot-automerge.yml index 0a19abc73..eddb767ed 100644 --- a/standards/workflows/dependabot-automerge.yml +++ b/standards/workflows/dependabot-automerge.yml @@ -36,4 +36,4 @@ jobs: contents: read pull-requests: read uses: petry-projects/.github/.github/workflows/dependabot-automerge-reusable.yml@dependabot-automerge/v2-stable # NOSONAR(githubactions:S7637) first-party channel ref - secrets: inherit + secrets: inherit # NOSONAR(githubactions:S7635) first-party trusted reusable diff --git a/standards/workflows/dev-lead.yml b/standards/workflows/dev-lead.yml index ec2c71f64..fbceadb33 100644 --- a/standards/workflows/dev-lead.yml +++ b/standards/workflows/dev-lead.yml @@ -64,7 +64,7 @@ jobs: uses: petry-projects/.github-private/.github/workflows/dev-lead-reusable.yml@dev-lead/v1-stable # NOSONAR(githubactions:S7637) first-party channel ref with: agent_ref: dev-lead/v1-stable - secrets: inherit + secrets: inherit # NOSONAR(githubactions:S7635) first-party trusted reusable permissions: contents: write pull-requests: write diff --git a/test/scripts/standards-templates/s7635-secrets-inherit.bats b/test/scripts/standards-templates/s7635-secrets-inherit.bats new file mode 100644 index 000000000..801a74b04 --- /dev/null +++ b/test/scripts/standards-templates/s7635-secrets-inherit.bats @@ -0,0 +1,82 @@ +#!/usr/bin/env bats +# Template-guard for the S7635 NOSONAR marker on caller-stub templates under +# standards/workflows/ (#875, mirrors the #860 v-form/S7637 pin guard). +# +# A caller stub that hands the reusable every org secret via `secrets: inherit` +# trips SonarCloud rule `githubactions:S7635` ("only pass required secrets"). +# The reusable is first-party and fully trusted, so the stub carries an inline +# secrets: inherit # NOSONAR(githubactions:S7635) +# marker that suppresses S7635 on exactly that line and travels with the stub to +# every consumer repo. Dropping the marker regresses any consumer that copied +# the stub (re-triggers S7635 in their SonarCloud run). This guard fails in CI +# the moment a template with `secrets: inherit` loses the marker. +# +# Stubs that pass secrets EXPLICITLY (least privilege, no `secrets: inherit` +# line — e.g. add-to-project.yml, pr-review-mention.yml) do not trip S7635 and +# are correctly ignored: the guard only inspects real `secrets: inherit` lines. + +REPO_ROOT="$(cd -- "${BATS_TEST_DIRNAME}/../../.." && pwd)" +WF_DIR="${REPO_ROOT}/standards/workflows" + +# The exact NOSONAR token every marked line must carry (grep -F, literal). +MARKER='NOSONAR(githubactions:S7635)' + +# Emit `secrets: inherit` lines that are REAL YAML (indented key), never a `#` +# comment mention. Anchors `secrets:` to the start-of-line-after-indent so a +# prose line like `# ... secrets: inherit ...` is not matched. +secrets_inherit_lines() { + grep -nE '^[[:space:]]*secrets:[[:space:]]+inherit([[:space:]]|$)' "$1" || true +} + +@test "every template with 'secrets: inherit' carries the S7635 marker" { + local violations=() + local f name lines line + for f in "${WF_DIR}"/*.yml; do + name="$(basename "$f")" + lines="$(secrets_inherit_lines "$f")" + [ -n "$lines" ] || continue + while IFS= read -r line; do + if ! grep -qF "$MARKER" <<<"$line"; then + violations+=("${name}: 'secrets: inherit' line lacks ${MARKER}") + fi + done <<<"$lines" + done + if [ "${#violations[@]}" -ne 0 ]; then + printf 'missing S7635 marker -> %s\n' "${violations[@]}" + return 1 + fi +} + +@test "every S7635 marker uses the canonical inline shape" { + # Two spaces before the '#', then the exact token. Trailing prose is free-form + # (e.g. 'first-party trusted reusable' vs 'org secrets are scoped ...'), but + # the marker itself must be byte-consistent so SonarCloud honours it. + local violations=() + local f name lines line + for f in "${WF_DIR}"/*.yml; do + name="$(basename "$f")" + lines="$(secrets_inherit_lines "$f")" + [ -n "$lines" ] || continue + while IFS= read -r line; do + grep -qF "$MARKER" <<<"$line" || continue + if ! grep -qE 'secrets: inherit # NOSONAR\(githubactions:S7635\) ' <<<"$line"; then + violations+=("${name}: marker is not the canonical 'secrets: inherit # NOSONAR(githubactions:S7635) ' shape") + fi + done <<<"$lines" + done + if [ "${#violations[@]}" -ne 0 ]; then + printf 'bad marker shape -> %s\n' "${violations[@]}" + return 1 + fi +} + +@test "the guard actually inspects at least one 'secrets: inherit' stub" { + # Positive control: without this, a broken matcher that finds nothing would let + # the first test pass vacuously (its `[ -n "$lines" ] || continue` skips every + # file). At least one template uses `secrets: inherit` by design. + local f found=0 + for f in "${WF_DIR}"/*.yml; do + [ -n "$(secrets_inherit_lines "$f")" ] && found=1 && break + done + [ "$found" -eq 1 ] || { echo "no template with a real 'secrets: inherit' line found"; return 1; } +} From bce3dd9032cda712dd719d910f882dbb3bfb12cb Mon Sep 17 00:00:00 2001 From: donpetry-bot <281750570+donpetry-bot@users.noreply.github.com> Date: Wed, 22 Jul 2026 16:28:50 +0000 Subject: [PATCH 2/2] fix(reviews): address review comments [skip ci-relay] --- standards/ci-standards.md | 3 +-- .../s7635-secrets-inherit.bats | 18 +++++++++++------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/standards/ci-standards.md b/standards/ci-standards.md index 666819d95..b20ccecb7 100644 --- a/standards/ci-standards.md +++ b/standards/ci-standards.md @@ -742,8 +742,7 @@ guard). Stubs that pass secrets **explicitly** (least privilege — no `secrets: inherit` line, e.g. `add-to-project.yml`, `pr-review-mention.yml`) never trip S7635 and -carry **no** marker. Do not add one: the marker suppresses S7635 on a -`secrets: inherit` line, and there is none to suppress. +carry **no** marker. Do not add one, as the marker is only necessary for `secrets: inherit` lines, which these stubs lack. ### 4. Secret Scanning (`ci.yml` — gitleaks job) diff --git a/test/scripts/standards-templates/s7635-secrets-inherit.bats b/test/scripts/standards-templates/s7635-secrets-inherit.bats index 801a74b04..23a291df8 100644 --- a/test/scripts/standards-templates/s7635-secrets-inherit.bats +++ b/test/scripts/standards-templates/s7635-secrets-inherit.bats @@ -30,14 +30,16 @@ secrets_inherit_lines() { @test "every template with 'secrets: inherit' carries the S7635 marker" { local violations=() - local f name lines line + local f name lines line lineno content for f in "${WF_DIR}"/*.yml; do name="$(basename "$f")" lines="$(secrets_inherit_lines "$f")" [ -n "$lines" ] || continue while IFS= read -r line; do - if ! grep -qF "$MARKER" <<<"$line"; then - violations+=("${name}: 'secrets: inherit' line lacks ${MARKER}") + lineno="${line%%:*}" + content="${line#*:}" + if ! grep -qF "$MARKER" <<<"$content"; then + violations+=("${name}:${lineno}: 'secrets: inherit' line lacks ${MARKER}") fi done <<<"$lines" done @@ -52,15 +54,17 @@ secrets_inherit_lines() { # (e.g. 'first-party trusted reusable' vs 'org secrets are scoped ...'), but # the marker itself must be byte-consistent so SonarCloud honours it. local violations=() - local f name lines line + local f name lines line lineno content for f in "${WF_DIR}"/*.yml; do name="$(basename "$f")" lines="$(secrets_inherit_lines "$f")" [ -n "$lines" ] || continue while IFS= read -r line; do - grep -qF "$MARKER" <<<"$line" || continue - if ! grep -qE 'secrets: inherit # NOSONAR\(githubactions:S7635\) ' <<<"$line"; then - violations+=("${name}: marker is not the canonical 'secrets: inherit # NOSONAR(githubactions:S7635) ' shape") + lineno="${line%%:*}" + content="${line#*:}" + grep -qF "$MARKER" <<<"$content" || continue + if ! grep -qE 'secrets: inherit # NOSONAR\(githubactions:S7635\) ' <<<"$content"; then + violations+=("${name}:${lineno}: marker is not the canonical 'secrets: inherit # NOSONAR(githubactions:S7635) ' shape") fi done <<<"$lines" done