Add scheduled supply-chain watch workflow

This commit is contained in:
Affaan Mustafa
2026-05-15 16:35:25 -04:00
committed by Affaan Mustafa
parent 6887f2952d
commit 6951b8d5d2
4 changed files with 154 additions and 8 deletions

View File

@@ -0,0 +1,57 @@
name: Supply-Chain Watch
on:
schedule:
- cron: '17 */6 * * *'
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: false
permissions:
contents: read
jobs:
ioc-watch:
name: IOC watch
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version: '20.x'
- name: Install dependencies without lifecycle scripts
run: npm ci --ignore-scripts
- name: Verify registry signatures and advisories
run: |
npm audit signatures
npm audit --audit-level=high
- name: Validate IOC scanner fixtures
run: node tests/ci/scan-supply-chain-iocs.test.js
- name: Generate IOC report
run: |
mkdir -p artifacts
node scripts/ci/scan-supply-chain-iocs.js --json > artifacts/supply-chain-ioc-report.json
- name: Validate workflow hardening rules
run: node scripts/ci/validate-workflow-security.js
- name: Upload IOC report
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: supply-chain-ioc-report
path: artifacts/supply-chain-ioc-report.json
retention-days: 14

View File

@@ -13,7 +13,7 @@ clean checkout.
| Issue queue | Current | 0 open issues across checked repos |
| Discussions | Current | 58 main-repo discussions; 0 need maintainer touch; 0 answerable discussions missing accepted answers |
| Local worktree | Current with caveat | `main...origin/main`; unrelated `?? docs/drafts/` ignored |
| Security sweep | Current with follow-up | IOC scan, audits, and package-manager hardening completed |
| Security sweep | Current with follow-up | IOC scan, audits, package-manager hardening, and scheduled watch workflow completed |
| Linear roadmap | Current with follow-up | `ECC Platform Roadmap`, ITO-44 through ITO-59 |
| ECC 2.0 publication | Not complete | Release, npm, plugin, and announcement gates pending |
| AgentShield enterprise depth | In progress | AgentShield #86 merged; live IOC loop still pending |
@@ -29,7 +29,7 @@ Run these from `everything-claude-code` unless a row says otherwise.
| Platform audit | `node scripts/platform-audit.js --json --allow-untracked docs/drafts/` | `ready: true`; open PRs 0/20; open issues 0/20; discussions needing maintainer touch 0; answerable discussions missing accepted answers 0; blocking dirty files 0 |
| Discussion audit | `node scripts/discussion-audit.js --json --repo affaan-m/everything-claude-code` | `ready: true`; 58 discussions sampled; 0 need maintainer touch; 0 answerable discussions missing accepted answers |
| Main repo status | `git status --short --branch` | `## main...origin/main`; `?? docs/drafts/` remains unrelated |
| Main commit | `git rev-parse HEAD` | `c0f8c3bc813360f29e9f2b66bcae7e977cd03327` |
| Main commit | `git rev-parse HEAD` | `6887f2952d193cff10b3eb79af7765555d8ca9f5` |
| Main repo PRs/issues | GitHub connector and `gh` readback | 0 open PRs; 0 open issues |
| AgentShield PRs/issues | GitHub connector and `gh` readback | 0 open PRs; 0 open issues |
| ECC Tools PRs/issues | Local `gh pr list` and `gh issue list` | 0 open PRs; 0 open issues |
@@ -37,7 +37,8 @@ Run these from `everything-claude-code` unless a row says otherwise.
| Supply-chain IOC scan | `node scripts/ci/scan-supply-chain-iocs.js --root <ECC-workspace> --home` | Passed; 1241 files inspected |
| IOC unit tests | `node tests/ci/scan-supply-chain-iocs.test.js` | 15/15 passed |
| Dead-man switch persistence sweep | Process, LaunchAgent, and known payload filename sweep for Mini Shai-Hulud markers | No matches |
| Workflow security gate | `node scripts/ci/validate-workflow-security.js` | Passed; 7 workflow files inspected |
| Workflow security gate | `node scripts/ci/validate-workflow-security.js` | Passed; 8 workflow files inspected |
| Supply-chain watch workflow | `.github/workflows/supply-chain-watch.yml` | Scheduled every 6 hours; emits `supply-chain-ioc-report.json` |
| npm signatures and audit | `npm audit signatures && npm audit --audit-level=moderate` in main, AgentShield, ECC Tools | 0 vulnerabilities in each checked package |
## Prompt-To-Artifact Checklist
@@ -94,9 +95,9 @@ Still-open lanes:
should not spend more time closing nonexistent PRs/issues.
- The discussion queue is current and repeatable through `discussion:audit`.
ITO-59 remains open only for recurring Linear/status synchronization.
- The Mini Shai-Hulud/TanStack protection pass is strong enough for current
local protection, but ITO-57 remains open until incident response and IOC
updates become a durable workflow.
- The Mini Shai-Hulud/TanStack protection pass now has a durable scheduled
watch workflow. ITO-57 remains open for advisory-source refresh automation
and Linear status synchronization.
- The release is still blocked by publication, package, plugin, billing, and
announcement gates. Passing `platform:audit` alone is not proof that ECC 2.0
is publishable.
@@ -107,7 +108,7 @@ Still-open lanes:
markdown artifact.
2. Run `platform:audit` and `discussion:audit` from the final release commit
before recording publication evidence.
3. Continue ITO-57 by turning emergency hardening into documented incident
response and scanner update workflow.
3. Continue ITO-57 by adding advisory-source refresh automation and Linear
status synchronization for the scheduled supply-chain watch.
4. Resume release/publication lanes ITO-45, ITO-46, and ITO-56 only after the
readiness dashboard can be refreshed from commands.

View File

@@ -81,6 +81,21 @@ node tests/run-all.js
If a search hit appears only in documentation examples, note it in the release
evidence but do not rotate credentials for a docs-only reference.
## Durable Watch Workflow
ECC also runs `.github/workflows/supply-chain-watch.yml` every six hours and on
manual dispatch. The workflow is read-only, disables checkout credential
persistence, installs with `npm ci --ignore-scripts`, verifies npm registry
signatures, runs the IOC scanner fixtures, emits
`supply-chain-ioc-report.json`, and re-validates GitHub Actions hardening rules.
Treat a failed scheduled watch as a release blocker until an operator confirms
whether the failure is a newly reported advisory, a stale scanner fixture, a
registry-signature issue, or a workflow hardening regression. If the scanner
needs new indicators, update `scripts/ci/scan-supply-chain-iocs.js`, add fixture
coverage in `tests/ci/scan-supply-chain-iocs.test.js`, refresh this runbook, and
attach the latest JSON artifact to the release evidence.
## Immediate Response
If ECC or a maintainer machine installed a known-bad package version:

View File

@@ -0,0 +1,73 @@
#!/usr/bin/env node
/**
* Validate the scheduled supply-chain watch workflow contract.
*/
const assert = require('assert');
const fs = require('fs');
const path = require('path');
const WORKFLOW_PATH = path.join(
__dirname,
'..',
'..',
'.github',
'workflows',
'supply-chain-watch.yml',
);
function test(name, fn) {
try {
fn();
console.log(`${name}`);
return true;
} catch (error) {
console.log(`${name}`);
console.log(` Error: ${error.message}`);
return false;
}
}
function run() {
console.log('\n=== Testing supply-chain watch workflow ===\n');
const source = fs.readFileSync(WORKFLOW_PATH, 'utf8');
let passed = 0;
let failed = 0;
if (test('runs on schedule and manual dispatch', () => {
assert.match(source, /schedule:\r?\n\s+- cron: '17 \*\/6 \* \* \*'/);
assert.match(source, /workflow_dispatch:/);
})) passed++; else failed++;
if (test('uses read-only permissions and non-persisting checkout credentials', () => {
assert.match(source, /permissions:\r?\n\s+contents: read/);
assert.doesNotMatch(source, /^\s+[A-Za-z-]+:\s*write\b/m);
assert.match(source, /uses: actions\/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd/);
assert.match(source, /persist-credentials: false/);
assert.doesNotMatch(source, /id-token:\s*write/);
assert.doesNotMatch(source, /actions\/cache@/);
})) passed++; else failed++;
if (test('installs without lifecycle scripts and verifies registry signatures', () => {
assert.match(source, /npm ci --ignore-scripts/);
assert.match(source, /npm audit signatures/);
assert.match(source, /npm audit --audit-level=high/);
})) passed++; else failed++;
if (test('runs IOC fixtures, emits JSON report, and uploads the artifact', () => {
assert.match(source, /node tests\/ci\/scan-supply-chain-iocs\.test\.js/);
assert.match(source, /node scripts\/ci\/scan-supply-chain-iocs\.js --json > artifacts\/supply-chain-ioc-report\.json/);
assert.match(source, /node scripts\/ci\/validate-workflow-security\.js/);
assert.match(source, /uses: actions\/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a/);
assert.match(source, /name: supply-chain-ioc-report/);
assert.match(source, /retention-days: 14/);
})) passed++; else failed++;
console.log(`\nPassed: ${passed}`);
console.log(`Failed: ${failed}`);
process.exit(failed > 0 ? 1 : 0);
}
run();