Skip to content

[WP12] CI/CD supply-chain & gating: unpinned actions, auto-merge without in-repo build gate, dead branch guard #180

Closed as not planned

Description

@shihyuho

此內容由 AI 產生(specification-mapper 全庫審查 2026-07-16)。

Work package WP12 · findings: DEP-02, TEST-01, COR-02 · worst severity: medium

From the 2026-07-16 whole-codebase review (branch jakarta, modules mapper/ + starter/). Each finding below is CONFIRMED by independent adversarial verification. File paths and line numbers reflect the review snapshot and may drift — treat them as starting points, not exact coordinates.


DEP-02 — Third-party actions pinned to mutable tags — including madhead/semver-utils@latest — in a workflow that wields a repo-write App token and auto-merges its own PRs

  • Category / severity: dependencies / medium · effort S · confidence high
  • Location: .github/workflows/bump-spring.yml:42
  • Evidence: bump-spring.yml:42 uses madhead/semver-utils@latest (a moving tag dependabot cannot pin or update); the same workflow gives third-party peter-evans/create-pull-request@v8 (line 76) a GitHub App token with contents+pull-requests write and then auto-merges the resulting PR (line 91). Other third-party actions across workflows (shihyuho/go-jenkins-trigger@v2 receiving JENKINS_PAT, wagoid/commitlint-github-action@v6, mukunku/[email protected], WyriHaximus/github-action-next-semvers@v1) are all tag-pinned, not SHA-pinned.
  • Failure scenario: A compromised or force-retagged release of any of these actions executes immediately in this repo's CI (the @latest one on the very next daily run) with access to the App token or Jenkins PAT — enough to push a branch, open a bump PR carrying arbitrary changes, and let the existing auto-merge flow land it on the release branch.
  • Suggested fix: Pin all third-party actions to full commit SHAs (dependabot's github-actions lane keeps SHA pins updated), and at minimum replace @latest with a fixed release; keep first-party actions/* on major tags if preferred.
  • Verification: [CONFIRMED] Traced .github/workflows/bump-spring.yml directly. Line 42 uses madhead/semver-utils@latest — a mutable moving tag, unpinnable/unupdatable by dependabot. Line 70 mints an App token (job-level permissions contents:write + pull-requests:write, lines 51-53). Line 76 passes that token to third-party peter-evans/create-pull-request@v8 (a mutable major tag), and line 91 runs gh pr merge --squash --auto with the same App token, auto-merging the bot's own PR. So the described supply-chain surface exists exactly as claimed: a compromised or force-retagged release of any third-party action here runs in this repo's daily CI, the @latest one on the very next scheduled run, and the cr…

TEST-01 — Dependency-bump PRs are auto-merged while the only in-repo PR-triggered check is commitlint — build/test gating depends entirely on out-of-repo Jenkins branch protection

  • Category / severity: testing / medium · effort S · confidence low
  • Location: .github/workflows/bump-spring.yml:91
  • Evidence: bump-spring.yml:89-93 runs gh pr merge --squash --auto on every Spring bump PR. No GitHub Actions workflow compiles or tests the code (workflows are commitlint, label-issues, release-please, site-build, bump-*; commitlint.yml is the only pull_request-triggered one). Compile/test lives only in the Jenkins multibranch pipeline (Jenkinsfile), and nothing in the repo shows Jenkins is a required status check for auto-merge.
  • Failure scenario: If the Jenkins PR status is not a required check (or Jenkins is down/not discovering bot branches), a Spring Boot patch that breaks compilation or tests merges to the jakarta release branch automatically the day it is published, and release-please then proposes a release from a broken tree.
  • Suggested fix: Verify branch protection marks the Jenkins build as a required status check for jakarta; alternatively add a minimal mvn verify GitHub Actions workflow on pull_request so the gate is visible and enforced in-repo.
  • Verification: [CONFIRMED] Every in-repo claim traced and verified. bump-spring.yml:89-91 runs gh pr merge --squash --auto on each created Spring bump PR. Among the 6 workflows, only commitlint.yml triggers on pull_request open/synchronize and it merely lints commit messages/PR title (no build). site-build.yml's pull_request trigger is types: [closed] and only builds the Hugo site (npm ci + hugo), never the Java code. Compile/test exist solely in Jenkinsfile (make compile / make test / matrix). So no in-repo GitHub Actions check compiles or tests the code before auto-merge — accurate as described. The failure (broken bump auto-merging to jakarta) is genuinely conditional on out-of-repo branch protectio…

COR-02 — Jenkins failure notification is gated on BRANCH_NAME == 'main', which is never true on the active jakarta branch

  • Category / severity: correctness / medium · effort S · confidence high
  • Location: Jenkinsfile:157
  • Evidence: Jenkinsfile:157 wraps the slackSend failure alert in if (env.BRANCH_NAME == 'main' ...), but this Jenkinsfile lives on (and builds) the jakarta branch — every workflow in .github/workflows targets branches: [jakarta] and release-please releases from jakarta, so for builds of this line BRANCH_NAME is 'jakarta' (or a PR/bot branch) and the condition is unreachable.
  • Failure scenario: The Unit Tests or the Java 17/21 x Boot 3.0-3.4 Matrix Tests stage fails on the jakarta release line and nobody is notified; breakage sits unnoticed until the next manual look at Jenkins or until a release build fails.
  • Suggested fix: Change the guard to the branch this line actually releases from (e.g. env.BRANCH_NAME == 'jakarta') or compare against the multibranch project's primary branch instead of a hardcoded name.
  • Verification: [CONFIRMED] Traced Jenkinsfile:155-157 — the failure post block wraps slackSend in if (env.BRANCH_NAME == 'main' && ...). Verified the branch context: git branch --show-current is jakarta, and there is NO origin/main or origin/master remote branch (checked git branch -r; only origin/jakarta and jakarta-derived bot/release-please branches exist). All active GitHub workflows target jakarta: commitlint.yml:4 and release-please.yml:7 both use branches: [jakarta], and release-please releases from jakarta. Therefore any Jenkins build of this branch sees BRANCH_NAME='jakarta' (or a PR/bot branch), never 'main', so the failure Slack alert is dead code and test/matrix failures go …

Full report: see the codebase-review-2026-07-16 branch (linked on the parent tracking issue).

Activity

  1. added theissue type on Jul 16, 2026
  2. shihyuho commented on Jul 16, 2026

    @shihyuho
    MemberAuthor

    This was generated by AI during triage.

    Triage Notes - needs human

    Category: enhancement (bundles two CI/supply-chain hardening findings and one CI-config correctness bug)

    Summary: The CI/CD surface on jakarta has three confirmed weaknesses — third-party Actions pinned to mutable tags (including @latest) in a token-wielding auto-merge workflow, dependency-bump PRs auto-merged with no in-repo build/test gate, and a Jenkins failure-alert guard hardcoded to a branch (main) this line never builds — but every fix lands in CI/supply-chain infrastructure that cannot be verified by the in-repo JUnit suite.

    Current behavior

    • DEP-02: The Bump Spring workflow's compare-version job uses madhead/semver-utils@latest, a moving tag Dependabot cannot pin or update. The same workflow mints a GitHub App token (job permissions contents: write + pull-requests: write), hands it to third-party peter-evans/create-pull-request@v8, then runs gh pr merge --squash --auto on the bot's own PR with that token. Every third-party action across the workflows is pinned to a mutable tag, not a commit SHA — madhead/semver-utils@latest, peter-evans/create-pull-request@v8, shihyuho/go-jenkins-trigger@v2 (receives JENKINS_PAT), shihyuho/go-spring-version@v1, liskin/gh-workflow-keepalive@v1, wagoid/commitlint-github-action@v6, mukunku/[email protected], WyriHaximus/github-action-next-semvers@v1, peaceiris/actions-hugo@v3, peaceiris/actions-gh-pages@v4. A github-actions Dependabot lane is already configured (weekly).
    • TEST-01: The Bump Spring workflow auto-merges every Spring bump PR. Among all workflows, Commitlint is the only pull_request-triggered check and it only lints commit messages / PR title; Site Build's pull_request trigger is types: [closed] and builds only the Hugo site. No GitHub Actions workflow compiles or tests the Java code. Compile/test (make compile, make test, the Java 17/21 × Boot matrix) live solely in the Jenkins multibranch pipeline, and nothing in the repo shows the Jenkins status is a required check for auto-merge.
    • COR-02: In the Jenkinsfile post { failure { ... } } block, the slackSend alert is guarded by if (env.BRANCH_NAME == 'main' && ...). This Jenkinsfile lives on and builds jakarta; there is no origin/main / origin/master (confirmed — only jakarta and jakarta-derived bot/release branches exist), and every workflow targets branches: [jakarta]. So BRANCH_NAME is jakarta (or a PR/bot branch), never main, and the failure alert is unreachable dead code.

    Desired behavior

    • DEP-02: Third-party actions pinned to immutable full commit SHAs (with a comment recording the human-readable version), and @latest replaced with a fixed release SHA; Dependabot's github-actions lane keeps SHA pins current. First-party actions/* may stay on major tags.
    • TEST-01: Build/test gating on bump PRs is enforced and visible — either the Jenkins build is confirmed a required status check on the jakarta branch-protection rule, or a minimal mvn verify GitHub Actions workflow runs on pull_request so the gate is enforced in-repo before auto-merge lands.
    • COR-02: The Jenkins failure Slack alert fires for builds of the branch this line actually releases from (e.g. jakarta, or a comparison against the multibranch project's primary branch) instead of the hardcoded main.

    Key interfaces (named, no line numbers)

    • .github/workflows/bump-spring.yml — jobs compare-version (madhead/semver-utils@latest), bump-spring (app-token → peter-evans/create-pull-request@v8 → gh pr merge --squash --auto), keepalive.
    • .github/workflows/release-please.yml, .github/workflows/bump-snapshot.yml — shihyuho/go-jenkins-trigger@v2 (holds JENKINS_PAT), WyriHaximus/github-action-next-semvers@v1, mukunku/[email protected].
    • .github/workflows/commitlint.yml, .github/workflows/site-build.yml — the only pull_request-triggered workflows; neither compiles/tests Java.
    • .github/dependabot.yml — existing github-actions update lane.
    • Jenkinsfile — post { failure } block; BRANCH_NAME guard; stages Unit Tests, Matrix Tests.
    • Repository branch-protection settings for jakarta (out-of-repo).

    Acceptance criteria

    • DEP-02: every third-party action reference is a full 40-char commit SHA with a version comment; no @latest remains; actions/* first-party may remain on major tags per policy.
    • DEP-02: Dependabot github-actions lane still resolves and can propose SHA updates for the pinned actions.
    • TEST-01: bump PRs cannot auto-merge without a build/test gate — either the jakarta branch-protection rule lists the Jenkins build as a required status check (evidence attached), or an in-repo pull_request workflow runs mvn verify and is required.
    • COR-02: a Jenkins build failure on the jakarta line triggers the slackSend alert (guard evaluates true for the active release branch); the alert stays suppressed for interrupted builds that never captured commit metadata.
    • No change to the actions' resolved versions or to release/bump behavior beyond the pin/gate/guard fixes.

    Out of scope

    • Migrating away from Jenkins, redesigning the release/auto-merge flow, or changing which branch is the release line.
    • Broadening the Spring/Java test matrix or altering make targets.

    Needs human because:

    • Supply-chain / CI sign-off (DEP-02): SHA-pinning changes what every workflow executes and how the App token and JENKINS_PAT are exposed; it needs an owner's supply-chain sign-off, plus a policy call on whether first-party actions/* are also pinned. There is no in-repo JUnit surface to verify workflow behavior — an agent self-verifying with mvn test cannot confirm the workflows still run.
    • CI gating policy + external access (TEST-01): The core question — is the Jenkins build a required status check on jakarta? — requires inspecting repository branch-protection settings the agent cannot reach. The alternative (adding a duplicate mvn verify GitHub Actions gate) is a CI-strategy decision (duplicate the Jenkins build in Actions?) with no in-repo test to prove it gates auto-merge. Finding confidence was low.
    • Environment-only verification (COR-02): The fix is a one-line guard change, but confirming the Slack alert actually fires requires a real Jenkins build of the branch; the JUnit suite cannot exercise the Jenkinsfile post block. A human should also confirm the intended target (hardcode jakarta vs. compare against the multibranch primary branch).
  3. shihyuho commented on Jul 20, 2026

    @shihyuho
    MemberAuthor

    Superseded by #197 in the execution map #204, which carries the same findings (DEP-02, TEST-01, COR-02). This 2026-07-16 static plan is being retired in favour of that map.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    github_actionsPull requests that update GitHub Actions codestatus: on-holdWe can't start working on this issue yettype: enhancementNew feature or request

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions