aiwg 2026.5.3 → 2026.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/CLAUDE.md +2 -1
  2. package/README.md +11 -0
  3. package/agentic/code/addons/aiwg-utils/manifest.json +2 -1
  4. package/agentic/code/frameworks/sdlc-complete/templates/aiwg-sections/02b-discover-first.md +42 -0
  5. package/agentic/code/frameworks/sdlc-complete/templates/aiwg-sections/manifest.json +7 -0
  6. package/agentic/code/frameworks/sdlc-complete/templates/copilot/copilot-instructions.md.aiwg-template +11 -0
  7. package/agentic/code/frameworks/security-engineering/manifest.json +2 -2
  8. package/agentic/code/frameworks/security-engineering/rules/RULES-INDEX.md +19 -4
  9. package/agentic/code/frameworks/security-engineering/rules/ci-action-pinning.md +145 -0
  10. package/agentic/code/frameworks/security-engineering/rules/dependency-source-policy.md +177 -0
  11. package/agentic/code/frameworks/security-engineering/skills/bun-release-age-gate/SKILL.md +193 -0
  12. package/agentic/code/frameworks/security-engineering/skills/ci-workflow-audit/SKILL.md +266 -0
  13. package/agentic/code/frameworks/security-engineering/skills/pnpm-release-age-gate/SKILL.md +218 -0
  14. package/agentic/code/frameworks/security-engineering/skills/security-engineering-quickref/SKILL.md +9 -0
  15. package/agentic/code/frameworks/security-engineering/skills/yarn-release-age-gate/SKILL.md +183 -0
  16. package/agentic/code/frameworks/security-engineering/templates/README.md +35 -0
  17. package/agentic/code/frameworks/security-engineering/templates/security-md-template.md +192 -0
  18. package/dist/src/cli/handlers/use.d.ts.map +1 -1
  19. package/dist/src/cli/handlers/use.js +21 -0
  20. package/dist/src/cli/handlers/use.js.map +1 -1
  21. package/dist/src/cli/project-isolation/detect.d.ts +8 -0
  22. package/dist/src/cli/project-isolation/detect.d.ts.map +1 -0
  23. package/dist/src/cli/project-isolation/detect.js +70 -0
  24. package/dist/src/cli/project-isolation/detect.js.map +1 -0
  25. package/dist/src/cli/project-isolation/index.d.ts +6 -0
  26. package/dist/src/cli/project-isolation/index.d.ts.map +1 -0
  27. package/dist/src/cli/project-isolation/index.js +7 -0
  28. package/dist/src/cli/project-isolation/index.js.map +1 -0
  29. package/dist/src/cli/project-isolation/signals.d.ts +4 -0
  30. package/dist/src/cli/project-isolation/signals.d.ts.map +1 -0
  31. package/dist/src/cli/project-isolation/signals.js +17 -0
  32. package/dist/src/cli/project-isolation/signals.js.map +1 -0
  33. package/dist/src/cli/project-isolation/warning.d.ts +20 -0
  34. package/dist/src/cli/project-isolation/warning.d.ts.map +1 -0
  35. package/dist/src/cli/project-isolation/warning.js +102 -0
  36. package/dist/src/cli/project-isolation/warning.js.map +1 -0
  37. package/dist/src/mcp/cli.mjs +6 -0
  38. package/dist/src/mcp/helpers.mjs +232 -0
  39. package/dist/src/mcp/server.mjs +41 -10
  40. package/dist/src/mcp/tools/command-run.mjs +101 -0
  41. package/dist/src/mcp/tools/discovery.mjs +291 -0
  42. package/dist/src/mcp/tools/subsystems.mjs +609 -0
  43. package/package.json +1 -1
  44. package/tools/agents/providers/hermes.mjs +338 -41
  45. package/tools/verify-hermes-citations.mjs +190 -0
  46. package/tools/warp/setup-warp.mjs +73 -1
package/CLAUDE.md CHANGED
@@ -87,7 +87,7 @@ All 10 providers receive all 4 artifact types (agents, commands, skills, rules).
87
87
  | Warp Terminal | `.warp/agents/` + WARP.md | `.warp/commands/` | `.warp/skills/` + `.agents/skills/` | `.warp/rules/` | `aiwg use sdlc --provider warp` |
88
88
  | Windsurf | AGENTS.md | `.windsurf/workflows/` | `.windsurf/skills/` + `.agents/skills/` | `.windsurf/rules/` | `aiwg use sdlc --provider windsurf` |
89
89
  | OpenClaw | `~/.openclaw/agents/` | `~/.openclaw/commands/` | `~/.openclaw/skills/` + `.agents/skills/` | `~/.openclaw/rules/` | `aiwg use sdlc --provider openclaw` |
90
- | Hermes | AGENTS.md | | `~/.hermes/skills/` | | `aiwg use sdlc --provider hermes` |
90
+ | Hermes | AGENTS.md + .hermes.md | via MCP `command-run` | `~/.hermes/skills/` (kernel) + `~/.hermes/skills/.aiwg/` (standard) | inlined in AGENTS.md + MCP `rule-list`/`rule-show` | `aiwg use sdlc --provider hermes` |
91
91
 
92
92
  **Special cases:**
93
93
  - **Codex**: Commands and skills deploy to home directory (`~/.codex/prompts/`, `~/.codex/skills/`) for user-level availability across all projects. `.codex/commands/` also deploys at project scope for operator visibility, but Codex commands are a static built-in enum in codex-rs and these files are not auto-scanned by the loader; AGENTS.md is the discovery bridge per ADR-1. Reference: `src/smiths/platform-paths.ts:23`.
@@ -95,6 +95,7 @@ All 10 providers receive all 4 artifact types (agents, commands, skills, rules).
95
95
  - **Warp**: Agents and commands are also aggregated into `WARP.md` for single-file context loading
96
96
  - **Windsurf**: Agents are aggregated into `AGENTS.md` at project root
97
97
  - **OpenClaw**: All artifacts deploy to home directory (`~/.openclaw/`). First provider to support behaviors (`~/.openclaw/behaviors/`)
98
+ - **Hermes**: MCP sidecar architecture (`Hermes → MCP → AIWG`). Commands reach AIWG via `mcp_aiwg_command_run` (allow-listed). Standard skills (~385) live under `~/.hermes/skills/.aiwg/` and are recursively discovered; kernel skills (~9) live at the top level and are protected from the Curator (v0.12.0+) via the `.bundled_manifest`. The MCP server has ~12 core tools by default; ~45 additional via `AIWG_MCP_TOOLSETS=<csv>`. See `docs/integrations/hermes-quickstart.md`.
98
99
 
99
100
  ## Writing Principles
100
101
 
package/README.md CHANGED
@@ -49,6 +49,17 @@ AIWG is a deployment tool and support utility for AI context. At its core, `aiwg
49
49
 
50
50
  Around that core, AIWG ships utilities for things the base platforms do not handle on their own: persistent artifact memory (`.aiwg/`), background orchestration (`aiwg mc`), autonomous loops (`aiwg ralph`), artifact indexing (`aiwg index`), cost telemetry, health diagnostics, and more. Most are opt-in. The deployment layer works standalone as plain text files the platform reads natively.
51
51
 
52
+ ### Project scope (recommended) vs user scope (global)
53
+
54
+ `aiwg use` writes artifacts at one of two scopes. Both are first-class supported (see ADR-NUA-001 in `.aiwg/studies/novice-user-adoption/`):
55
+
56
+ - **Project scope** — default. Run `aiwg use sdlc` from a project root and the artifacts land in `./.claude/agents/`, `./.claude/skills/`, etc. One project's agent set never bleeds into another's session. **This is the recommended default for most use cases.**
57
+ - **User scope (global install)** — `aiwg use sdlc --scope user` writes to `~/.claude/agents/`, `~/.claude/skills/`, etc. Same artifact set loads into every session, regardless of project. Fits "AIWG in every conversation" workflows and is the canonical mode for OpenClaw and Hermes (whose primary discovery is user-scope).
58
+
59
+ The trade-off is real: when the same agent set loads into every session, context from one project can bleed into reasoning about another. Research (REF-720, *Lost in Multi-Turn Conversation*, MSR/Salesforce 2025) measured a 39% capability drop when this happens. The non-blocking project-isolation warning surfaces the trade-off at deploy time so the scope choice is informed. Neither scope is wrong; pick the one that fits the workflow.
60
+
61
+ See `docs/cli-reference.md` (under `aiwg use` → "Scope models") for the per-provider details and the global-install rough-edge inventory.
62
+
52
63
  ## Simple Building Blocks
53
64
 
54
65
  AIWG ships five primitive artifact types. All are plain text:
@@ -36,7 +36,8 @@
36
36
  "consolidation": {
37
37
  "strategy": "index-with-links",
38
38
  "rulesIndex": "rules/RULES-INDEX.md",
39
- "deployIndexOnly": true
39
+ "deployIndexOnly": false,
40
+ "_note": "deployIndexOnly was true (#1343); changed to false 2026-05-14 so individual rule files (including skill-discovery.md) deploy to every provider's rules directory alongside the index. RULES-INDEX.md remains the canonical entry point; its links now resolve to local files on every provider, not just Claude/Cursor."
40
41
  },
41
42
  "commands": [],
42
43
  "agents": [
@@ -0,0 +1,42 @@
1
+ ## Discover-First Protocol (CRITICAL)
2
+
3
+ Before declining a user request as "outside AIWG's scope" or improvising a workflow from training data, you MUST consult AIWG's artifact index.
4
+
5
+ AIWG ships ~400 skills, ~190 agents, ~70 commands, and ~60 rules across its installed frameworks. Most are NOT loaded into your context — they live at the install location and reach you via two CLI commands:
6
+
7
+ ```bash
8
+ aiwg discover "<paraphrased user need>"
9
+ aiwg show <type> <name> # e.g. aiwg show skill flow-deploy-to-production
10
+ ```
11
+
12
+ ### When to run discover
13
+
14
+ You MUST run `aiwg discover` when any of the following is true:
15
+
16
+ - You are about to tell the user "AIWG doesn't have a way to do that"
17
+ - You are about to write a custom workflow / script / procedure from scratch
18
+ - The user's request mentions AIWG, a framework name (sdlc, research, forensics, ops, security-engineering, marketing, media-curator, knowledge-base), or capability keywords (skill, agent, command, rule, workflow, flow, template, addon)
19
+ - You are uncertain whether a curated AIWG artifact already addresses the request
20
+
21
+ You MAY skip discover only when:
22
+
23
+ - The user named a specific skill/command (e.g. `/flow-deploy-to-production`)
24
+ - The capability is clearly outside AIWG's scope (weather, translation, unrelated programming)
25
+ - You already ran `aiwg discover` for the same need this session
26
+ - A loaded quickref directly names the skill the user needs
27
+
28
+ ### Discover-first, NOT filesystem-first
29
+
30
+ When the user's request mentions an AIWG keyword, `aiwg discover` is your FIRST tool call — not `Grep` / `Glob` / `Read` against provider directories like `.claude/`, `.codex/`, `.factory/`, `.warp/`, `.windsurf/`, `~/.openclaw/`, or `~/.hermes/`.
31
+
32
+ Filesystem search against those paths is FORBIDDEN as a first move for AIWG-internal lookups. The reason: the discover index covers 10x the surface area that any single provider directory holds, and gives you ranked results with capability summaries. Grep gives you a literal-string hit and stops.
33
+
34
+ ### After discover returns a match
35
+
36
+ Use `aiwg show <type> <name>` to fetch the body — never `Read` on the returned path, never `find` / `ls` against the corpus directory. The CLI handles the read; you handle the application.
37
+
38
+ ### Why this matters
39
+
40
+ Most AIWG skills are not in your context by design — that's how the kernel-skill model keeps your context lean while making the full corpus reachable. Enumerating from memory will miss obvious matches. The cost of running `aiwg discover` is one CLI invocation; the cost of skipping it is recommending a workflow AIWG already has, or declining a request AIWG can already serve.
41
+
42
+ The full discover-first rule (with detection heuristics, recovery procedure, and integration with other rules) is deployed at the framework's rules path as `skill-discovery.md`. Read it once at session start if you have not already.
@@ -15,6 +15,13 @@
15
15
  "required": true,
16
16
  "description": "Orchestration responsibilities, multi-agent workflow pattern, NL translations, available commands"
17
17
  },
18
+ {
19
+ "id": "discover-first",
20
+ "file": "02b-discover-first.md",
21
+ "title": "Discover-First Protocol",
22
+ "required": true,
23
+ "description": "Mandates `aiwg discover` + `aiwg show` before declining requests or improvising workflows. Closes hookup-matrix Finding #2 (#1344) — the discover-first protocol now reaches every provider that loads AIWG.md (Codex, Windsurf, OpenCode, Hermes, plus Warp via twin emission)."
24
+ },
18
25
  {
19
26
  "id": "aiwg-rules",
20
27
  "file": "03-aiwg-rules.md",
@@ -8,6 +8,17 @@ This file provides project-specific instructions for GitHub Copilot.
8
8
 
9
9
  This project uses the AIWG SDLC Framework for software development lifecycle management.
10
10
 
11
+ ### Discover-First Protocol (CRITICAL)
12
+
13
+ Before declining a user request as "outside AIWG's scope" or improvising a workflow from training data, you MUST consult AIWG's artifact index. AIWG ships ~400 skills, ~190 agents, ~70 commands, and ~60 rules — most are NOT loaded into your context. Reach them via:
14
+
15
+ ```bash
16
+ aiwg discover "<paraphrased user need>"
17
+ aiwg show <type> <name> # e.g. aiwg show skill flow-deploy-to-production
18
+ ```
19
+
20
+ Run `aiwg discover` whenever the user mentions AIWG, a framework name (sdlc, research, forensics, ops, marketing, security-engineering, media-curator, knowledge-base), or capability keywords (skill, agent, command, rule, workflow). Filesystem search against `.github/agents/`, `.github/skills/`, or `.github/instructions/` is FORBIDDEN as a first move for AIWG-internal lookups — `aiwg discover` covers 10x the surface area with ranked results. The full protocol is documented at `.github/copilot-rules/skill-discovery.md` (or `.github/instructions/skill-discovery.instructions.md`); read it once at session start.
21
+
11
22
  ### Available Custom Agents
12
23
 
13
24
  AIWG agents are deployed to `.github/agents/` and can be invoked via `@agent-name`:
@@ -43,8 +43,8 @@
43
43
  "last_updated": "2026-05-13",
44
44
  "total_agents": 0,
45
45
  "total_commands": 0,
46
- "total_skills": 11,
47
- "total_rules": 0,
46
+ "total_skills": 15,
47
+ "total_rules": 6,
48
48
  "status": "active"
49
49
  },
50
50
  "memory": {
@@ -1,10 +1,10 @@
1
1
  # Security Engineering Rules Index
2
2
 
3
- Applied-security enforcement rules for cryptographic primitive choices, chain-of-trust integrity, secret handling, and related design-time concerns. Deployed when the `security-engineering` framework is installed.
3
+ Applied-security enforcement rules for cryptographic primitive choices, chain-of-trust integrity, secret handling, supply-chain pinning, dependency-source policy, and related design-time concerns. Deployed when the `security-engineering` framework is installed.
4
4
 
5
5
  ---
6
6
 
7
- ## Tier 1 Rules (4 rules — applied cryptography)
7
+ ## Tier 1 Rules (6 rules — applied cryptography + supply chain)
8
8
 
9
9
  ### HIGH
10
10
 
@@ -32,6 +32,18 @@ Applied-security enforcement rules for cryptographic primitive choices, chain-of
32
32
  **Maps to review finding**: H6
33
33
  **Full rule**: @$AIWG_ROOT/agentic/code/frameworks/security-engineering/rules/crypto-flag-verification.md
34
34
 
35
+ #### ci-action-pinning
36
+ **Summary**: Every CI workflow `uses:` reference MUST be a 40-character commit SHA (not a tag); every `container:`/`image:` reference MUST be `<name>:<tag>@sha256:<digest>`. Tools downloaded via `curl | sh` must record an observed-SHA log and support strict-mode SHA enforcement. Floating tags expose CI to silent supply-chain attacks (Shai-Hulud-class worm propagation). Maintain a pin manifest (`ci/digests.txt` or equivalent) as source of truth for diffs.
37
+ **When to apply**: Any workflow file under `.github/workflows/`, `.gitea/workflows/`, or equivalent; any tool-install step in CI
38
+ **Maps to issue**: #1293 (B3 / Mini Shai-Hulud)
39
+ **Full rule**: @$AIWG_ROOT/agentic/code/frameworks/security-engineering/rules/ci-action-pinning.md
40
+
41
+ #### dependency-source-policy
42
+ **Summary**: Non-registry dependency sources (`git+`, `github:`, raw tarball URLs, `file:`, `link:`) are PROHIBITED — they bypass registry signature verification and can execute arbitrary code at install time via `prepare` scripts (Mini Shai-Hulud's primary propagation vector). Policy applies to `package.json` AND transitive lockfile entries. Exceptions require an allowlist entry with owner, reason, review_date, and explicit risk acceptance. pnpm workspaces must set `blockExoticSubdeps: true` for workspace-scope enforcement.
43
+ **When to apply**: Any change to `package.json`, `package-lock.json`, `pnpm-lock.yaml`, `yarn.lock`, or `bun.lockb`; CI lint should run on every push
44
+ **Maps to issue**: #1297 (Mini Shai-Hulud follow-up)
45
+ **Full rule**: @$AIWG_ROOT/agentic/code/frameworks/security-engineering/rules/dependency-source-policy.md
46
+
35
47
  ---
36
48
 
37
49
  ## Quick Reference by Context
@@ -43,7 +55,10 @@ Applied-security enforcement rules for cryptographic primitive choices, chain-of
43
55
  | **Multi-key systems** | no-key-reuse-across-purposes, no-adhoc-kdf |
44
56
  | **Password handling** | no-adhoc-kdf (Argon2id/PBKDF2 ≥600k) |
45
57
  | **CLI crypto invocations** | crypto-flag-verification, no-unauthenticated-encryption |
46
- | **Reviewing cryptographic decisions** | All four rules in sequence |
58
+ | **CI workflow review** | ci-action-pinning |
59
+ | **Container image references** | ci-action-pinning |
60
+ | **package.json / lockfile review** | dependency-source-policy |
61
+ | **Reviewing cryptographic decisions** | All four crypto rules in sequence |
47
62
 
48
63
  ---
49
64
 
@@ -53,5 +68,5 @@ Future Tier 2 rules will cover authentication-factor architecture, degraded-mode
53
68
 
54
69
  ---
55
70
 
56
- *Generated from security-engineering framework — 4 rules in Tier 1*
71
+ *Generated from security-engineering framework — 6 rules in Tier 1*
57
72
  *Full rule files: @$AIWG_ROOT/agentic/code/frameworks/security-engineering/rules/*
@@ -0,0 +1,145 @@
1
+ # CI Action and Container Pinning
2
+
3
+ **Enforcement Level**: HIGH
4
+ **Scope**: All CI workflow files (`.github/workflows/`, `.gitea/workflows/`, equivalent) and any `container:` references therein
5
+ **Issue**: #1293
6
+
7
+ ## Principle
8
+
9
+ CI workflows execute third-party code with broad permissions on every push. A workflow that references `actions/checkout@v4` or `node:24` resolves the reference at run time — the bytes that execute today are not guaranteed to be the bytes that execute tomorrow. Anyone who can move the `v4` tag (a GitHub Action maintainer compromise, a registry account takeover, a published-but-malicious update) silently runs new code in your CI. Pinning by immutable digest closes this attack surface.
10
+
11
+ This is the same standard AIWG applies to its own CI per [`dev-idempotent-builds.md`](.claude/rules/dev-idempotent-builds.md) rule 2 ("no `:latest` in production"). B3 extends the standard to user projects as a deployable rule.
12
+
13
+ ## Mandatory Rules
14
+
15
+ ### Rule 1: GitHub Actions references pinned by 40-char commit SHA
16
+
17
+ Every `uses:` reference in a workflow file MUST point at a 40-character commit SHA, not a tag or branch.
18
+
19
+ **FORBIDDEN**:
20
+ ```yaml
21
+ - uses: actions/checkout@v4 # tag — can move
22
+ - uses: actions/checkout@main # branch — definitely moves
23
+ - uses: actions/checkout@v4.3.1 # version tag — usually immutable but a compromised account can re-point it
24
+ ```
25
+
26
+ **REQUIRED**:
27
+ ```yaml
28
+ - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
29
+ ```
30
+
31
+ The trailing comment is non-optional. It preserves human-readable diffability without compromising the immutability of the SHA pin.
32
+
33
+ ### Rule 2: Container images pinned by sha256 digest
34
+
35
+ Every `container:` or `image:` reference in a workflow MUST be `<name>:<tag>@sha256:<digest>`.
36
+
37
+ **FORBIDDEN**:
38
+ ```yaml
39
+ container: node:24
40
+ container: node:latest
41
+ container: node:24-bookworm
42
+ container: ghcr.io/owner/image:v2.1.0
43
+ ```
44
+
45
+ **REQUIRED**:
46
+ ```yaml
47
+ container: node:24@sha256:050bf2bbe33c1d6754e060bec89378a79ed831f04a7bb1a53fe45e997df7b3bb # 24.15.0
48
+ ```
49
+
50
+ The `<name>:<tag>` prefix is retained for human readability; the digest is the trust anchor. Trailing comment captures the resolved version for diff context.
51
+
52
+ ### Rule 3: Pin manifest at `ci/digests.txt` (or equivalent)
53
+
54
+ Every project that enforces this rule MUST maintain a pin manifest listing each pinned reference, its resolved version, the date pinned, and the rationale. The manifest is the source of truth for diffs — a SHA change without a corresponding manifest entry is a red flag.
55
+
56
+ Reference manifest format: see AIWG's own [`ci/digests.txt`](https://git.integrolabs.net/roctinam/aiwg/src/branch/main/ci/digests.txt).
57
+
58
+ Minimum row format:
59
+ ```
60
+ <kind> <name> <pin> <resolved-version> <date-pinned> <update-rationale>
61
+ container node:24 sha256:050bf2bbe33c1d6754e060bec89378a79ed831f04a7bb1a53fe45e997df7b3bb 24.15.0 2026-05-12 initial pin
62
+ action actions/checkout 93cb6efe18208431cddfb8368fd83d5badbf9bfd v5.0.1 2026-05-12 initial pin
63
+ ```
64
+
65
+ ### Rule 4: Pin bumps go through review
66
+
67
+ Every pin update is a reviewable change. The commit should:
68
+ - Reference the issue or advisory motivating the bump (CVE, needed feature, scheduled rotation)
69
+ - Update the manifest row in the same commit as the workflow file edit
70
+ - Include `chore(ci): bump <name> pin to <version> (refs #<issue>)` style commit message
71
+
72
+ Workflow diffs that change a digest without a corresponding manifest update fail review.
73
+
74
+ ### Rule 5: Standalone tools pinned by version + checksum
75
+
76
+ Tools downloaded by `curl | sh` or equivalent in CI (e.g., `syft install.sh`, ad-hoc binary releases) MUST pin a specific version AND record the installer's content hash. The version pin gives reproducibility; the content hash detects upstream tampering.
77
+
78
+ **Acceptable pattern** (strict-mode opt-in, with observed-SHA logging):
79
+ ```yaml
80
+ - name: Install syft
81
+ run: |
82
+ VERSION=v1.18.0
83
+ EXPECTED_INSTALL_SHA="" # populate after first run; logs `observed` SHA
84
+ SCRIPT=$(curl -fsSL "https://raw.githubusercontent.com/anchore/syft/${VERSION}/install.sh")
85
+ OBSERVED_SHA=$(printf '%s' "$SCRIPT" | sha256sum | awk '{print $1}')
86
+ echo "observed install-script SHA: ${OBSERVED_SHA}"
87
+ if [ -n "$EXPECTED_INSTALL_SHA" ] && [ "$OBSERVED_SHA" != "$EXPECTED_INSTALL_SHA" ]; then
88
+ echo "✗ install-script SHA drift — refusing to execute"
89
+ exit 1
90
+ fi
91
+ printf '%s' "$SCRIPT" | sh -s -- -b /usr/local/bin "$VERSION"
92
+ ```
93
+
94
+ ## Detection Patterns
95
+
96
+ CI lint should flag the following:
97
+
98
+ ```bash
99
+ # Floating action tags
100
+ grep -rE '^\s*-\s*uses:\s*[a-zA-Z0-9._-]+/[a-zA-Z0-9._-]+@(v?[0-9]+(\.[0-9]+)*|main|master|latest)\s*(#.*)?$' \
101
+ .github/workflows/ .gitea/workflows/ 2>/dev/null
102
+
103
+ # Unpinned container images
104
+ grep -rE '^\s*(container|image):\s*[a-zA-Z0-9._/-]+:[a-zA-Z0-9._-]+\s*(#.*)?$' \
105
+ .github/workflows/ .gitea/workflows/ 2>/dev/null \
106
+ | grep -v 'sha256:'
107
+
108
+ # Bare :latest anywhere
109
+ grep -rnE ':latest\b' .github/workflows/ .gitea/workflows/ 2>/dev/null
110
+
111
+ # Curl-pipe-shell installers without a content hash check
112
+ grep -rnE 'curl[^|]+\|\s*(bash|sh)' .github/workflows/ .gitea/workflows/ 2>/dev/null
113
+ ```
114
+
115
+ Wire these into a `npm run lint:ci-pins` script (or equivalent) and call it from the CI workflow itself so the rule is self-enforcing.
116
+
117
+ ## Acceptable Exceptions
118
+
119
+ The following are NOT subject to this rule:
120
+
121
+ 1. **Dockerfile `FROM` instructions in build stages** that are NOT consumed by CI directly — those are governed by the project's image-build pipeline (which should follow the same standard, but the rule lives in the Dockerfile review, not the workflow review).
122
+ 2. **Ephemeral self-hosted runners** that pull pre-baked AMIs/images with their own attestation chain — the chain-of-trust is at the AMI level, not the workflow level.
123
+ 3. **Reusable workflows from the same repository** (`uses: ./.github/workflows/foo.yml`) — these are pinned to the repository state at the calling commit.
124
+ 4. **Build-arg substitutions for the same digest** — `FROM ${BASE_IMAGE}` where `BASE_IMAGE` is built from a digest-pinned reference one level up.
125
+
126
+ Document any other exception in a code comment adjacent to the unpinned reference, with reasoning and an issue link tracking eventual migration to a pin.
127
+
128
+ ## Rationale
129
+
130
+ The mutable-tag attack surface is one of the largest unaddressed components of npm-ecosystem supply-chain risk as of 2026. The recent Shai-Hulud worm campaign demonstrated that compromised maintainer accounts can push malicious updates that propagate via floating version tags within hours. Workflows pinned by SHA stop that propagation at the boundary of every individual project that pins.
131
+
132
+ Pin-bump cost is low (one curl/git ls-remote command); incident cost from an unpinned dependency executing in CI with secret access is high. The asymmetry justifies the rule.
133
+
134
+ ## References
135
+
136
+ - [`dev-idempotent-builds.md`](.claude/rules/dev-idempotent-builds.md) — base rule on reproducible builds
137
+ - AIWG's own pin manifest: `ci/digests.txt`
138
+ - Defenses brief C7 (action pinning) and C8 (container pinning)
139
+ - Shai-Hulud worm post-mortem references
140
+ - GitHub Actions documentation: pinning third-party actions to a full-length commit SHA
141
+
142
+ ---
143
+
144
+ **Rule Status**: ACTIVE
145
+ **Last Updated**: 2026-05-13
@@ -0,0 +1,177 @@
1
+ # Dependency Source Policy
2
+
3
+ **Enforcement Level**: HIGH
4
+ **Scope**: `package.json` and lockfile (`package-lock.json`, `pnpm-lock.yaml`, `yarn.lock`, `bun.lockb`) — direct AND transitive entries
5
+ **Issue**: #1297
6
+
7
+ ## Principle
8
+
9
+ Non-registry dependency sources (`git+`, `github:`, raw tarball URLs, `file:`, `link:`) bypass registry signature verification AND can execute arbitrary code during install via lifecycle scripts (especially `prepare`). The May 2026 Mini Shai-Hulud campaign's primary propagation vector was an `optionalDependencies` entry sourced from `git+https://...attacker-repo` whose `prepare` script ran during every `npm install` of any project that pulled in the affected package.
10
+
11
+ Registry-hosted tarballs (`registry.npmjs.org/.../foo.tgz`, `registry.yarnpkg.com/...`, `npm.pkg.github.com/...`) are fine — they're the normal `resolved` URL format `npm install` emits for every registry-published dep. The policy only flags non-registry sources.
12
+
13
+ Companion to [`ci-action-pinning`](ci-action-pinning.md) (CI execution-environment trust) and the per-package-manager release-age-gate skills (which gate freshly-published registry versions). Together they cover the three primary supply-chain attack vectors: CI environment poisoning, registry-version poisoning, and direct dep-source injection.
14
+
15
+ ## Mandatory Rules
16
+
17
+ ### Rule 1: Six dep-source patterns are forbidden by default
18
+
19
+ Every dep in `package.json` AND every entry in the lockfile (including transitive) MUST be a registry-published version reference. The following patterns are blocked:
20
+
21
+ | Pattern | Example | Reason |
22
+ |---|---|---|
23
+ | `git+*` scheme | `"foo": "git+https://github.com/owner/foo.git"` | npm clones the repo and runs its `prepare` script — arbitrary code execution at install time |
24
+ | `git://` scheme | `"foo": "git://github.com/owner/foo.git"` | Same as above |
25
+ | `github:` shorthand | `"foo": "github:owner/foo"` | Same as above; npm expands to git+ |
26
+ | Non-registry tarball | `"foo": "https://example.com/foo-1.0.0.tgz"` | Tarball can contain any payload and lifecycle scripts; bypasses registry signature verification |
27
+ | `file:` path | `"foo": "file:./vendor/foo"` | Local-path deps bypass dep-resolution review and lockfile signature checks |
28
+ | `link:` symlink | `"foo": "link:./packages/foo"` | Follows the symlink target wherever it points; same gaps as `file:` |
29
+
30
+ Registry-hosted patterns that are ALWAYS acceptable:
31
+
32
+ - `"foo": "^1.2.3"` — registry semver range
33
+ - `"foo": "1.2.3"` — exact registry version
34
+ - `"foo": "npm:@scope/foo@^1.2.3"` — aliased registry dep
35
+ - Lockfile `"resolved": "https://registry.npmjs.org/..."` — registry-served tarball
36
+
37
+ ### Rule 2: Exceptions require an allowlist entry
38
+
39
+ If a legitimate non-registry source exists (e.g., temporary fork pending upstream merge, monorepo-internal `file:` link with a known security boundary), it MUST be allowlisted with:
40
+
41
+ - **Owner** — who took the risk-acceptance decision
42
+ - **Reason** — why a registry version isn't viable today
43
+ - **Expiry / review date** — when this exception must be re-evaluated (max 1 year)
44
+ - **Risk acceptance** — explicit statement that the operator accepts the dep-source bypass
45
+
46
+ Allowlist file format (committed at `.aiwg/security/dep-source-allowlist.yaml` or equivalent):
47
+
48
+ ```yaml
49
+ allowlist:
50
+ - dep: "foo"
51
+ source: "git+https://github.com/owner/foo.git#sha-or-tag"
52
+ owner: "alice@example.org"
53
+ reason: "Upstream PR #123 not yet merged; we need fix from main for issue X"
54
+ review_date: "2026-08-01"
55
+ risk_acceptance: "Source pinned to commit SHA; reviewed prepare script line-by-line; no shell exec in install path"
56
+ - dep: "@internal/shared"
57
+ source: "file:./packages/shared"
58
+ owner: "bob@example.org"
59
+ reason: "Monorepo-internal package; not published to registry"
60
+ review_date: "2027-01-01"
61
+ risk_acceptance: "Source within same security boundary as host package; no external code path"
62
+ ```
63
+
64
+ Allowlist entries beyond their `review_date` are treated as policy violations.
65
+
66
+ ### Rule 3: Lockfile entries are policy targets
67
+
68
+ Direct deps in `package.json` are easy to spot. Transitive entries in the lockfile are the actual attack surface — Mini Shai-Hulud propagated via transitive `optionalDependencies` with exotic sources. The CI lint MUST scan the lockfile for the same patterns, including:
69
+
70
+ - `"resolved": "git+..."` in `package-lock.json`
71
+ - `tarball: ...` URLs in `pnpm-lock.yaml` that don't match a registry origin
72
+ - `resolution: ` blocks in `yarn.lock`
73
+ - Binary lockfile (`bun.lockb`) — extract via `bun pm ls --all` and pattern-match the output
74
+
75
+ ### Rule 4: pnpm workspaces enable `blockExoticSubdeps`
76
+
77
+ When pnpm is the package manager, the workspace MUST set `blockExoticSubdeps: true` (in `pnpm-workspace.yaml`) or the per-repo equivalent in `.npmrc`:
78
+
79
+ ```yaml
80
+ # pnpm-workspace.yaml
81
+ packages:
82
+ - 'packages/*'
83
+ blockExoticSubdeps: true
84
+ ```
85
+
86
+ This is the pnpm advantage over npm: workspace-scope enforcement that npm has no equivalent for. The `pnpm-release-age-gate` skill documents the wiring.
87
+
88
+ ### Rule 5: Failure messages point to remediation
89
+
90
+ When CI rejects a violation, the failure message MUST:
91
+
92
+ 1. State which dep and which source pattern matched
93
+ 2. Reference the operator's choices: switch to a registry version, request an allowlist entry, or accept install-time compromise risk
94
+ 3. Link to this rule (or the project's adaptation of it) so the receiving developer can read the policy
95
+
96
+ Reference failure-message format (from AIWG's own `tools/lint/dep-source.mjs`):
97
+
98
+ ```
99
+ ✗ Dependency source policy violation
100
+
101
+ foo (in package.json)
102
+ source: git+https://github.com/owner/foo.git
103
+ pattern: git+ scheme
104
+
105
+ This dependency source bypasses registry signature verification and
106
+ executes arbitrary code at install time via the prepare script.
107
+
108
+ Options:
109
+ 1. Switch to a registry-published version: npm install foo@<version>
110
+ 2. Add an allowlist entry: see .aiwg/security/dep-source-allowlist.yaml
111
+ 3. Read the policy: docs/contributing/dependency-sources.md
112
+ ```
113
+
114
+ ## Detection Patterns
115
+
116
+ ```bash
117
+ # package.json — direct deps with exotic sources
118
+ node -p "
119
+ const p = require('./package.json');
120
+ const all = {...p.dependencies, ...p.devDependencies, ...p.optionalDependencies, ...p.peerDependencies};
121
+ for (const [name, source] of Object.entries(all || {})) {
122
+ if (/^(git\+|git:|github:|file:|link:)/.test(source) ||
123
+ (/^https?:\/\//.test(source) && /\.tgz/.test(source))) {
124
+ console.log(name, '=', source);
125
+ }
126
+ }
127
+ "
128
+
129
+ # package-lock.json — transitive entries with exotic resolved URLs
130
+ node -p "
131
+ const lock = require('./package-lock.json');
132
+ function walk(pkgs, prefix = '') {
133
+ for (const [path, entry] of Object.entries(pkgs || {})) {
134
+ if (entry.resolved && /^(git\+|git:)/.test(entry.resolved)) {
135
+ console.log(prefix + path, '=', entry.resolved);
136
+ }
137
+ }
138
+ }
139
+ walk(lock.packages);
140
+ "
141
+
142
+ # pnpm-lock.yaml — exotic resolution blocks
143
+ grep -E 'resolution:\s*\{(git|tarball:.*[^.]https?://[^/]+/[^.]*\.tgz)' pnpm-lock.yaml 2>/dev/null
144
+
145
+ # yarn.lock — exotic resolved entries
146
+ grep -E '"?resolved"?\s*"?:?\s*"?(git\+|git:)' yarn.lock 2>/dev/null
147
+ ```
148
+
149
+ Reference implementation: AIWG's own [`tools/lint/dep-source.mjs`](https://git.integrolabs.net/roctinam/aiwg/src/branch/main/tools/lint/dep-source.mjs) — runs `npm run lint:dep-sources` and exits non-zero on any violation.
150
+
151
+ ## Acceptable Exceptions
152
+
153
+ Three exception categories beyond explicit allowlist entries:
154
+
155
+ 1. **Monorepo workspace links** — `file:` or `link:` deps between packages in the same `pnpm-workspace.yaml` / `lerna.json` / `nx.json` / `package.json workspaces` configuration. These are within the same security boundary; flag as INFO only.
156
+ 2. **Lockfile resolved-URL prefixes that look exotic but ARE registry URLs** — e.g., `https://registry.npmjs.org/...` matches `^https://` but is a registry origin. The detection logic above already excludes these correctly by checking the host.
157
+ 3. **Dev-only deps for build tooling that consume only `package.json`-declared scripts** — debatable; recommend treating as standard policy violations and using allowlist entries for the temporary cases.
158
+
159
+ ## Rationale
160
+
161
+ Mini Shai-Hulud demonstrated that a single transitive `optionalDependencies` entry from a compromised maintainer can propagate a `prepare`-script payload to every downstream `npm install`. The registry's "this version was published by an authenticated maintainer" guarantee is the foundation of `npm audit signatures`; non-registry sources bypass that guarantee entirely.
162
+
163
+ Cost of compliance is low (most deps are already registry-published). Cost of a single exotic-source incident is high (full secret-rotation cycle, downstream user trust loss, potential CVE assignment). The asymmetry justifies the rule.
164
+
165
+ ## References
166
+
167
+ - [`ci-action-pinning`](ci-action-pinning.md) — CI execution-environment trust (companion rule)
168
+ - AIWG's lint implementation: [`tools/lint/dep-source.mjs`](https://git.integrolabs.net/roctinam/aiwg/src/branch/main/tools/lint/dep-source.mjs)
169
+ - AIWG's contributor doc: [`docs/contributing/dependency-sources.md`](https://git.integrolabs.net/roctinam/aiwg/src/branch/main/docs/contributing/dependency-sources.md)
170
+ - ADR: `.aiwg/architecture/adr-dep-source-policy.md` (in AIWG's own repo)
171
+ - pnpm `blockExoticSubdeps`: <https://pnpm.io/settings#blockexoticsubdeps>
172
+ - Mini Shai-Hulud post-mortem references
173
+
174
+ ---
175
+
176
+ **Rule Status**: ACTIVE
177
+ **Last Updated**: 2026-05-13