diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index ae3a5aa..17c0d40 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -342,6 +342,82 @@ jobs: apps/pwa/playwright-report/ retention-days: 14 + security: + runs-on: ubuntu-latest + needs: [changes] + if: github.event_name == 'pull_request' + # Runs in PARALLEL with fast-checks (D-15). gitleaks always runs (D-12 — secrets + # can appear in doc-only commits). pnpm audit + pnpm outdated run only on + # code/lockfile-changing PRs (step-level if: keeps the job always-running). + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # Required: base.sha must be locally available for git log range (Pitfall 3) + + # ── Probe PR base/head SHA with merge-base fallback (A2 / OQ-1) ────────── + # github.event.pull_request.base.sha may be empty on some Gitea versions. + # If so, fall back to git merge-base to compute the real branch-point SHA. + - name: Probe PR base/head SHA + run: | + set -euo pipefail + echo "Event base.sha: ${{ github.event.pull_request.base.sha }}" + echo "Event head.sha: ${{ github.event.pull_request.head.sha }}" + BASE_SHA="${{ github.event.pull_request.base.sha }}" + HEAD_SHA="${{ github.event.pull_request.head.sha }}" + if [ -z "$BASE_SHA" ]; then + echo "base.sha empty — computing merge-base fallback" + BASE_SHA=$(git merge-base "$(git rev-parse origin/${{ github.base_ref }})" HEAD) + echo "Computed BASE_SHA via merge-base: $BASE_SHA" + fi + echo "BASE_SHA=$BASE_SHA" >> "$GITHUB_ENV" + echo "HEAD_SHA=$HEAD_SHA" >> "$GITHUB_ENV" + + # ── Gitleaks (always runs, D-12) ───────────────────────────────────────── + - name: Install gitleaks + run: | + set -euo pipefail + VERSION=8.30.1 + curl -sL \ + "https://github.com/gitleaks/gitleaks/releases/download/v${VERSION}/gitleaks_${VERSION}_linux_x64.tar.gz" \ + | tar -xz gitleaks + chmod +x gitleaks + mv gitleaks /usr/local/bin/gitleaks + + - name: Secret scan (PR diff, blocking) + run: | + set -euo pipefail + gitleaks git \ + --log-opts="--no-merges ${BASE_SHA}..${HEAD_SHA}" \ + --config .gitleaks.toml \ + --baseline-path scripts/gitleaks-baseline.json \ + --report-path /tmp/gitleaks-pr-report.json \ + --exit-code 1 + + # ── pnpm audit + outdated (code-change PRs only, D-12) ─────────────────── + # actions/cache@v4 intentionally omitted — same reasoning as fast-checks job (D-PROBE-04). + + - uses: actions/setup-node@v4 + if: needs.changes.outputs.code == 'true' + with: + node-version: '22' + + - name: Enable pnpm + if: needs.changes.outputs.code == 'true' + run: corepack enable pnpm + + - name: Install dependencies + if: needs.changes.outputs.code == 'true' + run: pnpm install --frozen-lockfile + + - name: Dependency audit (blocking on High+Critical) + if: needs.changes.outputs.code == 'true' + run: node scripts/check-audit.mjs + + - name: Dependency outdated report (advisory only) + if: needs.changes.outputs.code == 'true' + run: node scripts/check-outdated.mjs + # Always exits 0 — log output only, never gates (D-06) + gate: runs-on: ubuntu-latest needs: [fast-checks, changes, api, harness]