chore(release): v0.31.0 (M4 API/format freeze) (#156) #386
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Security scan | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| workflow_dispatch: | |
| schedule: | |
| # Weekly Monday 06:00 UTC catches new CVEs in dependencies and | |
| # any drift in the scan rules. | |
| - cron: "0 6 * * 1" | |
| permissions: | |
| contents: read | |
| jobs: | |
| secrets: | |
| name: Secret scan (gitleaks) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| - name: Install gitleaks | |
| run: | | |
| GITLEAKS_VERSION="8.21.0" | |
| curl -sSfL "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz" \ | |
| -o /tmp/gitleaks.tgz | |
| tar -xzf /tmp/gitleaks.tgz -C /tmp gitleaks | |
| sudo install -m 0755 /tmp/gitleaks /usr/local/bin/gitleaks | |
| - name: Run gitleaks | |
| run: gitleaks detect --no-banner --redact --verbose | |
| planning-leak: | |
| name: Planning-doc identifier leak | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Scan for internal identifiers | |
| shell: bash | |
| run: | | |
| set -uo pipefail | |
| # Identifier patterns belonging to the private planning repo | |
| # that must never appear in public tracked files: | |
| # Y-N (Y-1, Y-2, Y-3a, Y-7b) | |
| # Y-N.N (Y-1.5) | |
| # B-XN (B-D1, B-D2) | |
| # "Codex YN" review references | |
| # M-N (M-1 cross-product playbook, M-2 dispute appeal) | |
| # The literal private-repo name | |
| # The workflow file itself is excluded from the scan because | |
| # the documented examples and regex literal above would | |
| # otherwise self-match. | |
| MATCHES=$(git ls-files | grep -v '^\.github/workflows/' | xargs grep -nE \ | |
| "Y-[0-9]+[a-z]?|Y-[0-9]+\.[0-9]+|B-[A-Z][0-9]+|Codex Y[0-9]+|M-[0-9]+[a-z]?|basou-planning" \ | |
| 2>/dev/null || true) | |
| if [ -n "$MATCHES" ]; then | |
| echo "::error::Planning-doc identifier leak detected" | |
| echo "$MATCHES" | |
| exit 1 | |
| fi | |
| echo "No planning-doc identifier leaks found." | |
| absolute-path: | |
| name: Absolute path leak | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Scan for real /Users/<username> paths | |
| shell: bash | |
| run: | | |
| set -uo pipefail | |
| # Path-sanitizer JSDoc documents these intentional placeholders. | |
| # Anything outside this set is a real absolute-path leak. | |
| ALLOWED='^/Users/(u|<u>|<user>|<you>|example|secret)$' | |
| REAL_PATHS=$(git ls-files | xargs grep -hoE "/Users/[a-zA-Z][a-zA-Z0-9_-]+" 2>/dev/null \ | |
| | sort -u | grep -vE "$ALLOWED" || true) | |
| if [ -n "$REAL_PATHS" ]; then | |
| echo "::error::Real /Users/<username> path detected (sanitize to a placeholder)" | |
| echo "$REAL_PATHS" | |
| exit 1 | |
| fi | |
| echo "No real absolute-path leaks found." | |
| step-n-watch: | |
| name: Step N cleanup watch (informational) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Count Step N residue | |
| shell: bash | |
| run: | | |
| set -uo pipefail | |
| # Residual "Step N" references in deep source / test comments | |
| # are tracked for follow-up cleanup. They are informational | |
| # only and do not block CI today. | |
| FILES=$(git ls-files | xargs grep -lE "Step [0-9]+" 2>/dev/null || true) | |
| if [ -n "$FILES" ]; then | |
| COUNT=$(echo "$FILES" | wc -l | tr -d ' ') | |
| echo "::warning::${COUNT} file(s) still contain Step N references" | |
| echo "$FILES" | |
| else | |
| echo "No Step N references remain." | |
| fi | |
| commit-message-ascii: | |
| name: Commit message ASCII check | |
| # PR is the only path to main (protect-main ruleset). Push/schedule/ | |
| # dispatch events cannot provide a meaningful base..head range, and | |
| # pre-existing non-ASCII commits in history must not be rescanned | |
| # (informational retention, not rewritten). Gating on pull_request | |
| # both prevents new violations and avoids retroactive failures. | |
| if: github.event_name == 'pull_request' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| # Need full history so git log base..head can resolve commits | |
| # between the PR base and head; the default depth=1 would fail. | |
| fetch-depth: 0 | |
| - name: Scan new commits for non-ASCII bytes | |
| shell: bash | |
| env: | |
| BASE_SHA: ${{ github.event.pull_request.base.sha }} | |
| HEAD_SHA: ${{ github.event.pull_request.head.sha }} | |
| run: | | |
| set -uo pipefail | |
| # CONTRIBUTING.md "English-only convention" requires commit | |
| # messages (subject + body) to be ASCII. The strict range is | |
| # 0x00-0x7F; tab and newline are inside that range and remain | |
| # allowed (the negated class below preserves them). | |
| # Merge commits are excluded: their auto-generated subjects | |
| # are ASCII by construction and merge bodies are not author- | |
| # written content. | |
| # perl is preinstalled on ubuntu-latest and macOS, so the same | |
| # check can be dry-run locally (BSD grep on macOS lacks -P). | |
| FAIL=0 | |
| while IFS= read -r -d '' SHA; do | |
| MSG=$(git log -1 --format=%B "$SHA") | |
| SUBJECT=$(git log -1 --format=%s "$SHA") | |
| HITS=$(printf '%s\n' "$MSG" | LC_ALL=C perl -ne 'print "$.:$_" if /[^\x00-\x7F]/') | |
| if [ -n "$HITS" ]; then | |
| FAIL=1 | |
| echo "::error::Non-ASCII bytes in commit ${SHA}: ${SUBJECT}" | |
| echo "$HITS" | sed 's/^/ /' | |
| fi | |
| done < <(git log --no-merges -z --format=%H "${BASE_SHA}..${HEAD_SHA}") | |
| if [ "$FAIL" -ne 0 ]; then | |
| echo "::error::One or more new commits contain non-ASCII bytes. See CONTRIBUTING.md 'English-only convention'." | |
| exit 1 | |
| fi | |
| echo "All new commits are ASCII-only." | |
| audit: | |
| name: Dependency vulnerability audit | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: pnpm/action-setup@v6 | |
| # pnpm version is read from the `packageManager` field of the | |
| # workspace root package.json; the action errors out if both | |
| # are specified. | |
| - uses: actions/setup-node@v6 | |
| with: | |
| node-version: "20" | |
| cache: pnpm | |
| - run: pnpm install --frozen-lockfile | |
| - name: Run pnpm audit (production deps only) | |
| # devDependencies (vitest, biome, tsup, etc.) only run on a | |
| # developer's machine and are not part of the published | |
| # tarball, so vulnerabilities there are not a runtime risk to | |
| # consumers. Track dev-dep advisories via Dependabot if | |
| # needed; here we gate on production-graph vulnerabilities. | |
| run: pnpm audit --audit-level=moderate --prod |