ci: add rainix-npm-blacklist reusable workflow#230
Conversation
Upstream the npm-package blacklist security gate (scan the installed dependency tree for known-malicious packages) into a rainix reusable, so consumers stop copy-pasting the job inline. The reusable owns the boilerplate — the shared nix + cachix preamble, running the caller's `install-command` once, then scanning every listed directory in a single call to the github-chore `npm-blacklist` action via its `working-directories` input. The expensive dependency install runs once and feeds every directory scanned, rather than re-installing per directory. Inputs: `install-command` (required), `working-directories` (whitespace list, default `.`), `cachix-name`. Secret: `CACHIX_AUTH_TOKEN` (optional). Adds a self-test caller that runs the reusable against the subgraph npm project in test/fixture, mirroring how the composites are self-tested. Closes #229 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
📝 WalkthroughWalkthroughAdds a new Changesnpm-blacklist Reusable Workflow
Action Description Text Reformats
Sequence Diagram(s)sequenceDiagram
participant Caller as npm-blacklist-self-test
participant Reusable as rainix-npm-blacklist
participant NixCachix as nix-cachix-setup
participant BlacklistAction as github-chore/npm-blacklist
Caller->>Reusable: workflow_call(install-command, working-directories, CACHIX_AUTH_TOKEN)
Reusable->>NixCachix: setup Nix + Cachix environment
NixCachix-->>Reusable: environment ready
Reusable->>Reusable: run install-command (cd test/fixture/subgraph && npm ci)
Reusable->>BlacklistAction: scan(working-directories=test/fixture/subgraph)
BlacklistAction-->>Reusable: blacklist scan complete
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
yamlfmt reflows the `>-` folded-scalar description bodies in the new rainix-npm-blacklist reusable and in the composite actions onto single lines, matching the `yamlfmt` pre-commit hook that `nix flake check` runs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Pass working-directory (singular) to the composite action — the action does not accept working-directories (plural), which caused a warning and a fallback to '.' where no package.json exists. Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
.github/workflows/npm-blacklist-self-test.yaml (1)
12-19: ⚖️ Poor tradeoffConsider testing multi-directory scanning.
The self-test only validates a single directory (
test/fixture/subgraph), but the reusable workflow supports scanning multiple directories via a whitespace-separated list. While single-directory is the common case, adding a test with multiple directories would provide more complete coverage of the workflow's capabilities.Example:
working-directories: >- test/fixture/subgraph test/fixture/another-packageThis is optional since the multi-directory iteration logic resides in the
npm-blacklistaction itself, not in this thin wrapper workflow.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/npm-blacklist-self-test.yaml around lines 12 - 19, The npm-blacklist-self-test workflow currently only tests a single directory in the working-directories field, but the reusable workflow supports whitespace-separated multiple directories. Add an additional test job (or modify the existing npm-blacklist job) to validate multi-directory scanning by specifying multiple directories in the working-directories field, such as test/fixture/subgraph and another test fixture directory, to ensure the workflow properly handles the multi-directory iteration capability.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/rainix-npm-blacklist.yaml:
- Around line 51-54: The `install-command` input is being directly interpolated
into the `run` step without any validation, creating a command injection
vulnerability. Restrict the `install-command` input to a predefined set of safe
commands by adding a `choices` constraint to the input definition in the
`inputs` section of the workflow, or alternatively implement validation logic
before the `run` step that checks the input against an allowlist of permitted
commands and fails the workflow if an unexpected value is provided. This ensures
only trusted install commands can be executed regardless of who modifies calling
workflows.
---
Nitpick comments:
In @.github/workflows/npm-blacklist-self-test.yaml:
- Around line 12-19: The npm-blacklist-self-test workflow currently only tests a
single directory in the working-directories field, but the reusable workflow
supports whitespace-separated multiple directories. Add an additional test job
(or modify the existing npm-blacklist job) to validate multi-directory scanning
by specifying multiple directories in the working-directories field, such as
test/fixture/subgraph and another test fixture directory, to ensure the workflow
properly handles the multi-directory iteration capability.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 966b41a0-7f26-4cf1-98a5-22a54df312df
📒 Files selected for processing (7)
.github/actions/cache/action.yml.github/actions/checkout/action.yml.github/actions/gh-release/action.yml.github/actions/nix-cachix-setup/action.yml.github/actions/rust-cache/action.yml.github/workflows/npm-blacklist-self-test.yaml.github/workflows/rainix-npm-blacklist.yaml
| # Install the dependency tree once; the blacklist checker inspects it but | ||
| # never installs, so a single install feeds every directory scanned below. | ||
| - name: Install npm dependencies | ||
| run: ${{ inputs.install-command }} |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find all workflows that call rainix-npm-blacklist
rg -t yaml -A 5 'uses:.*rainix-npm-blacklist' --glob '.github/workflows/*'Repository: rainlanguage/rainix
Length of output: 212
🏁 Script executed:
#!/bin/bash
# First, verify the file exists
ls -la .github/workflows/rainix-npm-blacklist.yaml
# Check lines 51-54 to see the actual code
echo "=== Lines 51-54 ==="
sed -n '51,54p' .github/workflows/rainix-npm-blacklist.yaml
# View more context around those lines
echo "=== Context (40-60) ==="
sed -n '40,60p' .github/workflows/rainix-npm-blacklist.yaml
# Check if there's input validation defined
echo "=== Looking for inputs definition ==="
grep -n "inputs:" .github/workflows/rainix-npm-blacklist.yaml -A 20Repository: rainlanguage/rainix
Length of output: 2693
🏁 Script executed:
#!/bin/bash
# Search for workflow calls to rainix-npm-blacklist without file type restriction
# Try different search patterns
echo "=== Searching for uses: rainix-npm-blacklist ==="
rg 'rainix-npm-blacklist' .github/workflows/ -A 3
echo "=== Searching for any references to this workflow ==="
rg 'rainix-npm-blacklist' . --type yaml 2>/dev/null | head -20
echo "=== List all yaml files in .github/workflows ==="
find .github/workflows -name "*.yaml" -o -name "*.yml"Repository: rainlanguage/rainix
Length of output: 2025
Command injection risk via template expansion in run step.
The install-command input is expanded directly into the run step without validation. While workflow_call limits callers to workflows within the same repository, an attacker with repository write access could modify calling workflows to inject arbitrary shell commands through this input.
Since the input is defined as a required string with no validation constraints, consider restricting it to a predefined set of safe commands or validating against a pattern. The self-test caller currently uses a hardcoded trusted input, but this pattern should not be assumed for future callers.
🧰 Tools
🪛 zizmor (1.25.2)
[error] 54-54: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/rainix-npm-blacklist.yaml around lines 51 - 54, The
`install-command` input is being directly interpolated into the `run` step
without any validation, creating a command injection vulnerability. Restrict the
`install-command` input to a predefined set of safe commands by adding a
`choices` constraint to the input definition in the `inputs` section of the
workflow, or alternatively implement validation logic before the `run` step that
checks the input against an allowlist of permitted commands and fails the
workflow if an unexpected value is provided. This ensures only trusted install
commands can be executed regardless of who modifies calling workflows.
Source: Linters/SAST tools
What
Adds
rainix-npm-blacklist.yaml, aworkflow_callreusable that owns the npm-package blacklist security gate (scan the installed dependency tree for known-malicious packages). Consumers replace their inlinenpm-blacklistjob with a short caller.Inputs
install-command(string, required) — how to install the dependency tree before the scan, e.g.nix develop .#wasm-shell -c npm install --no-check. The checker runsnpm ls --all, so this must populatenode_modulesfor every scanned directory.working-directories(string, optional, default.) — whitespace-separated list of NPM project dirs to scan, e.g. a YAML multiline of. packages/raindex packages/ui-components.cachix-name(string, optional, defaultrainlanguage).Secret
CACHIX_AUTH_TOKEN(optional) — for the nix-store Cachix cache; empty degrades to anonymous pull.The job
checkout → nix-quick-install → cachix → cache-nix-action (all via the shared
nix-cachix-setupcomposite, so the pinned third-party SHAs stay single-sourced) → runinstall-command→ run the github-chorenpm-blacklistaction once overworking-directories.Design choice: install once, scan many
The inline job in raindex #2739 installs once, then calls the composite action 3× (one per
working-directory) — cheap, because each extra call is just annpm lsin another dir. A naive matrix over directories would instead re-run the expensiveinstall-command(wasm build + npm install, minutes) per directory.A reusable workflow can't loop a composite
uses:over a dynamic list in YAML, so to preserve install-once I taught the action itself to take a list. The companion PR rainlanguage/github-chore#8 adds aworking-directoriesinput to thenpm-blacklistaction that loopschecker.shover each dir (backward compatible — the singularworking-directorystill works). This reusable installs once and calls the action once with the full list. The directory iteration lives in the action, which is its right home; this workflow stays a thin install-once wrapper, and there is no per-directory re-install cost.Dependency / bidirectional bootstrap: this reusable references
rainlanguage/github-chore/.github/actions/npm-blacklist@main, whoseworking-directoriesinput only exists once github-chore#8 is onmain. The self-test here (and any consumer) is therefore red until github-chore#8 merges — the same one-time@mainbootstrap pattern already documented for the rainix composites. Merge github-chore#8 first.Self-test
Adds
npm-blacklist-self-test.yaml, a caller that runs the reusable end to end against the real npm project intest/fixture/subgraph(npm cithen scan), mirroring how the composites are self-tested againsttest/fixtureintest.yml. The fixture only depends on@graphprotocol/graph-ts, which is not blacklisted, so the scan is green.Validation
actionlintis clean on both new files (actionlint .github/workflows/rainix-npm-blacklist.yaml .github/workflows/npm-blacklist-self-test.yaml→ exit 0; the only repo-wide actionlint findings are pre-existing, inrainix-autopublish.yaml/rainix-sol-test.yaml).First consumer
raindex #2739 is the first consumer: once this reusable and github-chore#8 are on
main, that PR's inlinenpm-blacklistjob (in bothvercel-prod.yamlandvercel-preview.yaml) is replaced by auses:of this reusable. That wiring is a follow-up — not part of this PR.Closes #229
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Documentation