|
| 1 | +name: Governance Check |
| 2 | + |
| 3 | +on: |
| 4 | + pull_request: |
| 5 | + branches: [main] |
| 6 | + |
| 7 | +permissions: |
| 8 | + contents: read |
| 9 | + |
| 10 | +jobs: |
| 11 | + governance: |
| 12 | + name: Governance validation |
| 13 | + runs-on: ubuntu-latest |
| 14 | + steps: |
| 15 | + - uses: actions/checkout@v4 |
| 16 | + |
| 17 | + - name: Check required governance files |
| 18 | + run: | |
| 19 | + STATUS="pass" |
| 20 | + for file in AGENTS.md SECURITY.md mcp-allowlist.yaml governance/policy.yaml; do |
| 21 | + if [ -f "$file" ]; then |
| 22 | + echo "✅ $file" |
| 23 | + else |
| 24 | + echo "❌ $file missing" |
| 25 | + STATUS="fail" |
| 26 | + fi |
| 27 | + done |
| 28 | + for file in .github/copilot-instructions.md; do |
| 29 | + if [ -f "$file" ]; then |
| 30 | + echo "✅ $file" |
| 31 | + else |
| 32 | + echo "⚠️ $file missing (recommended)" |
| 33 | + fi |
| 34 | + done |
| 35 | + if [ "$STATUS" = "fail" ]; then |
| 36 | + echo "::warning::Required governance files are missing" |
| 37 | + fi |
| 38 | +
|
| 39 | + - name: Validate MCP allowlist |
| 40 | + run: | |
| 41 | + python3 -c " |
| 42 | + import yaml, sys |
| 43 | + with open('mcp-allowlist.yaml') as f: |
| 44 | + data = yaml.safe_load(f) |
| 45 | + known = data.get('known', []) |
| 46 | + blocked = data.get('blocked', []) |
| 47 | + mode = data.get('enforcement', 'warn') |
| 48 | + print(f'Enforcement: {mode}') |
| 49 | + print(f'Known servers: {len(known)}') |
| 50 | + print(f'Blocked servers: {len(blocked)}') |
| 51 | + overlap = set(known) & set(blocked) |
| 52 | + if overlap: |
| 53 | + print(f'::error::Servers in both known and blocked: {overlap}') |
| 54 | + sys.exit(1) |
| 55 | + print('✅ MCP allowlist is valid') |
| 56 | + " |
| 57 | +
|
| 58 | + - name: Validate governance policy |
| 59 | + run: | |
| 60 | + python3 -c " |
| 61 | + import yaml |
| 62 | + with open('governance/policy.yaml') as f: |
| 63 | + data = yaml.safe_load(f) |
| 64 | + mode = data.get('kernel', {}).get('mode', 'unset') |
| 65 | + rings = data.get('rings', {}) |
| 66 | + blocked = data.get('blocked_patterns', []) |
| 67 | + print(f'Policy mode: {mode}') |
| 68 | + print(f'Rings defined: {len(rings)}') |
| 69 | + print(f'Blocked patterns: {len(blocked)}') |
| 70 | + print('✅ Governance policy is valid') |
| 71 | + " |
| 72 | +
|
| 73 | + - name: Check for hardcoded secrets |
| 74 | + run: | |
| 75 | + PATTERNS='(AKIA[0-9A-Z]{16}|sk-[a-zA-Z0-9]{48}|ghp_[a-zA-Z0-9]{36}|-----BEGIN (RSA |EC )?PRIVATE KEY-----)' |
| 76 | + if grep -rPn "$PATTERNS" --include="*.py" --include="*.yaml" --include="*.yml" --include="*.json" --exclude-dir=.git --exclude-dir=__pycache__ --exclude-dir=.pytest_cache . 2>/dev/null; then |
| 77 | + echo "::error::Potential hardcoded secrets detected" |
| 78 | + exit 1 |
| 79 | + else |
| 80 | + echo "✅ No hardcoded secrets detected" |
| 81 | + fi |
| 82 | +
|
| 83 | + - name: AGT toolkit verify (optional) |
| 84 | + continue-on-error: true |
| 85 | + run: | |
| 86 | + pip install "agent-governance-toolkit[full]>=3.0.0" --quiet 2>/dev/null || { echo "AGT not installable — skipping"; exit 0; } |
| 87 | + agent-governance verify --json || echo "::warning::AGT governance verify reported issues" |
| 88 | +
|
| 89 | + - name: AGT integrity check (optional) |
| 90 | + continue-on-error: true |
| 91 | + run: | |
| 92 | + which agent-governance >/dev/null 2>&1 || exit 0 |
| 93 | + agent-governance integrity --verify . || echo "::warning::Integrity check reported changes" |
0 commit comments