diff --git a/.github/workflows/ci-pull-requests.yml b/.github/workflows/ci-pull-requests.yml index c97655c79b9..2f7bceba7a6 100644 --- a/.github/workflows/ci-pull-requests.yml +++ b/.github/workflows/ci-pull-requests.yml @@ -20,6 +20,7 @@ jobs: outputs: frontend_changed: ${{ steps.paths-filter.outputs.frontend == 'true' }} non_python_changed: ${{ steps.paths-filter.outputs.non-python == 'true' }} + workflows_changed: ${{ steps.paths-filter.outputs.workflows == 'true' }} commit_sha: ${{ steps.commit-sha.outputs.sha }} steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -46,6 +47,8 @@ jobs: non-python: - '**' - '!packages/@n8n/task-runner-python/**' + workflows: + - .github/** - name: Setup and Build if: steps.paths-filter.outputs.non-python == 'true' uses: ./.github/actions/setup-nodejs @@ -136,19 +139,37 @@ jobs: branch: ${{ needs.install-and-build.outputs.commit_sha }} secrets: inherit + security-checks: + name: Security Checks + needs: install-and-build + if: needs.install-and-build.outputs.workflows_changed == 'true' + uses: ./.github/workflows/ci-security.yml + with: + ref: ${{ needs.install-and-build.outputs.commit_sha }} + secrets: inherit + # This job is required by GitHub branch protection rules. # PRs cannot be merged unless this job passes. - # If you add/remove jobs that should block merging, update the 'needs' array below. + # If you add/remove jobs that should block merging, update the 'needs' array + # and the skip conditions in the 'if' block below. required-checks: name: Required Checks - needs: [install-and-build, unit-test, typecheck, lint, e2e-tests] + needs: [install-and-build, unit-test, typecheck, lint, e2e-tests, security-checks] if: always() runs-on: ubuntu-slim steps: - name: Fail if any required job failed or was skipped unexpectedly - # The non_python_changed check allows jobs to be skipped for python-only changes, - # since those jobs don't run when only python files are modified. + # Explicit checks for each job's skip conditions: + # - Non-python jobs (unit-test, typecheck, lint, e2e-tests): can skip when non_python_changed == false + # - security-checks: can skip when workflows_changed == false if: | contains(needs.*.result, 'failure') || - (needs.install-and-build.outputs.non_python_changed == 'true' && contains(needs.*.result, 'skipped')) + (needs.install-and-build.outputs.non_python_changed == 'true' && ( + needs.unit-test.result == 'skipped' || + needs.typecheck.result == 'skipped' || + needs.lint.result == 'skipped' || + needs.e2e-tests.result == 'skipped' + )) || + (needs.install-and-build.outputs.workflows_changed == 'true' && + needs.security-checks.result == 'skipped') run: exit 1 diff --git a/.github/workflows/ci-security.yml b/.github/workflows/ci-security.yml new file mode 100644 index 00000000000..48b3b596bd2 --- /dev/null +++ b/.github/workflows/ci-security.yml @@ -0,0 +1,23 @@ +name: Security Checks + +on: + workflow_call: + inputs: + ref: + description: GitHub ref to scan. + required: false + type: string + default: '' + +jobs: + poutine-scan: + name: Poutine Security Scan + uses: ./.github/workflows/security-poutine-scan-callable.yml + with: + ref: ${{ inputs.ref }} + secrets: inherit + + # Future security checks can be added here: + # - dependency-scan: + # - secret-detection: + # - container-scan: diff --git a/.github/workflows/security-poutine-scan-callable.yml b/.github/workflows/security-poutine-scan-callable.yml new file mode 100644 index 00000000000..6039aa7b666 --- /dev/null +++ b/.github/workflows/security-poutine-scan-callable.yml @@ -0,0 +1,34 @@ +name: Security - Scan GitHub Actions with Poutine + +on: + workflow_dispatch: + workflow_call: + inputs: + ref: + description: GitHub ref to scan. + required: false + type: string + default: '' + +permissions: + contents: read + security-events: write + +jobs: + poutine_scan: + name: Poutine Security Scan + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ inputs.ref }} + + - name: Run Poutine Security Scanner + uses: boostsecurityio/poutine-action@84c0a0d32e8d57ae12651222be1eb15351429228 # v0.15.2 + + - name: Upload SARIF results + uses: github/codeql-action/upload-sarif@48ab28a6f5dbc2a99bf1e0131198dd8f1df78169 # v3.28.0 + if: always() + with: + sarif_file: results.sarif diff --git a/.poutine.yml b/.poutine.yml new file mode 100644 index 00000000000..b7d72c748fd --- /dev/null +++ b/.poutine.yml @@ -0,0 +1,54 @@ +# Poutine Security Scanner Configuration +# https://github.com/boostsecurityio/poutine +# +# This file defines skip rules for known-safe patterns. +# Add new entries only after security review. + +skip: + # === SELF-HOSTED RUNNERS === + # We use Blacksmith (trusted CI provider) for self-hosted runners. + # The ubuntu-slim runner is also a trusted provider. + - rule: pr_runs_on_self_hosted + + # === UNVERIFIED ACTIONS === + # Third-party actions from non-verified GitHub Marketplace creators. + # These have been reviewed and approved for use. + # Add new actions here only after security review. + - rule: github_action_from_unverified_creator_used + purl: + - pkg:githubactions/act10ns/slack + - pkg:githubactions/anthropics/claude-code-action + - pkg:githubactions/astral-sh/setup-uv + - pkg:githubactions/chromaui/action + - pkg:githubactions/dorny/paths-filter + - pkg:githubactions/extractions/setup-just + - pkg:githubactions/fjogeleit/http-request-action + - pkg:githubactions/isbang/compose-action + - pkg:githubactions/lironer/bundlemon-action + - pkg:githubactions/ncipollo/release-action + - pkg:githubactions/peter-evans/create-or-update-comment + - pkg:githubactions/peter-evans/create-pull-request + - pkg:githubactions/pnpm/action-setup + - pkg:githubactions/rharkor/caching-for-turbo + - pkg:githubactions/tomi/paths-filter-action + - pkg:githubactions/useblacksmith/setup-docker-builder + + # === TEMPORARY SKIPS === + # These findings need to be addressed in a follow-up ticket. + # Do not add new entries to this section. + + # TODO: Fix injection vulnerability - use environment variable instead of + # direct interpolation of github.event.inputs.dataset + - rule: injection + path: + - .github/workflows/ci-evals.yml + + # TODO: Review untrusted checkout execution patterns. + # These workflows run local actions or package managers after checking out + # untrusted PR code, which is a potential security risk. + - rule: untrusted_checkout_exec + path: + - .github/workflows/sbom-generation-callable.yml + - .github/workflows/linting-reusable.yml + - .github/workflows/test-workflows-callable.yml + - .github/workflows/units-tests-reusable.yml