Skip to content

feat: Add scan-community-node composite action - #2

Closed
bennycode wants to merge 10 commits into
mainfrom
scan-community-node
Closed

bennycode wants to merge 10 commits into
mainfrom
scan-community-node

Conversation

@bennycode

@bennycode bennycode commented Sep 21, 2026 •

Copy link
Copy Markdown
Member

Summary

Adds scan-community-node, a composite action that scans an npm package, or the package.json in 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 scanner input:

scanner Tool Area of concern
guarddog GuardDog Malicious and supply-chain behavior
semgrep Semgrep Insecure code patterns
scorecard OpenSSF Scorecard Security posture of the source repository
osv-scanner OSV-Scanner Known vulnerabilities in the dependency tree
gitleaks Gitleaks Hardcoded secrets

The 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

  • Published package (package set): 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.
  • Workspace (package empty): the caller's checkout is scanned and the SARIF report is uploaded to GitHub code scanning under the category scan-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/... after actions/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

  • The scanner, package, version and tool version inputs are validated before they are spliced into command lines, so a workflow_dispatch input cannot become an option or point uvx at a different package.
  • All third-party actions are pinned to SHAs. GuardDog and Semgrep are installed from PyPI at pinned versions exposed as inputs; Gitleaks is downloaded with a checksum; Scorecard and OSV-Scanner are pinned inside the action.
  • Reports are written to .scan-community-node/ in the workspace, because the Scorecard and OSV-Scanner actions run in Docker and only see that directory.
  • No scripts, nothing to typecheck. The action is bash, jq and uvx.
  • Added to the release dropdown and the README table.

Usage

- uses: n8n-io/github-actions/scan-community-node@<sha> # scan-community-node/v1.0.0
  with:
    scanner: ${{ matrix.scanner }}

See scan-community-node/examples/ci-security-scan.yml.

Review in cubic

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.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Loading

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread scan-community-node/action.yml Outdated
Comment thread scan-community-node/action.yml Outdated
Comment thread scan-community-node/README.md Outdated
Comment thread scan-community-node/examples/ci-security-scan.yml
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.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread .github/workflows/scan-community-node.yml Outdated
Comment thread scan-community-node/README.md
Comment thread README.md
Comment thread scan-community-node/README.md Outdated
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 `$/`.
@github-advanced-security

Copy link
Copy Markdown

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:

  • The 'Security' tab will display more code scanning analysis results (e.g., for the default branch).
  • Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results.
  • You will be able to see the analysis results for the pull request's branch on this overview once the scans have completed and the checks have passed.

For more information about GitHub Code Scanning, check out the documentation.

@github-advanced-security github-advanced-security AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scorecard found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.

Comment thread .github/workflows/scan-community-node-gitleaks.yml
Comment thread .github/workflows/scan-community-node-gitleaks.yml
Comment thread .github/workflows/scan-community-node-gitleaks.yml
Comment thread .github/workflows/scan-community-node-gitleaks.yml
Comment thread .github/workflows/scan-community-node-guarddog.yml
Comment thread .github/workflows/scan-community-node-scorecard.yml
Comment thread .github/workflows/scan-community-node-semgrep.yml
Comment thread .github/workflows/scan-community-node-semgrep.yml
Comment thread .github/workflows/scan-community-node-semgrep.yml
Comment thread .github/workflows/scan-community-node-semgrep.yml

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread scan-community-node/actions/upload-security-sarif/action.yml Outdated
Comment thread .github/workflows/scan-community-node.yml
Comment thread .github/workflows/scan-community-node-osv-scanner.yml Outdated
Comment thread .github/workflows/scan-community-node-semgrep.yml
Comment thread scan-community-node/actions/security-summary/action.yml Outdated
Comment thread scan-community-node/actions/security-summary/action.yml Outdated

jobs:
package:
uses: $/.github/workflows/scan-community-node.yml

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>

Comment thread .github/workflows/release.yml Outdated
- 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.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread .github/workflows/scan-community-node-osv-scanner.yml
Comment thread .github/workflows/ci-scan-community-node.yml
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.
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