diff --git a/.github/workflows/dependency-review-reusable.yml b/.github/workflows/dependency-review-reusable.yml new file mode 100644 index 0000000..e7b61bc --- /dev/null +++ b/.github/workflows/dependency-review-reusable.yml @@ -0,0 +1,60 @@ +name: Dependency Review + +permissions: + contents: read + +on: + workflow_call: + inputs: + config-file: + description: "Path to the dependency review configuration file" + required: false + type: string + default: "windlasstech/.github/.github/dependency-review-config.yml@main" + fail-on-severity: + description: "Minimum severity to fail the check (low, moderate, high, critical)" + required: false + type: string + default: "" + comment-summary-in-pr: + description: "Post summary comment to PR (always, on-failure, never)" + required: false + type: string + default: "on-failure" + warn-only: + description: "Report only, never fail" + required: false + type: boolean + default: false + external-repo-token: + description: "Token for accessing external repository config files" + required: false + type: string + default: "" + +jobs: + dependency-review: + runs-on: ubuntu-latest + permissions: + contents: read + # Required when comment-summary-in-pr is enabled + pull-requests: write + steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1 + with: + egress-policy: audit + + - name: Checkout repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Dependency Review + uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0 + with: + config-file: ${{ inputs.config-file }} + comment-summary-in-pr: ${{ inputs.comment-summary-in-pr }} + warn-only: ${{ inputs.warn-only }} + fail-on-severity: ${{ inputs.fail-on-severity }} + external-repo-token: ${{ inputs.external-repo-token }} diff --git a/.github/workflows/lint-and-format.yml b/.github/workflows/lint-and-format.yml new file mode 100644 index 0000000..c83f013 --- /dev/null +++ b/.github/workflows/lint-and-format.yml @@ -0,0 +1,70 @@ +name: Repository Lint and Format + +permissions: + contents: read + +on: + pull_request: + paths: + - "**/*.md" + - "**/*.mdx" + - "**/*.yml" + - "**/*.yaml" + - "**/*.json" + - "**/*.jsonc" + - ".github/workflows/lint-and-format.yml" + - ".markdownlint-cli2.jsonc" + - ".prettierignore" + - ".prettierrc" + - "bun.lock" + - "lefthook.yml" + - "package.json" + push: + branches: [main] + paths: + - "**/*.md" + - "**/*.mdx" + - "**/*.yml" + - "**/*.yaml" + - "**/*.json" + - "**/*.jsonc" + - ".github/workflows/lint-and-format.yml" + - ".markdownlint-cli2.jsonc" + - ".prettierignore" + - ".prettierrc" + - "bun.lock" + - "lefthook.yml" + - "package.json" + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1 + with: + egress-policy: audit + + - name: Checkout repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Setup Bun + uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 + with: + bun-version: latest + + - name: Install dependencies + run: bun install --frozen-lockfile + + - name: Check formatting with Prettier + run: bun run format:check + + - name: Lint Markdown with markdownlint + run: bun run lint:md + + - name: Lint workflow files with actionlint + uses: windlasstech/actionlint-hardened-action@043a16f2538fe7bee89d8e19bbd5292e925210e0 # v1.0.0 + with: + paths: | + .github/workflows/*.yml + .github/workflows/*.yaml diff --git a/.github/workflows/osv-scanner-full-reusable.yml b/.github/workflows/osv-scanner-full-reusable.yml new file mode 100644 index 0000000..03353f7 --- /dev/null +++ b/.github/workflows/osv-scanner-full-reusable.yml @@ -0,0 +1,96 @@ +name: OSV Scanner Full + +on: + workflow_call: + inputs: + scan-args: + description: "Additional scan arguments, one per line. Do not set --format or --output." + required: false + type: string + default: |- + --recursive + ./ + results-file-name: + description: "SARIF results file name" + required: false + type: string + default: "results.sarif" + download-artifact: + description: "Optional artifact name to download before scanning" + required: false + type: string + default: "" + ref: + description: "Optional branch, tag, or commit to scan" + required: false + type: string + default: "" + upload-sarif: + description: "Upload SARIF to code scanning when available for this repository" + required: false + type: boolean + default: true + fail-on-vuln: + description: "Fail when vulnerabilities are found" + required: false + type: boolean + default: true + checkout-submodules: + description: "Checkout git submodules" + required: false + type: boolean + default: false + +permissions: + contents: read + +jobs: + scan-full: + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1 + with: + egress-policy: audit + + - name: Checkout repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + submodules: ${{ inputs.checkout-submodules }} + ref: ${{ inputs.ref }} + + - name: Download custom artifact if specified + if: ${{ inputs.download-artifact != '' }} + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.0 + with: + name: ${{ inputs.download-artifact }} + path: ./ + + - name: Run scanner + uses: google/osv-scanner-action/osv-scanner-action@6e4298ebc4db23e847df9b2e2de2939d6f066c67 # v2.5.1 + continue-on-error: true + with: + scan-args: |- + --output=results.json + --format=json + ${{ inputs.scan-args }} + + - name: Build SARIF report + uses: google/osv-scanner-action/osv-reporter-action@6e4298ebc4db23e847df9b2e2de2939d6f066c67 # v2.5.1 + with: + scan-args: |- + --output=${{ inputs.results-file-name }} + --new=results.json + --gh-annotations=false + --fail-on-vuln=${{ inputs.fail-on-vuln }} + + - name: Upload SARIF to code scanning + if: ${{ inputs.upload-sarif }} + uses: github/codeql-action/upload-sarif@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9 + with: + sarif_file: ${{ inputs.results-file-name }} diff --git a/.github/workflows/osv-scanner-pr-reusable.yml b/.github/workflows/osv-scanner-pr-reusable.yml new file mode 100644 index 0000000..742ee83 --- /dev/null +++ b/.github/workflows/osv-scanner-pr-reusable.yml @@ -0,0 +1,98 @@ +name: OSV Scanner PR + +on: + workflow_call: + inputs: + scan-args: + description: "Additional scan arguments, one per line. Do not set --format or --output." + required: false + type: string + default: |- + --recursive + ./ + results-file-name: + description: "SARIF results file name" + required: false + type: string + default: "results.sarif" + upload-sarif: + description: "Upload SARIF to code scanning when available for this repository" + required: false + type: boolean + default: true + fail-on-vuln: + description: "Fail when the pull request introduces new vulnerabilities" + required: false + type: boolean + default: true + checkout-submodules: + description: "Checkout git submodules" + required: false + type: boolean + default: false + +permissions: + contents: read + +jobs: + scan-pr: + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1 + with: + egress-policy: audit + + - name: Checkout repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 0 + submodules: ${{ inputs.checkout-submodules }} + + - name: Checkout target branch + run: | + git checkout "$GITHUB_BASE_REF" + git submodule update --recursive + + - name: Run scanner on base branch + uses: google/osv-scanner-action/osv-scanner-action@6e4298ebc4db23e847df9b2e2de2939d6f066c67 # v2.5.1 + continue-on-error: true + with: + scan-args: |- + --format=json + --output=old-results.json + ${{ inputs.scan-args }} + + - name: Checkout current branch + run: | + git checkout -f "$GITHUB_SHA" + git submodule update --recursive + + - name: Run scanner on PR branch + uses: google/osv-scanner-action/osv-scanner-action@6e4298ebc4db23e847df9b2e2de2939d6f066c67 # v2.5.1 + continue-on-error: true + with: + scan-args: |- + --format=json + --output=new-results.json + ${{ inputs.scan-args }} + + - name: Build SARIF report + uses: google/osv-scanner-action/osv-reporter-action@6e4298ebc4db23e847df9b2e2de2939d6f066c67 # v2.5.1 + with: + scan-args: |- + --output=${{ inputs.results-file-name }} + --old=old-results.json + --new=new-results.json + --gh-annotations=true + --fail-on-vuln=${{ inputs.fail-on-vuln }} + + - name: Upload SARIF to code scanning + if: ${{ inputs.upload-sarif }} + uses: github/codeql-action/upload-sarif@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9 + with: + sarif_file: ${{ inputs.results-file-name }} diff --git a/.github/workflows/osv-scanner-smoke.yml b/.github/workflows/osv-scanner-smoke.yml new file mode 100644 index 0000000..86accd5 --- /dev/null +++ b/.github/workflows/osv-scanner-smoke.yml @@ -0,0 +1,39 @@ +name: OSV Scanner Smoke + +on: + pull_request: + paths: + - ".github/workflows/osv-scanner-*.yml" + - "README.md" + - "SECURITY.md" + - "bun.lock" + workflow_dispatch: + +permissions: + contents: read + +jobs: + pr-smoke: + if: github.event_name == 'pull_request' + permissions: + actions: read + contents: read + security-events: write + uses: ./.github/workflows/osv-scanner-pr-reusable.yml + with: + scan-args: |- + --lockfile=./bun.lock + upload-sarif: true + + full-smoke: + if: github.event_name == 'workflow_dispatch' + permissions: + actions: read + contents: read + security-events: write + uses: ./.github/workflows/osv-scanner-full-reusable.yml + with: + scan-args: |- + --lockfile=./bun.lock + upload-sarif: true + fail-on-vuln: false diff --git a/.github/workflows/scorecard-reusable.yml b/.github/workflows/scorecard-reusable.yml new file mode 100644 index 0000000..37fa55a --- /dev/null +++ b/.github/workflows/scorecard-reusable.yml @@ -0,0 +1,127 @@ +# This workflow uses actions that are not certified by GitHub. They are provided +# by a third-party and are governed by separate terms of service, privacy +# policy, and support documentation. + +name: Scorecard supply-chain security + +on: + workflow_call: + inputs: + results-file: + description: "Path to the SARIF results file" + required: false + type: string + default: "results.sarif" + results-format: + description: "Results format (sarif, json, csv, etc.)" + required: false + type: string + default: "sarif" + publish-results: + description: "Publish results to OpenSSF REST API for public repositories" + required: false + type: boolean + default: true + repo-token: + description: "Fine-grained PAT token for Branch-Protection check on public repos or private repo installation" + required: false + type: string + default: "" + file-mode: + description: "File mode for .gitattributes with export-ignore (e.g., git)" + required: false + type: string + default: "" + retention-days: + description: "Artifact retention days" + required: false + type: number + default: 5 + upload-artifact: + description: "Upload results as artifact" + required: false + type: boolean + default: true + upload-code-scanning: + description: "Upload results to GitHub code scanning dashboard" + required: false + type: boolean + default: true + +# Declare default permissions as read only. +permissions: + contents: read + issues: read + pull-requests: read + checks: read + +jobs: + analysis: + name: Scorecard analysis + runs-on: ubuntu-latest + # `publish_results: true` only works when run from the default branch. conditional can be removed if disabled. + if: github.event.repository.default_branch == github.ref_name || github.event_name == 'pull_request' + permissions: + # Needed for Scorecard to read repository contents. + contents: read + # Needed for Scorecard to query issues and pull requests. + issues: read + pull-requests: read + # Needed for Scorecard to check configured SAST tools. + checks: read + # Needed to upload the results to code-scanning dashboard. + security-events: write + # Needed to publish results and get a badge (see publish_results below). + id-token: write + + steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@e14015d583714f6e62063499dc959a02595150a1 # v2.21.1 + with: + egress-policy: audit + + - name: "Checkout code" + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: "Run analysis" + uses: ossf/scorecard-action@2d1146689b8cda280b9bc96326124645441f03bc # v2.4.4 + with: + results_file: ${{ inputs.results-file }} + results_format: ${{ inputs.results-format }} + # (Optional) "write" PAT token. Uncomment the `repo_token` line below if: + # - you want to enable the Branch-Protection check on a *public* repository, or + # - you are installing Scorecard on a *private* repository + # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action?tab=readme-ov-file#authentication-with-fine-grained-pat-optional. + repo_token: ${{ inputs.repo-token }} + + # Public repositories: + # - Publish results to OpenSSF REST API for easy access by consumers + # - Allows the repository to include the Scorecard badge. + # - See https://github.com/ossf/scorecard-action#publishing-results. + # For private repositories: + # - `publish_results` will always be set to `false`, regardless + # of the value entered here. + publish_results: ${{ inputs.publish-results }} + + # (Optional) Uncomment file_mode if you have a .gitattributes with files marked export-ignore + file_mode: ${{ inputs.file-mode }} + + # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF + # format to the repository Actions tab. + - name: "Upload artifact" + if: ${{ inputs.upload-artifact }} + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: SARIF file + path: ${{ inputs.results-file }} + retention-days: ${{ inputs.retention-days }} + + # Upload the results to GitHub's code scanning dashboard (optional). + # Commenting out will disable upload of results to your repo's Code Scanning dashboard + - name: "Upload to code-scanning" + if: ${{ inputs.upload-code-scanning }} + uses: github/codeql-action/upload-sarif@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9 + with: + sarif_file: ${{ inputs.results-file }}