Skip to content

ci(npm-publish): the gate exempted every event except release - #81

Merged
LMPrado-DZ23 merged 1 commit into
release/v3.8.55from
fix/npm-publish-workflow-call
Sep 20, 2026
Merged

LMPrado-DZ23 merged 1 commit into
release/v3.8.55from
fix/npm-publish-workflow-call

Conversation

@LMPrado-DZ23

Copy link
Copy Markdown
Owner

From the security audit (B-M5). This matters here specifically: publishing v3.8.54 already started an npm publish of a package upstream owns, and it was cancelled by hand. #44 built this gate in response.

The hole

if [ "$EVENT_NAME" != "release" ] || [ "$ENABLED" = "true" ]

Everything that was not a GitHub Release passed straight through. The only brake on the workflow_call path was one if: in electron-release.yml.

That caller does carry the same condition:

if: ${{ vars.ENABLE_NPM_PUBLISH == 'true' && (github.event_name != 'workflow_dispatch' || inputs.publish_npm) }}

…so nothing is broken today. But the whole guarantee then rests on a single line in another file. A second caller, or one edit to that line, opens a provenance-signed npm publish with NPM_TOKEN in scope, and nothing in this workflow would object. The audit is right that a gate with its brake somewhere else is not a gate.

The change

Everything except workflow_dispatch now requires the variable. Dispatch stays exempt: it is already a deliberate human action, and it is the emergency route if the variable is ever wrong.

A subtlety worth writing down

Inside a reusable workflow, github.event_name is the caller's event — never the string "workflow_call". electron-release.yml triggers on push: tags, so a tag push arrives here as push, which the old != "release" test waved through. It is now recorded in the workflow's own comments, because the next person to read that condition will make the same assumption I did.

Today's behaviour is unchanged, because the caller already requires the variable. What changes is that it no longer depends on that.

Tests

  • pins the new condition and refuses the != "release" shape;
  • asserts the caller still carries its own guard — removing it would leave only the callee's, the mirror of the situation this fixes, and defence in depth means both.

Proven failable: restoring the old condition takes it from 5 pass to 1 fail.

Gate Result
npm-publish-release-gate 5 pass / 0 fail
check:workflows no new findings
YAML parses, 5 jobs intact ✓
prettier --check clean

Not fixed here: the two opencode-plugin jobs run npm install without --ignore-scripts before a npm publish --provenance, so a compromised transitive dependency's postinstall would get a valid SLSA attestation over poisoned bytes. The main omniroute job does it correctly. That is a separate change and is recorded in the audit document.

🤖 Generated with Claude Code

From the security audit (B-M5). The gate read:

  if [ "$EVENT_NAME" != "release" ] || [ "$ENABLED" = "true" ]

so everything that was not a GitHub Release passed straight through,
and the only brake on the `workflow_call` path was one `if:` in
electron-release.yml. That caller does carry the same
`vars.ENABLE_NPM_PUBLISH == 'true'` condition — but the whole guarantee
then rests on a single line in another file. A second caller, or one
edit to it, opens a provenance-signed `npm publish` with NPM_TOKEN in
scope, and nothing in this workflow would object.

Now everything except `workflow_dispatch` requires the variable.
Dispatch stays exempt: it is already a deliberate human action and it is
the emergency route if the variable is ever wrong.

Worth knowing, and now written down: inside a reusable workflow
`github.event_name` is the CALLER's event, never "workflow_call".
electron-release.yml triggers on `push: tags`, so a tag push arrives
here as `push` — which the old test waved through and this one does not.
Today's behaviour is unchanged, because the caller already requires the
variable; what changes is that it no longer depends on that.

Two tests added. The first pins the condition and refuses the
"not a release" shape. The second asserts the CALLER still carries its
own guard — removing it would leave only the callee's, which is the
mirror of the situation this fixes, and defence in depth means both.

Proven failable: restoring the old condition takes it to 1 fail.

  npm-publish-release-gate  5 pass / 0 fail
  check:workflows no new findings; YAML parses; prettier clean

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 20, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: cc393c81-c6a0-4a1f-977b-6832ae91fa27


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.

@LMPrado-DZ23
LMPrado-DZ23 merged commit 85b348f into release/v3.8.55 Sep 20, 2026
15 checks passed
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.

2 participants