Conversation
Ports the reusable workflows from n8n-io/scan-community-node-action into a single composite action. One scanner runs per invocation, chosen with the `scanner` input: GuardDog, Semgrep, OpenSSF Scorecard, OSV-Scanner or Gitleaks. The example workflow runs them in a matrix. With `package` set, the npm tarball is downloaded and scanned and the findings go to the step summary only. Without it, the workspace is scanned and the SARIF report is uploaded to GitHub code scanning. Inputs are validated before they are spliced into command lines, all third-party actions are pinned to SHAs, and the reports live in .scan-community-node/ inside the workspace because the Scorecard and OSV-Scanner actions run in Docker and only see that directory.
There was a problem hiding this comment.
All reported issues were addressed across 5 files
Architecture diagram
sequenceDiagram
participant W as Workflow (ci-security-scan.yml)
participant MC as Main Composite Action
participant P as Prepare Step
participant N as Node Setup
participant U as UV Setup
participant D as Download Package
participant S as Scanner Steps
participant R as Report Output
participant G as GitHub Code Scanning
Note over W,MC: Two modes: Package Scan vs Workspace Scan
W->>MC: Invoke action with scanner, package, version, sandbox, upload-sarif
MC->>P: Validate inputs (scanner, package, version)
P->>P: Validate against regex patterns
P->>P: Create .scan-community-node directory
P-->>MC: Outputs: work, sarif, text, spec
alt Package mode (package != '')
MC->>N: Setup Node.js (skip for guarddog/scorecard)
MC->>U: Setup uv (always)
alt Scanner requires npm download (semgrep, osv-scanner, gitleaks)
MC->>D: Download npm package tarball
D->>D: npm pack --ignore-scripts
D->>D: Extract tarball
D-->>MC: Target directory path
end
alt Scanner is guarddog
MC->>S: Run GuardDog npm scan
S->>S: Execute in sandbox (optional)
S-->>R: Native text report
else Scanner is semgrep
MC->>S: Run Semgrep scan
S->>S: Create .semgrepignore in target
S-->>R: SARIF output
else Scanner is scorecard
MC->>S: Run Scorecard via Docker
S->>S: Score npm package source repo
S-->>R: Default text report
else Scanner is osv-scanner
MC->>S: Resolve dependencies
S->>S: npm install --package-lock-only
S->>S: Run OSV-Scanner
S-->>R: SARIF output (continue-on-error)
else Scanner is gitleaks
MC->>S: Run Gitleaks scan
S-->>R: SARIF output
end
R-->>MC: Reports written to workspace
Note over MC,G: No SARIF upload for package scans
MC-->>W: Outputs: sarif, text paths
else Workspace mode (package == '')
MC->>U: Setup uv (always)
alt Scanner is guarddog
MC->>S: Run GuardDog npm verify
S->>S: Scan package.json in workspace
S-->>R: SARIF output
else Scanner is semgrep
MC->>S: Run Semgrep scan
S->>S: Scan github.workspace
S-->>R: SARIF output
else Scanner is scorecard
MC->>S: Run Scorecard action
S->>S: Scan repository source
S-->>R: SARIF output
else Scanner is osv-scanner
MC->>S: Run OSV-Scanner
S->>S: Scan lockfiles in workspace
S-->>R: SARIF output (continue-on-error)
else Scanner is gitleaks
MC->>S: Run Gitleaks scan
S-->>R: SARIF output
end
R-->>MC: Reports written to workspace
MC->>G: Upload SARIF to code scanning (if upload-sarif true)
MC-->>W: Outputs: sarif path, text path
end
W->>W: Step summary contains findings
Note over MC,S: All scanners use continue-on-error semantics, step only fails if scanner cannot run
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
Pin the Scorecard image to a digest since the container receives the caller's token. Run the OSV-Scanner binary directly, verified by checksum, so exit code 1 (vulnerabilities found) can be ignored while scanner failures still fail the step. Extract packages for all scanners into RUNNER_TEMP now that nothing needs them inside the workspace. The example workflow skips the SARIF upload and Scorecard on pull requests from forks, where the token is read-only.
A reusable workflow gives each scanner its own parallel job with a timeout, and handles the checkout itself, so a caller is a single `uses:` job. The composite action could not offer that. The `scanners` input takes a comma-separated subset; the Prepare job validates inputs, builds the matrix, drops Scorecard on fork pull requests and writes the target table to the step summary. Release and CI now accept a reusable workflow as a releasable package: it lives in .github/workflows/<name>.yml with README and example under <name>/, and release notes render the workflow `uses:` path.
There was a problem hiding this comment.
All reported issues were addressed across 7 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
In package mode, discard the package's .semgrepignore, .gitleaks.toml and .gitleaksignore and ignore inline nosemgrep and gitleaks:allow comments. Workspace scans keep honoring the repository's configuration. Also fix the README wording about fork pull requests, and retitle the root README's table and consuming section now that it lists a reusable workflow next to actions.
…dDog Semgrep and GuardDog exit 0 on findings by default. Handle Semgrep's exit code the same way as OSV-Scanner's, treating 1 as findings and anything else as a failure, and spell out GuardDog's behavior next to its invocation so the README's report-only promise is backed by the workflow rather than by tool defaults.
Restores the layout of n8n-io/scan-community-node-action: an entry workflow that fans out to one reusable workflow per scanner, with the shared setup, package download, summary and upload steps as small composite actions under scan-community-node/actions/. This is possible because the `$/` self-repository prefix resolves those references against this repository at the running commit, even when the workflow is called from another repository. The original `./` paths resolved against the caller's checkout, which is why the port started out as a single file. Sub-workflows are named <package>-<scanner>.yml; the release path filter and the CI dropdown check treat them as part of the package. A CI workflow runs the scan on pull requests that touch it, in both modes, since act does not understand `$/`.
|
You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool. What Enabling Code Scanning Means:
For more information about GitHub Code Scanning, check out the documentation. |
There was a problem hiding this comment.
Scorecard found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.
There was a problem hiding this comment.
2 issues found across 14 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name=".github/workflows/ci-scan-community-node.yml">
<violation number="1" location=".github/workflows/ci-scan-community-node.yml:24">
P2: Both jobs call the workflow with `uses: $/.github/workflows/scan-community-node.yml`, but GitHub's documented semantics for the `$/.` prefix resolve the referenced workflow against the repository's default branch, not against the ref the calling workflow runs on. This PR is what introduces `scan-community-node.yml`, so the default branch does not contain it yet: the `package` and `workspace` jobs fail to resolve the workflow on this PR (merge-blocking), and after merge, PRs that change the workflow would be executed against main's copy instead of the change under review — the CI never exercises what it is meant to test. Use the same-repository relative form `uses: ./.github/workflows/scan-community-node.yml` in both jobs, which resolves at the calling workflow's commit. Note that the claim in scan-community-node/README.md that `$/` resolves 'at the running commit' conflicts with the documented default-branch resolution.</violation>
</file>
<file name=".github/workflows/scan-community-node-guarddog.yml">
<violation number="1" location=".github/workflows/scan-community-node-guarddog.yml:49">
P0: Every invocation fails before GuardDog runs because `$/...` is not a valid GitHub Actions `uses` reference. Replace all three `$/...` references in this workflow with fully qualified, pinned `n8n-io/github-actions/...@<sha>` references (or supported local paths when the repository is checked out).</violation>
</file>
Requires human review: Auto-approval blocked because this review re-detected 1 unresolved issue already reported by Cubic.
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
| with: | ||
| persist-credentials: false | ||
|
|
||
| - uses: $/scan-community-node/actions/setup |
There was a problem hiding this comment.
P0: Every invocation fails before GuardDog runs because $/... is not a valid GitHub Actions uses reference. Replace all three $/... references in this workflow with fully qualified, pinned n8n-io/github-actions/...@<sha> references (or supported local paths when the repository is checked out).
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/scan-community-node-guarddog.yml, line 49:
<comment>Every invocation fails before GuardDog runs because `$/...` is not a valid GitHub Actions `uses` reference. Replace all three `$/...` references in this workflow with fully qualified, pinned `n8n-io/github-actions/...@<sha>` references (or supported local paths when the repository is checked out).</comment>
<file context>
@@ -0,0 +1,108 @@
+ with:
+ persist-credentials: false
+
+ - uses: $/scan-community-node/actions/setup
+ with:
+ package: ${{ inputs.package }}
</file context>
There was a problem hiding this comment.
|
|
||
| jobs: | ||
| package: | ||
| uses: $/.github/workflows/scan-community-node.yml |
There was a problem hiding this comment.
P2: Both jobs call the workflow with uses: $/.github/workflows/scan-community-node.yml, but GitHub's documented semantics for the $/. prefix resolve the referenced workflow against the repository's default branch, not against the ref the calling workflow runs on. This PR is what introduces scan-community-node.yml, so the default branch does not contain it yet: the package and workspace jobs fail to resolve the workflow on this PR (merge-blocking), and after merge, PRs that change the workflow would be executed against main's copy instead of the change under review — the CI never exercises what it is meant to test. Use the same-repository relative form uses: ./.github/workflows/scan-community-node.yml in both jobs, which resolves at the calling workflow's commit. Note that the claim in scan-community-node/README.md that $/ resolves 'at the running commit' conflicts with the documented default-branch resolution.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/ci-scan-community-node.yml, line 24:
<comment>Both jobs call the workflow with `uses: $/.github/workflows/scan-community-node.yml`, but GitHub's documented semantics for the `$/.` prefix resolve the referenced workflow against the repository's default branch, not against the ref the calling workflow runs on. This PR is what introduces `scan-community-node.yml`, so the default branch does not contain it yet: the `package` and `workspace` jobs fail to resolve the workflow on this PR (merge-blocking), and after merge, PRs that change the workflow would be executed against main's copy instead of the change under review — the CI never exercises what it is meant to test. Use the same-repository relative form `uses: ./.github/workflows/scan-community-node.yml` in both jobs, which resolves at the calling workflow's commit. Note that the claim in scan-community-node/README.md that `$/` resolves 'at the running commit' conflicts with the documented default-branch resolution.</comment>
<file context>
@@ -0,0 +1,30 @@
+
+jobs:
+ package:
+ uses: $/.github/workflows/scan-community-node.yml
+ with:
+ package: n8n-nodes-evolution-api
</file context>
- Drop the hashFiles() term from the upload guard; security-summary only sets its sarif output when the file exists and is not empty. - Remove report files before each scan and reject symlinked reports, so a checked-in or stale security.<scanner>.* cannot pass for a result. - Remove a package's .semgrepignore before replacing it, so a symlink in the tarball cannot redirect the write. - Escape backticks indented up to three spaces in the step summary, which could otherwise still close the code fence. - Resolve a lockfile for OSV-Scanner when the target has none, in a scratch copy for repositories, instead of scanning nothing. - Warn and write a summary note when a fork PR leaves no scanner to run instead of reporting success silently. - Let git, not the shell, expand the sub-workflow glob in release notes so renamed or removed files stay in the history. - Stop uploading SARIF from the workspace self-test: Semgrep's mutable tag rule does not know $/ and flagged every self-reference.
There was a problem hiding this comment.
All reported issues were addressed across 12 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
OSV-Scanner now always scans the target itself. When a repository has no root lockfile, one is resolved from its package.json in a scratch copy that is scanned in addition, so nested lockfiles are not lost. The workspace self-test keeps the SARIF upload on and leaves out only Semgrep, whose mutable-tag rule flags the `$/` references; the package run still covers Semgrep.
Every workflow now starts from `contents: read`, and the scanner jobs declare the `security-events: write` they need for the SARIF upload. The caller's grant remains the ceiling. The self-test caller grants only `contents: read` at the top level and `security-events: write` on the workspace job alone, so the package job now exercises a read-only consumer.
A called workflow that declares `security-events: write` makes GitHub reject the whole run at startup for any caller that granted less, even when the job would be skipped. Declaring it broke the read-only package self-test, so the scan workflows declare nothing and the caller's grant applies unchanged. The comments and README say why. The self-test caller keeps `contents: read` at the top level with `security-events: write` on the workspace job only. Scorecard's workspace run moves to its own job without the upload: it flags the missing declarations and the `$/` references, and uploading that SARIF failed this repo's code scanning check.
Summary
Adds
scan-community-node, a composite action that scans an npm package, or thepackage.jsonin the workspace, for security problems before it is trusted. It ports the reusable workflows from n8n-io/scan-community-node-action into the per-action layout of this repo.One scanner runs per invocation, chosen with the
scannerinput:scannerguarddogsemgrepscorecardosv-scannergitleaksThe example workflow runs the action in a matrix so the five scans execute in parallel, which is what the separate reusable workflows gave us before.
Two modes
packageset): the npm tarball is downloaded and scanned, no checkout needed. Findings only go to the step summary; they belong to another project and are never uploaded to the caller's code scanning.packageempty): the caller's checkout is scanned and the SARIF report is uploaded to GitHub code scanning under the categoryscan-community-node/<scanner>.Every scanner reports; none fails the step on findings. The step only fails when a scanner cannot run.
Why a composite action
The reusable workflows in the source repo referenced local helper actions via
./.github/actions/...afteractions/checkout. Called from another repository, that path resolves in the caller's checkout, so they only ever worked inside their own repo. A composite action carries its own steps, and this repo's release flow can tag it.Notes
scanner,package,versionand tool version inputs are validated before they are spliced into command lines, so aworkflow_dispatchinput cannot become an option or point uvx at a different package..scan-community-node/in the workspace, because the Scorecard and OSV-Scanner actions run in Docker and only see that directory.Usage
See
scan-community-node/examples/ci-security-scan.yml.