From 9eda43430eba641e4affd5956d1c33c17668c963 Mon Sep 17 00:00:00 2001 From: Thibaud-Vdb Date: Tue, 4 Aug 2026 10:39:34 +0200 Subject: [PATCH 1/3] ci: pin github actions to commit shas Mutable tags are movable pointers: whoever controls an action, or anyone who compromises it, can re-point the tag and the next run executes unreviewed code with the job's token. This repo publishes to npm from CI, so the release path deserves frozen inputs. Same pattern as the tj-actions/changed-files incident (CVE-2025-30066). Pinning to the full commit sha freezes what runs; the version each sha corresponds to stays as a comment. Every sha was resolved from the upstream repository and cross-checked against its release tag. No workflow logic changes. --- .github/workflows/ci.yml | 6 +++--- .github/workflows/codeql-analysis.yml | 8 ++++---- .github/workflows/npm-publish.yml | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 015d703c2..f3c42ffa9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,17 +13,17 @@ jobs: name: Run tests on Node.js ${{ matrix.node-version }} steps: - name: Setup Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4 + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: ${{ matrix.node-version }} - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 - name: Install dependencies run: npm install --legacy-peer-deps - name: Run tests run: npm test - if: matrix.node-version == 24 name: Send coverage info to Codecov - uses: codecov/codecov-action@v5 + uses: codecov/codecov-action@0fb7174895f61a3b6b78fc075e0cd60383518dac # v5.5.5 with: token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index e89a9b51e..90021e980 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -38,11 +38,11 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v3 + uses: github/codeql-action/init@e60ea984bd3baa95954f2856bcf24f9eaba46637 # v3.37.5 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -53,7 +53,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v3 + uses: github/codeql-action/autobuild@e60ea984bd3baa95954f2856bcf24f9eaba46637 # v3.37.5 # â„šī¸ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -67,4 +67,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 + uses: github/codeql-action/analyze@e60ea984bd3baa95954f2856bcf24f9eaba46637 # v3.37.5 diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index c159e11eb..6d98d5706 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -10,12 +10,12 @@ jobs: id-token: write steps: - name: Setup Node.js 24 - uses: actions/setup-node@v5 + uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5.0.0 with: node-version: 24 registry-url: https://registry.npmjs.org/ - name: Checkout Repository - uses: actions/checkout@v4 + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 - name: Install Dependencies run: npm install --legacy-peer-deps - name: Run Tests From a264036e4723362143e1c5d038ab4c143bb04571 Mon Sep 17 00:00:00 2001 From: Thibaud-Vdb Date: Tue, 4 Aug 2026 10:39:34 +0200 Subject: [PATCH 2/3] ci: declare permissions on the test workflow Without a permissions block the test job's GITHUB_TOKEN inherits the repository default scope. Coverage uploads use the dedicated CODECOV_TOKEN secret and the GitHub token is never used, so contents read is exact. The npm publish and CodeQL workflows already declare scoped permissions and are untouched. --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f3c42ffa9..431c0e825 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,6 +4,9 @@ on: branches: [master] pull_request: branches: [master] +permissions: + contents: read + jobs: test: runs-on: ubuntu-latest From 399f202f89247df6140369aaf080f88f67709c12 Mon Sep 17 00:00:00 2001 From: Thibaud-Vdb Date: Tue, 4 Aug 2026 10:40:05 +0200 Subject: [PATCH 3/3] ci: add plumber workflow security check Scans the workflows on each push to master and on pull requests, and fails when something regresses: an unpinned action, a job without a permissions block, a known CVE. The gate passes at 85 points so a single small finding does not block PRs. The config is a small overlay that inherits the CLI's built-in baseline and narrows the branch protection requirement to master, where npm releases cut from. Each run publishes the score to score.getplumber.io, shown as a badge in the README. --- .github/workflows/plumber.yml | 34 ++++++++++++++++++++++++++++++++++ .plumber.yaml | 15 +++++++++++++++ README.md | 3 +++ 3 files changed, 52 insertions(+) create mode 100644 .github/workflows/plumber.yml create mode 100644 .plumber.yaml diff --git a/.github/workflows/plumber.yml b/.github/workflows/plumber.yml new file mode 100644 index 000000000..45610c094 --- /dev/null +++ b/.github/workflows/plumber.yml @@ -0,0 +1,34 @@ +name: Plumber + +on: + push: + branches: [master] + pull_request: + +permissions: + contents: read + +jobs: + plumber: + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: read + security-events: write + # Needed by score-push to publish the score for the README badge. + id-token: write + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + + - uses: getplumber/plumber@40bd6b5bb8ff4f0944feacaeb41dea6bce08d62d # v0.4.28 + with: + # Code scanning upload needs security-events write, which PRs + # from forks do not get. The report stays available as a + # workflow artifact there. + upload-sarif: ${{ github.event.pull_request.head.repo.fork != true }} + # Publishes the score to score.getplumber.io, which feeds the + # badge in the README. A failed push never fails the run. + score-push: true + # Gate at 85 points instead of the all-or-nothing default, + # leaves room for a small finding without blocking PRs. + min-points: 85 diff --git a/.plumber.yaml b/.plumber.yaml new file mode 100644 index 000000000..35c3dc3a2 --- /dev/null +++ b/.plumber.yaml @@ -0,0 +1,15 @@ +# Plumber overlay: inherits every control from the CLI's built-in +# baseline, only the differences for this repo are written here. +# Run 'plumber config resolve' to see the full effective config. +extends: plumber:default +version: "2.0" + +github: + controls: + # The baseline also expects release/* branches to be protected; + # this repo's release branches are working branches. Keep the + # requirement on master, where the npm releases cut from. + branchMustBeProtected: + defaultMustBeProtected: true + namePatterns: + - master diff --git a/README.md b/README.md index 9e2f826e6..2068806d1 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ [![NPM version][npm-image]][npm-url] [![CI][ci-image]][ci-url] [![Coverage][codecov-image]][codecov-url] +[![Plumber Score][plumber-image]][plumber-url] [![Downloads][downloads-image]][npm-url] [![Backers on Open Collective](https://opencollective.com/validatorjs/backers/badge.svg)](#backers) [![Sponsors on Open Collective](https://opencollective.com/validatorjs/sponsors/badge.svg)](#sponsors) @@ -229,6 +230,8 @@ This project is licensed under the [MIT](LICENSE). See the [LICENSE](LICENSE) fi [codecov-url]: https://codecov.io/gh/validatorjs/validator.js [codecov-image]: https://codecov.io/gh/validatorjs/validator.js/branch/master/graph/badge.svg +[plumber-image]: https://score.getplumber.io/github.com/validatorjs/validator.js.svg +[plumber-url]: https://score.getplumber.io/github.com/validatorjs/validator.js [ci-url]: https://github.com/validatorjs/validator.js/actions?query=workflow%3ACI [ci-image]: https://github.com/validatorjs/validator.js/workflows/CI/badge.svg?branch=master