Merge pull request #26 from aegisora-ai/release/2.1-enterprise-authority #26
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 Readiness | |
| on: | |
| push: | |
| branches: | |
| - main | |
| - "hardening/**" | |
| pull_request: | |
| branches: | |
| - main | |
| permissions: | |
| contents: read | |
| jobs: | |
| security-readiness: | |
| name: Validate Security Readiness | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 11.20.0 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 24 | |
| cache: pnpm | |
| cache-dependency-path: pnpm-lock.yaml | |
| - name: Verify toolchain | |
| id: toolchain | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| node --version | |
| npm --version | |
| pnpm --version | |
| - name: Install dependencies | |
| id: install | |
| run: pnpm install --frozen-lockfile | |
| - name: Validate security artifacts | |
| id: artifacts | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| required_files=( | |
| "SECURITY.md" | |
| "docs/security/THREAT_MODEL.md" | |
| "docs/security/SECURITY_CONTROLS.md" | |
| "docs/security/INCIDENT_RESPONSE.md" | |
| "docs/security/VULNERABILITY_MANAGEMENT.md" | |
| "docs/security/COMPLIANCE_READINESS.md" | |
| "docs/security/EVIDENCE_REGISTER.md" | |
| "docs/security/SECURITY_RISK_REGISTER.md" | |
| "docs/security/ACCESS_REVIEW.md" | |
| ".github/CODEOWNERS" | |
| ) | |
| for file in "${required_files[@]}"; do | |
| test -s "$file" | |
| echo "PASS $file" | |
| done | |
| - name: Validate security claims | |
| id: claims | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| grep -q "ALLOW" docs/security/THREAT_MODEL.md | |
| grep -q "BLOCK" docs/security/THREAT_MODEL.md | |
| grep -q "ESCALATE" docs/security/THREAT_MODEL.md | |
| grep -q "T-12 Runtime Enforcement Bypass" docs/security/THREAT_MODEL.md | |
| grep -q "SEC-001" docs/security/SECURITY_CONTROLS.md | |
| grep -q "SEC-013" docs/security/SECURITY_CONTROLS.md | |
| grep -q "SEC-016" docs/security/SECURITY_CONTROLS.md | |
| grep -q "RISK-001" docs/security/SECURITY_RISK_REGISTER.md | |
| grep -q "RISK-010" docs/security/SECURITY_RISK_REGISTER.md | |
| grep -q "Quarterly" docs/security/ACCESS_REVIEW.md | |
| echo "PASS security claim validation" | |
| - name: Build | |
| id: build | |
| run: pnpm build | |
| - name: Typecheck | |
| id: typecheck | |
| run: pnpm typecheck | |
| - name: Test | |
| id: test | |
| run: pnpm test | |
| - name: Audit published tarballs | |
| id: tarballs | |
| shell: bash | |
| env: | |
| AUDIT_SCRIPT: | | |
| const fs = require("fs"); | |
| const manifest = process.argv[1]; | |
| const pkg = JSON.parse(fs.readFileSync(manifest, "utf8")); | |
| if (pkg.version !== "2.0.0") { | |
| throw new Error(`Expected 2.0.0, got ${pkg.version}`); | |
| } | |
| const keys = ["name", "version", "license", "repository", "bugs", "homepage", "exports", "types"]; | |
| for (const key of keys) { | |
| if (!pkg[key]) throw new Error(`${manifest}: missing ${key}`); | |
| } | |
| if (!Array.isArray(pkg.files) || pkg.files.length === 0) { | |
| throw new Error(`${manifest}: missing files whitelist`); | |
| } | |
| const sections = ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"]; | |
| for (const section of sections) { | |
| if (!pkg[section]) continue; | |
| for (const [name, value] of Object.entries(pkg[section])) { | |
| if (String(value).startsWith("workspace:")) { | |
| throw new Error(`${manifest}: workspace protocol leaked in ${section}/${name}`); | |
| } | |
| } | |
| } | |
| console.log(`PASS ${pkg.name}`); | |
| run: | | |
| set -euo pipefail | |
| packages=( | |
| "audit" | |
| "core" | |
| "observability" | |
| "plugins" | |
| "policy-engine" | |
| "runtime" | |
| "sdk" | |
| "security-engine" | |
| "storage" | |
| ) | |
| temp_root="$(mktemp -d)" | |
| trap 'rm -rf "$temp_root"' EXIT | |
| for package in "${packages[@]}"; do | |
| dir="core/packages/$package" | |
| dest="$temp_root/$package" | |
| mkdir -p "$dest" | |
| echo "Packing $dir" | |
| ( | |
| cd "$dir" | |
| pnpm pack --pack-destination "$dest" >/dev/null | |
| ) | |
| tarball="$(find "$dest" -maxdepth 1 -type f -name '*.tgz' | head -n 1)" | |
| test -n "$tarball" | |
| extract="$dest/extract" | |
| mkdir -p "$extract" | |
| tar -xzf "$tarball" -C "$extract" | |
| manifest="$extract/package/package.json" | |
| test -s "$manifest" | |
| node -e "$AUDIT_SCRIPT" "$manifest" | |
| done | |
| echo "PASS all public package tarballs" | |
| - name: Scan repository for obvious secrets | |
| id: secrets | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| if grep -R -n -I -E \ | |
| "(BEGIN (RSA|OPENSSH|EC|DSA) PRIVATE KEY|AKIA[0-9A-Z]{16}|ghp_[A-Za-z0-9]{20,}|npm_[A-Za-z0-9]{20,})" \ | |
| --exclude-dir=.git \ | |
| --exclude-dir=node_modules \ | |
| --exclude='*.lock' \ | |
| .; then | |
| echo "FAIL possible secret material detected" | |
| exit 1 | |
| fi | |
| echo "PASS obvious secret scan" | |
| - name: Validate CODEOWNERS | |
| id: codeowners | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| test -s .github/CODEOWNERS | |
| grep -q "@ozereray" .github/CODEOWNERS | |
| if grep -q "@aegisora-ai/security" .github/CODEOWNERS; then | |
| echo "FAIL placeholder security team found" | |
| exit 1 | |
| fi | |
| if grep -q "@aegisora-ai/release" .github/CODEOWNERS; then | |
| echo "FAIL placeholder release team found" | |
| exit 1 | |
| fi | |
| echo "PASS CODEOWNERS" | |
| - name: Generate L3 security evidence | |
| id: evidence | |
| if: always() | |
| shell: bash | |
| env: | |
| EV_TOOLCHAIN: ${{ steps.toolchain.outcome }} | |
| EV_INSTALL: ${{ steps.install.outcome }} | |
| EV_ARTIFACTS: ${{ steps.artifacts.outcome }} | |
| EV_CLAIMS: ${{ steps.claims.outcome }} | |
| EV_BUILD: ${{ steps.build.outcome }} | |
| EV_TYPECHECK: ${{ steps.typecheck.outcome }} | |
| EV_TEST: ${{ steps.test.outcome }} | |
| EV_TARBALLS: ${{ steps.tarballs.outcome }} | |
| EV_SECRETS: ${{ steps.secrets.outcome }} | |
| EV_CODEOWNERS: ${{ steps.codeowners.outcome }} | |
| EV_GIT_DIFF: success | |
| run: | | |
| set -euo pipefail | |
| node scripts/security/generate-evidence.mjs | |
| - name: Upload security evidence | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: aegisora-security-readiness-evidence-${{ github.run_id }} | |
| path: security-evidence/ | |
| if-no-files-found: error | |
| retention-days: 90 | |
| - name: Final security evidence gate | |
| if: always() | |
| shell: bash | |
| env: | |
| GATE_SCRIPT: | | |
| const fs = require("fs"); | |
| const manifest = JSON.parse(fs.readFileSync("security-evidence/manifest.json", "utf8")); | |
| if (manifest.overall_status !== "PASS") { | |
| console.error("FAIL security evidence gate"); | |
| process.exit(1); | |
| } | |
| console.log("PASS security evidence gate"); | |
| run: | | |
| set -euo pipefail | |
| test -s security-evidence/manifest.json | |
| node -e "$GATE_SCRIPT" | |
| - name: Validate git diff | |
| id: git_diff | |
| if: always() | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| git diff --check | |
| echo "PASS git diff check" |