diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8ace27e..53f4d3c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,6 +16,14 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +env: + # Same-repo PRs have a writable token; fork PRs get a read-only token that + # cannot upload SARIF. Computed once here and reused by every upload step + # instead of repeating the same three-line condition in each job. + UPLOAD_SARIF: >- + ${{ github.event_name != 'pull_request' || + github.event.pull_request.head.repo.full_name == github.repository }} + jobs: build: runs-on: ubuntu-latest @@ -25,6 +33,11 @@ jobs: contents: read security-events: write steps: + - name: Harden Runner + uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0 + with: + egress-policy: audit + - name: Checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: @@ -62,10 +75,7 @@ jobs: --report-path gitleaks.sarif - name: Upload gitleaks SARIF - if: > - always() && - (github.event_name != 'pull_request' || - github.event.pull_request.head.repo.full_name == github.repository) + if: always() && env.UPLOAD_SARIF == 'true' uses: github/codeql-action/upload-sarif@f205ea1c3313d32999d8d6a48b4f6530d4437b38 # v4 with: sarif_file: gitleaks.sarif @@ -91,6 +101,11 @@ jobs: permissions: contents: read steps: + - name: Harden Runner + uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0 + with: + egress-policy: audit + - name: Checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: @@ -107,15 +122,8 @@ jobs: echo "::error::$cmd is missing" exit 1 fi + "$cmd" --version 2>&1 | head -1 done - python3 --version - node --version - npm --version - gh --version - opencode --version - curl --version | head -1 - jq --version - claude --version scan: runs-on: ubuntu-latest @@ -126,6 +134,11 @@ jobs: contents: read security-events: write steps: + - name: Harden Runner + uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0 + with: + egress-policy: audit + - name: Checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: @@ -155,10 +168,7 @@ jobs: cache: false - name: Upload Trivy image scan results - if: > - always() && - (github.event_name != 'pull_request' || - github.event.pull_request.head.repo.full_name == github.repository) + if: always() && env.UPLOAD_SARIF == 'true' uses: github/codeql-action/upload-sarif@f205ea1c3313d32999d8d6a48b4f6530d4437b38 # v4 with: sarif_file: 'trivy-results.sarif' @@ -196,10 +206,7 @@ jobs: cache: false - name: Upload Trivy filesystem scan results - if: > - always() && - (github.event_name != 'pull_request' || - github.event.pull_request.head.repo.full_name == github.repository) + if: always() && env.UPLOAD_SARIF == 'true' uses: github/codeql-action/upload-sarif@f205ea1c3313d32999d8d6a48b4f6530d4437b38 # v4 with: sarif_file: 'trivy-fs-results.sarif' diff --git a/AGENTS.md b/AGENTS.md index 8f0798f..aaac8c0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -57,6 +57,10 @@ current via daily PRs. `# hadolint ignore=DLXXXX` with a justification comment. - Keep the package list minimal — features (not the Dockerfile) install language runtimes and CLI tools. +- The `linux-libc-dev` purge and Pebble binary removal are deliberate CVE + fixes (see inline comments) — the `scan` job gates on CRITICAL Trivy + findings, so don't drop these lines without re-verifying the scan stays + clean. ## CI pipeline (`ci.yml`) @@ -119,3 +123,14 @@ npx @devcontainers/cli exec --workspace-folder . -- bash -c ' - **Volume mount target.** The Claude Code volume is mounted at `/home/vscode/.claude` (matching `remoteUser: vscode`). If the remote user ever changes, the mount target must be updated to match. +- **Claude Code CLI is installed by the devcontainer feature** + (`ghcr.io/anthropics/devcontainer-features/claude-code:1.0`), which runs as + root during the image build and places the package under the nvm global + `node_modules` dir. Never add `npm install -g @anthropic-ai/claude-code` (or + re-run its `install.cjs`) to `updateContentCommand` — that step runs as user + `vscode` and fails with `EACCES` trying to rename the root-owned package + (this broke CI in PR #51). `updateContentCommand` should only install + user-writable packages (currently just `opencode-ai`). +- **CI runs on Copilot/bot-authored PR branches often show `action_required`** + and won't execute automatically. Approve them in the GitHub UI, or run + `gh run rerun --repo idvoretskyi/dev` to trigger them.