diff --git a/.github/dependabot.yml b/.github/dependabot.yml index a87f7e04c..59fb48579 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -7,7 +7,7 @@ updates: schedule: interval: weekly cooldown: - default-days: 3 + default-days: 7 commit-message: prefix: 'chore' include: 'scope' @@ -26,7 +26,7 @@ updates: schedule: interval: weekly cooldown: - default-days: 3 + default-days: 7 commit-message: prefix: 'chore' include: 'scope' @@ -52,6 +52,7 @@ updates: semver-patch-days: 3 semver-minor-days: 3 semver-major-days: 7 + default-days: 7 open-pull-requests-limit: 10 commit-message: prefix: 'chore' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d2d78065a..7af4950d8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,7 +10,7 @@ on: branches: [main] permissions: - pull-requests: write + contents: read jobs: build-ubuntu: @@ -31,6 +31,7 @@ jobs: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 + persist-credentials: false - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 @@ -103,6 +104,7 @@ jobs: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 + persist-credentials: false - name: Use Node.js 24.x uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 1302dcd37..93f7b8a46 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -31,17 +31,20 @@ jobs: egress-policy: audit - name: Checkout repository - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false - name: Initialize CodeQL - uses: github/codeql-action/init@d97b3428e8eebbb1810cf454d6397886d136b4ba # ratchet:github/codeql-action/init@v4 + uses: github/codeql-action/init@7188fc363630916deb702c7fdcf4e481b751f97a # v4.37.1 + with: languages: ${{ matrix.language }} - name: Autobuild - uses: github/codeql-action/autobuild@d97b3428e8eebbb1810cf454d6397886d136b4ba # ratchet:github/codeql-action/autobuild@v4 + uses: github/codeql-action/autobuild@7188fc363630916deb702c7fdcf4e481b751f97a # v4.37.1 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@d97b3428e8eebbb1810cf454d6397886d136b4ba # ratchet:github/codeql-action/analyze@v4 + uses: github/codeql-action/analyze@7188fc363630916deb702c7fdcf4e481b751f97a # v4.37.1 with: category: '/language:${{matrix.language}}' diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index fd5f4dd33..f13153981 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -16,8 +16,10 @@ jobs: - name: 'Checkout Repository' uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: Dependency Review - uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v4 + uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0 with: comment-summary-in-pr: always fail-on-severity: high diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 20e8822d7..3e2051d3a 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -27,7 +27,8 @@ jobs: - name: Checkout Repository uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - + with: + persist-credentials: false - name: Log in to Docker Hub if: github.repository_owner == 'finos' uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4 @@ -39,7 +40,7 @@ jobs: id: tags run: | if [ "${{ github.event_name }}" = "release" ]; then - echo "tags=${{ github.repository }}:${{ github.ref_name }},${{ github.repository }}:latest" >> $GITHUB_OUTPUT + echo "tags=${{ github.repository }}:${GITHUB_REF_NAME},${{ github.repository }}:latest" >> $GITHUB_OUTPUT else echo "tags=${{ github.repository }}:main" >> $GITHUB_OUTPUT fi diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 85b3b5db2..31dc5b68d 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -2,8 +2,6 @@ name: E2E Tests permissions: contents: read - issues: write - pull-requests: write on: push: @@ -28,6 +26,8 @@ jobs: - name: Checkout code uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false - name: Set up Docker Buildx uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c @@ -41,7 +41,7 @@ jobs: uses: docker/setup-compose-action@3408803818f5b5065308cdf3293ecbddf1ab2fac - name: Set up Node.js - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v6 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: '24' cache: 'npm' diff --git a/.github/workflows/experimental-inventory-ci.yml b/.github/workflows/experimental-inventory-ci.yml index 9a79c6778..f09d7c72a 100644 --- a/.github/workflows/experimental-inventory-ci.yml +++ b/.github/workflows/experimental-inventory-ci.yml @@ -31,9 +31,10 @@ jobs: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 + persist-credentials: false - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v6 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: ${{ matrix.node-version }} diff --git a/.github/workflows/experimental-inventory-cli-publish.yml b/.github/workflows/experimental-inventory-cli-publish.yml index 1b0bb1810..c129699ad 100644 --- a/.github/workflows/experimental-inventory-cli-publish.yml +++ b/.github/workflows/experimental-inventory-cli-publish.yml @@ -19,17 +19,21 @@ jobs: egress-policy: audit - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false # Setup .npmrc file to publish to npm - - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v6 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: '24.x' registry-url: 'https://registry.npmjs.org' - name: check version matches input run: | - grep "\"version\": \"${{ github.event.inputs.version }}\"," package.json + grep "\"version\": \"${GITHUB_EVENT_INPUTS_VERSION}\"," package.json working-directory: ./experimental/li-cli + env: + GITHUB_EVENT_INPUTS_VERSION: ${{ github.event.inputs.version }} - run: npm ci working-directory: ./experimental/li-cli @@ -41,3 +45,5 @@ jobs: working-directory: ./experimental/li-cli env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + with: + persist-credentials: false diff --git a/.github/workflows/experimental-inventory-publish.yml b/.github/workflows/experimental-inventory-publish.yml index 81bd2843b..193679c15 100644 --- a/.github/workflows/experimental-inventory-publish.yml +++ b/.github/workflows/experimental-inventory-publish.yml @@ -19,17 +19,22 @@ jobs: egress-policy: audit - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false # Setup .npmrc file to publish to npm - - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v6 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: '24.x' registry-url: 'https://registry.npmjs.org' + persist-credentials: false - name: check version matches input run: | - grep "\"version\": \"${{ github.event.inputs.version }}\"," package.json + grep "\"version\": \"${GITHUB_EVENT_INPUTS_VERSION}\"," package.json working-directory: ./experimental/license-inventory + env: + GITHUB_EVENT_INPUTS_VERSION: ${{ github.event.inputs.version }} - run: npm ci working-directory: ./experimental/license-inventory diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index e97c1a000..376819e89 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -19,7 +19,7 @@ jobs: egress-policy: audit - name: Install NodeJS - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v6 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: ${{ env.NODE_VERSION }} @@ -27,6 +27,7 @@ jobs: uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 + persist-credentials: false - name: Install Dependencies run: npm ci diff --git a/.github/workflows/npm.yml b/.github/workflows/npm.yml index f5a7def7d..a065c39bf 100644 --- a/.github/workflows/npm.yml +++ b/.github/workflows/npm.yml @@ -1,6 +1,8 @@ name: Publish to NPM on: release: + # False positive: setup-node step doesn't use cache input + # zizmor: ignore[cache-poisoning] types: [published] workflow_dispatch: inputs: @@ -26,8 +28,10 @@ jobs: egress-policy: audit - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false # Setup .npmrc file to publish to npm - - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v6 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: '24' registry-url: 'https://registry.npmjs.org' diff --git a/.github/workflows/pr-lint.yml b/.github/workflows/pr-lint.yml index f7a4e69b3..8f7a858e6 100644 --- a/.github/workflows/pr-lint.yml +++ b/.github/workflows/pr-lint.yml @@ -3,6 +3,8 @@ name: 'PR' on: + # PR target used in accordance with README + # zizmor: ignore[dangerous-triggers] pull_request_target: types: - opened diff --git a/.github/workflows/sample-publish.yml b/.github/workflows/sample-publish.yml index f154f405d..78d153496 100644 --- a/.github/workflows/sample-publish.yml +++ b/.github/workflows/sample-publish.yml @@ -3,6 +3,8 @@ name: Publish samples to NPM on: push: tags: + # False positive: setup-node step doesn't use cache input + # zizmor: ignore[cache-poisoning] - 'sample-*' permissions: @@ -17,8 +19,11 @@ jobs: with: egress-policy: audit - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + # Setup .npmrc file to publish to npm - - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v6 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: '24.x' registry-url: 'https://registry.npmjs.org' diff --git a/.github/workflows/unused-dependencies.yml b/.github/workflows/unused-dependencies.yml index bf1fc6eef..40cc880d3 100644 --- a/.github/workflows/unused-dependencies.yml +++ b/.github/workflows/unused-dependencies.yml @@ -15,8 +15,11 @@ jobs: - name: 'Checkout Repository' uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - name: 'Setup Node.js' - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v6 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: '24.x' - name: 'Run depcheck' diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml new file mode 100644 index 000000000..138de5f13 --- /dev/null +++ b/.github/workflows/zizmor.yml @@ -0,0 +1,32 @@ +name: zizmor + +# Static analysis of GitHub Actions workflows with zizmor +# (https://docs.zizmor.sh). Results are uploaded as SARIF to +# Code Scanning; findings do not fail the build. + +on: + push: + branches: [main] + pull_request: + branches: [main] + +permissions: {} + +jobs: + zizmor: + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write # SARIF upload to Code Scanning + steps: + - name: Checkout code + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false + + - name: Run zizmor + uses: zizmorcore/zizmor-action@192e21d79ab29983730a13d1382995c2307fbcaa # v0.5.7 + with: + min-severity: low + min-confidence: low diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6d09f1be5..337a95b20 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,6 +17,8 @@ For project governance, roles, and voting procedures, see the [Governance sectio - [Fuzz Tests](#fuzz-tests) - [Coverage Requirements](#coverage-requirements) - [Code Quality](#code-quality) + - [Linting & Formatting](#linting--formatting) + - [GitHub Actions Security (zizmor)](#github-actions-security-zizmor) - [Configuration Schema](#configuration-schema) - [Submitting a Pull Request](#submitting-a-pull-request) - [Community](#community) @@ -25,12 +27,13 @@ For project governance, roles, and voting procedures, see the [Governance sectio We actively support and test against the latest two LTS Node versions, currently 22 and 24. When a new LTS version rolls out (26, 28, etc.), we deprecate the oldest one. -| Tool | Version | Notes | -| ---------------------------------------------------------------------------------------------------------- | -------------------- | --------------------------- | -| [Node.js](https://nodejs.org/en/download) | 22.13.1+, or 24.0.0+ | Check with `node -v` | -| [npm](https://npmjs.com/) | 8+ | Bundled with Node.js | -| [Git](https://git-scm.com/downloads) | Any recent version | Must support HTTP/S | -| [Docker](https://docs.docker.com/get-docker/) & [Docker Compose](https://docs.docker.com/compose/install/) | Any recent version | Required for E2E tests only | +| Tool | Version | Notes | +| ---------------------------------------------------------------------------------------------------------- | -------------------- | --------------------------------------------------------------------------------------- | +| [Node.js](https://nodejs.org/en/download) | 22.13.1+, or 24.0.0+ | Check with `node -v` | +| [npm](https://npmjs.com/) | 8+ | Bundled with Node.js | +| [Git](https://git-scm.com/downloads) | Any recent version | Must support HTTP/S | +| [Docker](https://docs.docker.com/get-docker/) & [Docker Compose](https://docs.docker.com/compose/install/) | Any recent version | Required for E2E tests only | +| [zizmor](https://docs.zizmor.sh/) | Any recent version | Optional; scans GitHub Actions workflows (see [below](#github-actions-security-zizmor)) | ## Getting Started @@ -349,6 +352,8 @@ The coverage report is written to `./coverage/`. If your PR is below the thresho ## Code Quality +### Linting & Formatting + ```bash npm run lint # Run ESLint npm run lint:fix # ESLint with auto-fix @@ -358,6 +363,62 @@ npm run format:check # Check formatting without modifying files CI runs ESLint, Prettier, and TypeScript type checks on every PR (see [`.github/workflows/lint.yml`](.github/workflows/lint.yml)). +### GitHub Actions Security (zizmor) + +We use [zizmor](https://docs.zizmor.sh/) to statically analyze our GitHub Actions workflows for security issues (template injection, credential persistence, overly broad permissions, etc.). It runs in CI on every push and PR to `main` (see [`.github/workflows/zizmor.yml`](.github/workflows/zizmor.yml)) and uploads results to GitHub Code Scanning. By design, the CI job does **not** fail the build, so running zizmor locally before pushing is the best way to catch and fix findings early. + +If you change anything under `.github/workflows/`, you should first run zizmor locally. + +#### Install + +zizmor is a standalone tool. Install it with your preferred tool: + +```bash +# Using uv (recommended) +uv tool install zizmor + +# Using pipx +pipx install zizmor + +# Using pip +pip install zizmor + +# Using Homebrew (macOS/Linux) +brew install zizmor + +# Using Cargo (Rust toolchain) +cargo install zizmor +``` + +See the [zizmor installation docs](https://docs.zizmor.sh/installation/) for more options. + +#### Run + +```bash +# Scan all workflows in the repo (matches the CI thresholds) +zizmor --min-severity low --min-confidence low . + +# Scan a single workflow +zizmor .github/workflows/ci.yml +``` + +By default zizmor runs in offline mode; some audits such as version pinning require online access. To enable the full set of checks, pass a GitHub token: + +```bash +zizmor . --gh-token +``` + +#### Fixing findings + +Some findings have auto-fixes. Preview them before applying: + +```bash +zizmor --fix=all --dry-run . # Show what would change +zizmor --fix=all . # Apply safe and unsafe fixes +``` + +Always review auto-fixes before committing. For findings without an auto-fix, follow the linked audit documentation in the zizmor output. If a finding is a false positive or an accepted risk, suppress it with an inline [`# zizmor: ignore[]` comment](https://docs.zizmor.sh/usage/#ignoring-results) rather than disabling the check globally. + ## Configuration Schema GitProxy uses a JSON Schema ([config.schema.json](config.schema.json)) to define and validate configuration. When adding or modifying config properties: @@ -399,7 +460,7 @@ The following checks must pass before a PR can be merged: - **Lint & format**: ESLint, Prettier, TypeScript type checks - **Commit lint**: Conventional Commits validation - **Coverage**: 80%+ patch coverage via CodeCov -- **Security**: CodeQL analysis, dependency review, OpenSSF Scorecard +- **Security**: CodeQL analysis, dependency review, OpenSSF Scorecard, and [zizmor](#github-actions-security-zizmor) GitHub Actions analysis ### Contributor License Agreement (CLA)