-
Notifications
You must be signed in to change notification settings - Fork 0
feat(js-pr-validation): add socket.dev supply chain gate #645
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
4693ef8
cfe360f
f75d2fd
ee22bc5
77b3ad2
5b9ecc6
54f3c4d
bb8ca83
cde744a
7a33a1e
ef1c887
a778bc2
13d8d48
4a53572
4f08039
de9c860
43ac207
b9f61ce
52c6a81
651aa00
0f1afd5
e190d16
d5fa617
eacae6a
a301f93
817e47d
3ce2df2
b6365a2
6137cc4
ce54891
b38f781
87fffd0
b38a179
c7ad918
f850c85
7d39f39
10b45f7
95c6f70
aaf144c
c807994
b1aa4f4
81cc6bf
7b3255f
9f96d77
cbaa29e
471eb23
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,6 +27,10 @@ on: | |
| description: 'Run the security scan pipeline (Trivy, CodeQL, prerelease checks)' | ||
| type: boolean | ||
| default: true | ||
| run_socket: | ||
| description: 'Run the Socket supply-chain pipeline (Socket Firewall and, when enabled, the Socket CLI scan)' | ||
| type: boolean | ||
| default: true | ||
|
Comment on lines
+40
to
+43
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift Wire the paid Socket scan or remove the public claim. The
📍 Affects 2 files
🤖 Prompt for AI Agents |
||
|
|
||
| # ----------------- Change gate ----------------- | ||
| ignore_globs: | ||
|
|
@@ -272,11 +276,55 @@ on: | |
| description: 'Comma-separated directories to skip in every Trivy filesystem scan (appended to the built-in skip list).' | ||
| type: string | ||
| default: '' | ||
|
|
||
| # ----------------- Socket supply chain (src/security/socket-*) ----------------- | ||
| socket_enable_firewall: | ||
| description: 'Run Socket Firewall (free tier, no token) and install dependencies through it, blocking malicious packages at install time.' | ||
| type: boolean | ||
| default: true | ||
| socket_enable_scan: | ||
| description: 'Run the Socket CLI scan (paid tier). Requires the SOCKET_SECURITY_API_KEY secret; without it the step skips with a notice.' | ||
| type: boolean | ||
| default: false | ||
| socket_working_dir: | ||
| description: 'Directory holding the package.json and lockfile scanned by the Socket jobs' | ||
| type: string | ||
| default: '.' | ||
| socket_firewall_version: | ||
| description: 'Socket Firewall binary version. Empty or "latest" tracks the newest release.' | ||
| type: string | ||
| default: 'latest' | ||
| socket_job_summary: | ||
| description: 'Socket Firewall job summary verbosity (all, errors, none)' | ||
| type: string | ||
| default: 'all' | ||
| socket_fail_on_block: | ||
| description: 'Fail the Socket job when Socket Firewall blocks a package. Set to false to report the block as a warning instead.' | ||
| type: boolean | ||
| default: true | ||
| socket_fail_on_findings: | ||
| description: 'Fail the Socket job when the Socket CLI scan reports blocking alerts. Default false keeps the scan advisory until a repository is clean.' | ||
| type: boolean | ||
| default: false | ||
| socket_sarif_file: | ||
| description: 'Path where the Socket CLI scan writes its SARIF report. Empty = no SARIF.' | ||
| type: string | ||
| default: '' | ||
|
Comment on lines
+364
to
+370
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Document configured API findings as blocking. A non-empty
📍 Affects 2 files
🤖 Prompt for AI Agents |
||
| socket_ignore_commit_files: | ||
| description: 'Scan every manifest instead of only the ones touched by the commit (--ignore-commit-files)' | ||
| type: boolean | ||
| default: false | ||
| socket_python_version: | ||
| description: 'Python version used to run the Socket CLI' | ||
| type: string | ||
| default: '3.12' | ||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||
| secrets: | ||
| MANAGE_TOKEN: | ||
| required: false | ||
| SLACK_WEBHOOK_URL: | ||
| required: false | ||
| SOCKET_SECURITY_API_KEY: | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| required: false | ||
|
|
||
| permissions: | ||
| actions: read | ||
|
|
@@ -417,6 +465,66 @@ jobs: | |
| result: ${{ needs.changes.result != 'success' && needs.changes.result || needs.security.result }} | ||
| label: Security | ||
|
|
||
| # ----------------- Socket Supply Chain ----------------- | ||
| socket: | ||
| name: Socket (checks) | ||
| needs: changes | ||
| if: inputs.run_socket && needs.changes.outputs.code == 'true' | ||
| runs-on: ${{ vars.GENERAL_RUNNERS || inputs.runner_type }} | ||
| permissions: | ||
| contents: read | ||
| issues: write | ||
| pull-requests: write | ||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6 | ||
| with: | ||
| # socketcli diffs the commit to decide which manifests changed. | ||
| fetch-depth: 2 | ||
| persist-credentials: false | ||
|
|
||
| - name: Socket Firewall | ||
| if: inputs.socket_enable_firewall | ||
| uses: LerianStudio/github-actions-shared-workflows/src/security/socket-firewall@v1 | ||
| with: | ||
| package-manager: ${{ inputs.package_manager }} | ||
| node-version: ${{ inputs.node_version }} | ||
| working-dir: ${{ inputs.socket_working_dir }} | ||
| firewall-version: ${{ inputs.socket_firewall_version }} | ||
| job-summary: ${{ inputs.socket_job_summary }} | ||
| fail-on-block: ${{ inputs.socket_fail_on_block }} | ||
| dry-run: ${{ inputs.dry_run }} | ||
|
|
||
| - name: Socket Scan | ||
| if: always() && inputs.socket_enable_scan | ||
| uses: LerianStudio/github-actions-shared-workflows/src/security/socket-scan@v1 | ||
| with: | ||
| # Token presence is resolved inside the composite rather than in an | ||
| # `if:` — the secrets context is unavailable in step conditions. | ||
| socket-api-key: ${{ secrets.SOCKET_SECURITY_API_KEY }} | ||
| github-token: ${{ secrets.MANAGE_TOKEN || github.token }} | ||
| target-path: ${{ inputs.socket_working_dir }} | ||
| pr-number: ${{ github.event.pull_request.number || 0 }} | ||
| python-version: ${{ inputs.socket_python_version }} | ||
| fail-on-findings: ${{ inputs.socket_fail_on_findings }} | ||
| sarif-file: ${{ inputs.socket_sarif_file }} | ||
| ignore-commit-files: ${{ inputs.socket_ignore_commit_files }} | ||
| dry-run: ${{ inputs.dry_run }} | ||
|
|
||
| socket-gate: | ||
| name: Socket | ||
| needs: [changes, socket] | ||
| if: always() | ||
| runs-on: ${{ vars.GENERAL_RUNNERS || inputs.runner_type }} | ||
| permissions: | ||
| contents: read | ||
| steps: | ||
| - name: Aggregate Socket result | ||
| uses: LerianStudio/github-actions-shared-workflows/src/validate/result-gate@v1 | ||
| with: | ||
| result: ${{ needs.changes.result != 'success' && needs.changes.result || needs.socket.result }} | ||
| label: Socket | ||
|
|
||
| # ----------------- Org ruleset compatibility stubs ----------------- | ||
| # Go Analysis and Lib Version are required status checks in the Lerian org | ||
| # branch-protection ruleset (designed for Go repos). These stubs are | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,101 @@ | ||
| <table border="0" cellspacing="0" cellpadding="0"> | ||
| <tr> | ||
| <td><img src="https://github.com/LerianStudio.png" width="72" alt="Lerian" /></td> | ||
| <td><h1>socket-firewall</h1></td> | ||
| </tr> | ||
| </table> | ||
|
|
||
| Composite action that installs [Socket Firewall](https://github.com/SocketDev/sfw-free) (free edition, no account required) and then runs the project's dependency install through it. Socket Firewall shims `npm`, `yarn` and `pnpm`, inspects each package as it is fetched and refuses the ones whose behavior matches a supply-chain attack — malicious install scripts, credential exfiltration, typosquats, hijacked patch releases. | ||
|
|
||
| This is the free tier of Socket. It catches malicious packages at install time but produces no PR report and enforces no central policy — for that, see [`socket-scan`](../socket-scan/README.md). | ||
|
|
||
| ## Inputs | ||
|
|
||
| | Input | Description | Required | Default | | ||
| |---|---|:---:|---| | ||
| | `package-manager` | Package manager used to install dependencies (`npm`, `yarn`, `pnpm`) | No | `npm` | | ||
| | `node-version` | Node.js version used for the guarded install | No | `22` | | ||
| | `working-dir` | Directory holding the `package.json` and lockfile | No | `.` | | ||
| | `firewall-version` | Socket Firewall binary version. `latest` tracks the newest release | No | `latest` | | ||
| | `job-summary` | Socket Firewall job summary verbosity (`all`, `errors`, `none`) | No | `all` | | ||
| | `use-cache` | Cache the Socket Firewall binaries between runs | No | `true` | | ||
| | `github-token` | Token used by Socket Firewall to download its binaries. Empty falls back to `github.token` | No | `''` | | ||
| | `fail-on-block` | Fail the step when Socket Firewall blocks a package | No | `true` | | ||
| | `dry-run` | Print the resolved configuration and never fail the step | No | `false` | | ||
|
|
||
| ## Outputs | ||
|
|
||
| | Output | Description | | ||
| |---|---| | ||
| | `skipped` | `true` when no lockfile was found in `working-dir`, so nothing was installed or inspected | | ||
| | `blocked` | `true` when the install failed and the output carries a Socket Firewall block marker | | ||
| | `install-exit-code` | Exit code returned by the package manager install | | ||
| | `report-path` | Path to the Socket Firewall report JSON produced by the underlying action | | ||
|
|
||
| ## No lockfile, no run | ||
|
|
||
| Before touching the toolchain the action checks for the lockfile matching `package-manager` (`package-lock.json`, `yarn.lock` or `pnpm-lock.yaml`) inside `working-dir`. If it is absent, everything is skipped with a `::warning::` and `skipped=true`. | ||
|
|
||
| That keeps monorepos — whose manifests live under a subdirectory — from going red on a configuration gap: they get a warning pointing at `working-dir` instead of a failed install. | ||
|
|
||
| ## How the verdict is decided | ||
|
|
||
| The shims make a blocked package surface as a non-zero exit from the package manager itself, so a failing install is either a Socket block or an ordinary dependency resolution problem. The action separates the two by looking for a Socket block marker in the install output: | ||
|
|
||
| | Install exit | Block marker | `fail-on-block` | Result | | ||
| |---|---|---|---| | ||
| | `0` | — | any | Step passes | | ||
| | non-zero | present | `true` | `::error::` + step fails | | ||
| | non-zero | present | `false` | `::warning::` + step passes | | ||
| | non-zero | absent | any | `::error::` + step fails | | ||
|
|
||
| A broken install is **always** a failure. Swallowing it would hide a genuine problem behind a security toggle, so `fail-on-block: false` only softens confirmed Socket blocks. | ||
|
|
||
| With `dry-run: true` the install still runs through the firewall and everything is reported, but the step never fails. | ||
|
|
||
| ## Usage | ||
|
|
||
| ### As a composite step | ||
|
|
||
| ```yaml | ||
| jobs: | ||
| socket: | ||
| runs-on: blacksmith-4vcpu-ubuntu-2404 | ||
| steps: | ||
| - uses: actions/checkout@v6 | ||
| with: | ||
| persist-credentials: false | ||
|
|
||
| - name: Socket Firewall | ||
| uses: LerianStudio/github-actions-shared-workflows/src/security/socket-firewall@v1 | ||
| with: | ||
| package-manager: 'npm' | ||
| node-version: '22' | ||
| working-dir: '.' | ||
| ``` | ||
|
|
||
| ### Via the reusable workflow | ||
|
|
||
| Socket Firewall is wired into the `js-pr-validation` umbrella and enabled by default: | ||
|
|
||
| ```yaml | ||
| jobs: | ||
| pr-validation: | ||
| uses: LerianStudio/github-actions-shared-workflows/.github/workflows/js-pr-validation.yml@v1.x.x | ||
| with: | ||
| run_socket: true # default | ||
| socket_enable_firewall: true # default | ||
| socket_fail_on_block: true # default | ||
| secrets: inherit | ||
| ``` | ||
|
|
||
| ## Permissions required | ||
|
|
||
| ```yaml | ||
| permissions: | ||
| contents: read | ||
| ``` | ||
|
|
||
| ## Why this action | ||
|
|
||
| Socket Firewall ships as a binary plus package-manager shims, which is awkward to install correctly inline. [`SocketDev/action`](https://github.com/SocketDev/action) is the vendor-maintained installer, so this composite wraps it (pinned by commit SHA) and adds the parts it does not cover: the toolchain setup, the guarded install itself, block-versus-failure attribution and the advisory mode. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: LerianStudio/github-actions-shared-workflows
Length of output: 8631
🏁 Script executed:
Repository: LerianStudio/github-actions-shared-workflows
Length of output: 35084
🏁 Script executed:
Repository: LerianStudio/github-actions-shared-workflows
Length of output: 50400
🏁 Script executed:
Repository: LerianStudio/github-actions-shared-workflows
Length of output: 9610
Add an active update path for
SocketDev/action.Dependabot does not scan composite actions under
src/**/action.yml. The existingpinned-actionsvalidator checks pin format but does not update action versions. Bothsrc/security/socket-firewall/action.ymlandsrc/setup/setup-node-guarded/action.ymlreferenceSocketDev/action. Add a drift check that covers both files.🤖 Prompt for AI Agents