Repository navigation
fix(deps): patch proxy-addr (critical) and source-map-js (high) (#30) #85
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: CI | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: ci-${{ github.ref }} | |
| cancel-in-progress: ${{ github.event_name == 'pull_request' }} | |
| jobs: | |
| test: | |
| name: Test (${{ matrix.os }}) | |
| runs-on: ${{ matrix.os }} | |
| timeout-minutes: 20 | |
| # All three OSes are blocking: the native Windows failures (backlog M8-6) were fixed | |
| # in nilhemdot/agent-os#25; WSL2 remains the recommended Windows path for users. | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, macos-latest, windows-latest] | |
| defaults: | |
| run: | |
| # windows-latest defaults to pwsh, which cannot parse the bash-array | |
| # security-lint steps (R1.4); bash exists on all three runners. | |
| shell: bash | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v7 | |
| with: | |
| node-version-file: source/.nvmrc | |
| cache: 'npm' | |
| cache-dependency-path: source/package-lock.json | |
| - name: Install dependencies | |
| working-directory: source | |
| run: npm ci | |
| - name: Type check | |
| working-directory: source | |
| run: npm run typecheck | |
| - name: Lint | |
| working-directory: source | |
| run: npm run lint | |
| # blocking: M8.18 lint-debt burn-down reached 0 errors; warnings do not fail the step | |
| - name: Security lint - forbid child_process in app/features (R1.4) | |
| working-directory: source | |
| run: | | |
| # R1.4: All subprocess launches must route through runner.ts (spawnSubprocess, run, spawnStream) | |
| # Check for: from imports, require(), and dynamic import() patterns | |
| PATTERNS=( | |
| "from ['\"]child_process['\"]" | |
| "from ['\"]node:child_process['\"]" | |
| "require\(\s*['\"]child_process['\"]\s*\)" | |
| "require\(\s*['\"]node:child_process['\"]\s*\)" | |
| "import\(\s*['\"]child_process['\"]\s*\)" | |
| "import\(\s*['\"]node:child_process['\"]\s*\)" | |
| ) | |
| VIOLATIONS="" | |
| for PATTERN in "${PATTERNS[@]}"; do | |
| FOUND=$(grep -rE "$PATTERN" src/app src/features 2>/dev/null || true) | |
| if [ -n "$FOUND" ]; then | |
| VIOLATIONS="$VIOLATIONS$FOUND"$'\n' | |
| fi | |
| done | |
| if [ -n "$VIOLATIONS" ]; then | |
| echo "ERROR: Direct child_process imports found in src/app/** or src/features/** (R1.4 violation)" | |
| echo "$VIOLATIONS" | |
| echo "Use spawnSubprocess/run/spawnStream from @/lib/runner instead" | |
| exit 1 | |
| fi | |
| # Blocking job: fails CI immediately on any child_process import in app/features | |
| - name: Security lint - ESLint no-restricted-imports (R1.4, AST-level) | |
| working-directory: source | |
| run: | | |
| # AST-level backstop for the grep check above: catches aliased/re-exported | |
| # static imports and dynamic import() that text patterns can miss. | |
| # Scoped to the R1.4 rule only so the pre-existing lint backlog (M8.18) | |
| # cannot fail this job. | |
| # workspace-relative temp file: /tmp is bash-only on windows runners, | |
| # and node.exe cannot resolve it | |
| npx eslint 'src/app/**/*.{js,jsx,ts,tsx}' 'src/features/**/*.{js,jsx,ts,tsx}' \ | |
| --no-error-on-unmatched-pattern --format json > eslint-r14.json || true | |
| node -e " | |
| const results = require('./eslint-r14.json'); | |
| const hits = results.flatMap(f => f.messages | |
| .filter(m => m.ruleId === 'no-restricted-imports') | |
| .map(m => f.filePath + ':' + m.line + ' ' + m.message)); | |
| if (hits.length) { | |
| console.error('R1.4 ESLint violations:\n' + hits.join('\n')); | |
| process.exit(1); | |
| } | |
| console.log('R1.4 ESLint check clean (' + results.length + ' files)'); | |
| " | |
| rm -f eslint-r14.json | |
| - name: Run tests (includes adversarial suite m8-*.test.ts) | |
| working-directory: source | |
| run: npm run test | |
| - name: Run eval | |
| working-directory: source | |
| run: npm run eval | |
| build: | |
| name: Build | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 20 | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v7 | |
| with: | |
| node-version-file: source/.nvmrc | |
| cache: 'npm' | |
| cache-dependency-path: source/package-lock.json | |
| - name: Cache Next.js build | |
| uses: actions/cache@v6 | |
| with: | |
| path: source/.next/cache | |
| key: nextjs-${{ runner.os }}-${{ hashFiles('source/package-lock.json') }}-${{ hashFiles('source/src/**', 'source/next.config.ts') }} | |
| restore-keys: | | |
| nextjs-${{ runner.os }}-${{ hashFiles('source/package-lock.json') }}- | |
| - name: Install dependencies | |
| working-directory: source | |
| run: npm ci | |
| - name: Next.js production build | |
| working-directory: source | |
| run: npm run build | |
| env: | |
| NEXT_TELEMETRY_DISABLED: '1' | |
| security-evidence: | |
| name: Security evidence (SBOM + audit) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| # Evidence only: dependency-review.yml gates PRs and codeql.yml uploads SARIF to code scanning. | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v7 | |
| with: | |
| node-version-file: source/.nvmrc | |
| - name: Generate CycloneDX SBOM and npm audit report | |
| working-directory: source | |
| run: | | |
| mkdir -p ../security-evidence | |
| npm sbom --sbom-format cyclonedx --package-lock-only --omit dev > ../security-evidence/sbom.cdx.json | |
| # npm audit exits non-zero when it finds anything; keep the report, don't fail the job | |
| npm audit --json --package-lock-only > ../security-evidence/npm-audit.json || true | |
| # A registry/network failure yields {"error": ...} with no metadata; report it instead of crashing before the upload | |
| node -e "let a; try { a = require('../security-evidence/npm-audit.json'); } catch (e) { a = { error: String(e) }; } console.log('npm audit:', JSON.stringify(a.metadata?.vulnerabilities ?? { error: a.error ?? 'no metadata in report' }))" | tee -a "$GITHUB_STEP_SUMMARY" | |
| - name: Upload security evidence | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: security-evidence-${{ github.sha }} | |
| path: security-evidence/ | |
| retention-days: 90 |