actions-warden 0.2.0 → 0.3.0

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 (62) hide show
  1. package/AGENTS.md +189 -0
  2. package/CONTRIBUTING.md +109 -0
  3. package/README.md +272 -224
  4. package/RELEASING.md +338 -0
  5. package/SECURITY.md +25 -3
  6. package/docs/AI-AGENTS.md +458 -0
  7. package/docs/CLI.md +421 -0
  8. package/docs/CONFIGURATION.md +340 -0
  9. package/docs/DEVELOPMENT.md +350 -0
  10. package/docs/GITHUB-ACTION.md +281 -0
  11. package/docs/JAVASCRIPT-API.md +355 -0
  12. package/docs/OUTPUTS.md +409 -0
  13. package/docs/README.md +27 -0
  14. package/examples/org-scan.yml +42 -0
  15. package/examples/upgrade-pr.yml +57 -0
  16. package/llms.txt +38 -0
  17. package/package.json +32 -10
  18. package/skills/actions-warden/SKILL.md +140 -38
  19. package/src/action.js +317 -0
  20. package/src/cli.js +267 -22
  21. package/src/commands/audit.js +189 -36
  22. package/src/commands/org-scan.js +549 -0
  23. package/src/commands/pin.js +59 -56
  24. package/src/commands/report.js +122 -10
  25. package/src/commands/upgrade.js +102 -62
  26. package/src/commands/verify.js +193 -0
  27. package/src/index.js +21 -4
  28. package/src/lib/action-status.js +27 -0
  29. package/src/lib/agent-mode.js +175 -0
  30. package/src/lib/annotations.js +250 -0
  31. package/src/lib/baseline.js +103 -0
  32. package/src/lib/cache.js +47 -10
  33. package/src/lib/concurrency.js +27 -0
  34. package/src/lib/config.js +185 -0
  35. package/src/lib/execution.js +71 -0
  36. package/src/lib/formatter.js +127 -8
  37. package/src/lib/github-org.js +374 -0
  38. package/src/lib/identity.js +62 -0
  39. package/src/lib/ignore.js +7 -6
  40. package/src/lib/org-checkpoint.js +378 -0
  41. package/src/lib/org-progress.js +60 -0
  42. package/src/lib/parser.js +326 -52
  43. package/src/lib/patcher.js +199 -0
  44. package/src/lib/paths.js +35 -12
  45. package/src/lib/redact.js +65 -4
  46. package/src/lib/resolver.js +225 -43
  47. package/src/lib/targets.js +28 -0
  48. package/src/lib/triggers.js +12 -0
  49. package/src/lib/writer.js +45 -8
  50. package/src/rules/excessive-permissions.js +24 -33
  51. package/src/rules/index.js +19 -1
  52. package/src/rules/pull-request-target-checkout.js +149 -18
  53. package/src/rules/reusable-workflow-secrets.js +32 -0
  54. package/src/rules/script-injection.js +77 -12
  55. package/src/rules/secrets-in-env.js +101 -18
  56. package/src/rules/unpinned-action.js +3 -2
  57. package/src/rules/unpinned-container-image.js +39 -0
  58. package/src/rules/unpinned-docker-action.js +30 -0
  59. package/src/rules/untrusted-self-hosted-runner.js +109 -0
  60. package/src/rules/workflow-run-artifact-execution.js +122 -0
  61. package/src/rules/workflow-structure.js +396 -0
  62. package/src/version.js +3 -0
package/RELEASING.md ADDED
@@ -0,0 +1,338 @@
1
+ # Releasing actions-warden
2
+
3
+ This is the authoritative maintainer and coding-agent runbook. `actions-warden`
4
+ has no long-running service to deploy. In this repository, a release or deploy
5
+ means publishing one coordinated version through four distribution channels:
6
+
7
+ - the `actions-warden` npm package;
8
+ - an immutable GitHub tag and release named `vX.Y.Z`;
9
+ - the floating GitHub Action tag `vX`;
10
+ - the `chiz0me/claude-plugins` marketplace entry.
11
+
12
+ The tag-triggered
13
+ [release workflow](https://github.com/chiz0me/actions-warden/blob/main/.github/workflows/release.yml)
14
+ is the only normal publication path. Do not run `npm publish` from a workstation
15
+ except for an explicitly approved bootstrap or incident-recovery operation.
16
+
17
+ ## Agent authorization contract
18
+
19
+ Repository-aware agents must map release requests as follows. A direct request
20
+ is enough authorization for the matching row; the maintainer does not need to
21
+ repeat the commands in this guide.
22
+
23
+ | Maintainer request | Authorized work | Remote publication |
24
+ |---|---|---|
25
+ | “check/review release readiness” | Read-only local, GitHub, and npm checks | none |
26
+ | “prepare release `X.Y.Z`” or “bump to `X.Y.Z`” | Update local version sources, rebuild `dist/`, inspect the package, and validate | none |
27
+ | “release/publish/deploy actions-warden `X.Y.Z`” | Preflight, prepare, commit intended release changes, push `main`, wait for CI, create and push the immutable tag, monitor, and verify | yes |
28
+ | “release/publish/deploy actions-warden” | The same full workflow, with the version selected by the policy below | yes |
29
+ | “retry release `vX.Y.Z`” | Classify existing state and rerun the existing workflow when recovery is idempotent | only missing stages |
30
+
31
+ “Prepare,” “bump,” “check,” and “review” do not authorize a commit, push, tag,
32
+ GitHub release, npm publish, or marketplace write. A full release request does
33
+ not authorize force-moving or deleting an immutable version tag, publishing
34
+ unreviewed files, weakening validation, changing repository protections, npm
35
+ unpublishing, or npm deprecation. Those require separate explicit direction.
36
+
37
+ Before any full release, the agent must state the chosen version and the
38
+ preflight result in a short progress update. It should continue without asking
39
+ the maintainer to restate this runbook when every check passes.
40
+
41
+ ### Hard stops
42
+
43
+ Stop before creating a remote commit or tag when any of these is true:
44
+
45
+ - the worktree contains changes whose ownership or release intent is unknown;
46
+ - the current branch is not `main`, or local `main` is behind or diverged from
47
+ `origin/main` after a fresh fetch;
48
+ - the intended release commit is not contained in `origin/main`;
49
+ - the candidate is not a stable `X.Y.Z`, is not newer than npm `latest`, or an
50
+ immutable tag/release already exists outside an intentional retry;
51
+ - another release run is queued or in progress;
52
+ - GitHub authentication, trusted publishing, the marketplace secret, branch
53
+ access, or tag access is unavailable;
54
+ - tests, lint, audit, documentation, package inspection, or bundle
55
+ reproducibility fails.
56
+
57
+ A clean checkout that is only behind may be updated with a fast-forward pull.
58
+ Never reset, overwrite, stash, or merge a dirty/diverged checkout to get around
59
+ a blocker. Report the exact evidence and the smallest required correction.
60
+
61
+ ## Choose the version
62
+
63
+ Use the version the maintainer supplies when it is a valid, unpublished stable
64
+ SemVer greater than npm `latest`. If no version is supplied, compare the latest
65
+ immutable release with the intended diff and choose the highest applicable
66
+ bump:
67
+
68
+ | Change | Version bump |
69
+ |---|---|
70
+ | Compatible fix, documentation, internal refactor, or dependency maintenance | patch |
71
+ | New public command, option, rule, output capability, or other compatible feature | minor |
72
+ | Incompatible public behavior while the package is `0.x` | minor |
73
+ | Incompatible public behavior after `1.0.0` | major |
74
+
75
+ Stable releases only are currently supported. Do not create prerelease tags or
76
+ invent a lower version from the checkout: npm and GitHub are the live source of
77
+ truth. If compatibility cannot be determined safely, stop and ask only for the
78
+ version decision.
79
+
80
+ ## Full release procedure
81
+
82
+ ### 1. Inspect live state
83
+
84
+ Start without mutating project files:
85
+
86
+ ```sh
87
+ git status --short --branch
88
+ git fetch --prune origin main
89
+ git branch --show-current
90
+ git rev-list --left-right --count HEAD...origin/main
91
+ npm view actions-warden version dist-tags --json
92
+ gh auth status
93
+ gh run list --workflow=release.yml --limit=20 \
94
+ --json databaseId,status,conclusion,headSha,displayTitle,url
95
+ gh secret list --repo chiz0me/actions-warden
96
+ gh api repos/chiz0me/actions-warden/rulesets \
97
+ --jq 'map({name,target,enforcement})'
98
+ gh api repos/chiz0me/actions-warden/branches/main/protection \
99
+ --jq '{required_status_checks,required_pull_request_reviews,enforce_admins}'
100
+ gh api repos/chiz0me/actions-warden/environments \
101
+ --jq '[.environments[].name]'
102
+ ```
103
+
104
+ The branch must be `main`; before preparation, `HEAD...origin/main` must be
105
+ `0 0`. The status output must contain only understood, intended changes. The
106
+ secret list must include `MARKETPLACE_SYNC_TOKEN`; never request or print its
107
+ value. No release workflow may be `queued`, `pending`, `waiting`, or
108
+ `in_progress`. Observe the live branch, tag, and environment rules and follow
109
+ them without bypass; an empty result means those optional protections are not
110
+ configured. HTTP 404 from the legacy branch-protection query means no legacy
111
+ rule is configured; evaluate repository rulesets separately. Neither result
112
+ authorizes an agent to invent repository settings during a release.
113
+
114
+ Set the selected version locally for the remaining examples and verify that no
115
+ immutable object already owns it:
116
+
117
+ ```sh
118
+ release_version=X.Y.Z
119
+ git tag --list "v${release_version}"
120
+ git ls-remote --tags --refs origin "refs/tags/v${release_version}"
121
+ gh release view "v${release_version}"
122
+ ```
123
+
124
+ The first two commands must print nothing and `gh release view` must report no
125
+ release. Existing state is acceptable only for the retry procedure.
126
+
127
+ ### 2. Prepare and inspect the candidate
128
+
129
+ Install the committed graph, update every version-bearing source, and rebuild
130
+ the GitHub Action bundle:
131
+
132
+ ```sh
133
+ npm ci
134
+ npm run release:prepare -- "$release_version"
135
+ npm run build:action
136
+ git diff --check
137
+ git status --short
138
+ git diff
139
+ ```
140
+
141
+ `release:prepare` updates `package.json`, both lockfile version fields,
142
+ `.claude-plugin/plugin.json`, `src/version.js`, and exact public
143
+ `actions-warden@X.Y.Z` invocations. It never commits, tags, pushes, or
144
+ publishes.
145
+
146
+ Review every changed file. Stage exact intended paths—do not use a broad add in
147
+ a dirty worktree—then run the release gate:
148
+
149
+ ```sh
150
+ git add <reviewed-paths>
151
+ npm run release:check
152
+ git diff --cached --check
153
+ git diff --cached
154
+ ```
155
+
156
+ `release:check` verifies registry version monotonicity, version synchronization,
157
+ exact dependencies, YAML, documentation links, lint, tests, dependency audit,
158
+ the exact npm tarball manifest, and the staged/working Action bundle against a
159
+ clean rebuild. On a workflow retry, the registry guard also requires the local
160
+ tarball integrity to equal the already-published immutable artifact. The gate
161
+ intentionally fails if `dist/index.js` or
162
+ `dist/package.json` is not staged.
163
+
164
+ ### 3. Commit and pass main CI
165
+
166
+ ```sh
167
+ git commit -m "release: v${release_version}"
168
+ release_sha="$(git rev-parse HEAD)"
169
+ git push origin HEAD:main
170
+ gh run list --workflow=ci.yml --commit "$release_sha" --limit=1 \
171
+ --json databaseId,status,conclusion,url
172
+ gh run watch <CI_RUN_ID> --exit-status
173
+ ```
174
+
175
+ The direct push is the current repository path. If branch policy later requires
176
+ a pull request, push a dedicated release branch, open the release PR, and wait
177
+ for its required review and CI instead of bypassing protection. After the
178
+ approved merge, set `release_sha` to the resulting `origin/main` commit. A full
179
+ release request authorizes that normal release PR flow, but not a protection
180
+ bypass.
181
+
182
+ Wait for the CI run belonging to `release_sha`; do not substitute an older
183
+ green run. After it succeeds, fetch again and require a clean worktree plus an
184
+ exact remote match:
185
+
186
+ ```sh
187
+ git fetch --prune origin main
188
+ git status --short --branch
189
+ test "$(git rev-parse HEAD)" = "$(git rev-parse origin/main)"
190
+ ```
191
+
192
+ ### 4. Create the immutable trigger
193
+
194
+ The remote version tag is the irreversible publication trigger:
195
+
196
+ ```sh
197
+ git tag -a "v${release_version}" -m "v${release_version}" "$release_sha"
198
+ git push origin "refs/tags/v${release_version}"
199
+ ```
200
+
201
+ Never use `--force` for `vX.Y.Z`. If the push races with an existing tag, stop
202
+ and compare its commit before doing anything else.
203
+
204
+ ### 5. Monitor publication
205
+
206
+ Find the release run for the exact commit and watch it to completion:
207
+
208
+ ```sh
209
+ gh run list --workflow=release.yml --commit "$release_sha" --limit=1 \
210
+ --json databaseId,status,conclusion,url
211
+ gh run watch <RELEASE_RUN_ID> --exit-status
212
+ ```
213
+
214
+ The workflow is serialized with a
215
+ [queued concurrency group](https://docs.github.com/en/actions/how-tos/write-workflows/choose-when-workflows-run/control-workflow-concurrency?apiVersion=2022-11-28).
216
+ Its publication chain is:
217
+
218
+ ```text
219
+ validate -> release-draft -> publish-npm -> release
220
+ |-> floating-major-tag
221
+ `-> sync-marketplace
222
+ ```
223
+
224
+ | Job | Responsibility |
225
+ |---|---|
226
+ | `validate` | Stable tag, package match, `main` ancestry, npm monotonicity/retry integrity, tests, lint, YAML/docs, exact dependencies, version sync, audit, tarball inspection, and bundle reproduction |
227
+ | `release-draft` | Create or reuse a draft GitHub release for the immutable tag |
228
+ | `publish-npm` | Publish with npm OIDC and provenance, or safely skip an already-published retry |
229
+ | `release` | Make the GitHub release final only after npm succeeds |
230
+ | `floating-major-tag` | Move the intentionally mutable `vN` Action tag to the release commit |
231
+ | `sync-marketplace` | Update and commit the external Claude plugin marketplace version |
232
+
233
+ The draft-first order prevents a final GitHub release from advertising an npm
234
+ publication that failed. Timeouts bound every job, and queued concurrency
235
+ prevents overlapping releases from racing the npm dist-tag or floating Action
236
+ tag.
237
+
238
+ ### 6. Verify every channel
239
+
240
+ ```sh
241
+ npm view actions-warden version dist-tags --json
242
+ npm view "actions-warden@${release_version}" version dist.integrity --json
243
+ gh release view "v${release_version}" \
244
+ --json tagName,isDraft,isPrerelease,targetCommitish,url
245
+ git ls-remote --tags origin \
246
+ "refs/tags/v${release_version}" \
247
+ "refs/tags/v${release_version}^{}" \
248
+ "refs/tags/v${release_version%%.*}"
249
+ gh api repos/chiz0me/claude-plugins/contents/.claude-plugin/marketplace.json \
250
+ --jq '.content' | base64 -d \
251
+ | node -e "let s='';process.stdin.on('data',c=>s+=c).on('end',()=>{const d=JSON.parse(s);console.log(d.plugins.find(p=>p.name==='actions-warden').version)})"
252
+ ```
253
+
254
+ Confirm that npm `latest`, the npm package, the final GitHub release, the
255
+ immutable tag, the floating major tag, and the marketplace entry all identify
256
+ the new version/commit. Also confirm the npm package page shows provenance.
257
+ For the annotated immutable tag, its peeled `^{}` row and the floating `vN` row
258
+ must equal `release_sha`. Record the release and workflow URLs in the handoff.
259
+
260
+ Do not use a local floating `vN` tag as evidence. It is expected to become stale
261
+ when a release workflow moves the remote tag, and a blanket `git fetch --tags`
262
+ may reject that update as a clobber. The explicit `git ls-remote` result above
263
+ is authoritative; immutable `vX.Y.Z` tags are never moved.
264
+
265
+ ## Retry and recovery
266
+
267
+ Classify the failure before acting. Reruns are designed to be idempotent, but
268
+ version tags and npm versions are immutable.
269
+
270
+ | Observed state | Safe action |
271
+ |---|---|
272
+ | Validation or transient infrastructure failed; nothing published | Rerun the same workflow only if the tagged commit needs no code change. If code must change, fix `main` and release a new version. |
273
+ | Draft release exists; npm is absent | Correct the external/OIDC problem and rerun the same workflow. The draft is reused. |
274
+ | npm version exists; GitHub release is still a draft | Rerun. Registry checks and publish are safe no-ops, then the draft is finalized. |
275
+ | Final GitHub release exists; floating tag or marketplace sync failed | Rerun the failed jobs or whole workflow. Do not republish or create a replacement tag. |
276
+ | Marketplace authentication or push failed | Restore `MARKETPLACE_SYNC_TOKEN` access or resolve the marketplace branch conflict, then rerun. |
277
+ | Published package is defective | Fix forward with a newer version. Deprecate only on explicit maintainer instruction; never unpublish automatically. |
278
+ | Remote tag exists but no workflow run exists | Verify the tag commit and that the workflow existed there. Do not force-push the tag; use a new version unless the maintainer explicitly directs recovery. |
279
+
280
+ Use GitHub's **Re-run all jobs** or `gh run rerun <RUN_ID>` for an unchanged
281
+ tagged commit. Do not delete a draft, release, package, or remote tag as an
282
+ automatic cleanup step.
283
+
284
+ ## One-time repository setup
285
+
286
+ ### npm Trusted Publisher
287
+
288
+ The package is configured with
289
+ [npm trusted publishing](https://docs.npmjs.com/trusted-publishers/) and this
290
+ publisher identity:
291
+
292
+ - Organization or user: `chiz0me`
293
+ - Repository: `actions-warden`
294
+ - Workflow filename: `release.yml`
295
+ - Environment: blank
296
+
297
+ The workflow grants only the publish job `id-token: write`, uses a GitHub-hosted
298
+ runner, disables package-manager caching, and pins Node `24.18.0` plus npm
299
+ `12.0.2`. That Node version satisfies npm `12.0.2`'s engine range. npm trusted
300
+ publishing currently requires npm 11.5.1 or newer and Node 22.14.0 or newer.
301
+ Update both pins deliberately when requirements or security fixes change;
302
+ never replace them with mutable `latest` installs in the release job. The
303
+ publish command ignores package lifecycle scripts after the tagged source has
304
+ passed validation and requests an npm
305
+ [provenance statement](https://docs.npmjs.com/generating-provenance-statements/).
306
+
307
+ After OIDC publication is verified, configure npm package access to require 2FA
308
+ and disallow token-based publishing where the account policy permits it. If a
309
+ GitHub environment is added to the publish job later, the npm trusted publisher
310
+ environment must be updated to the exact same name.
311
+
312
+ ### `MARKETPLACE_SYNC_TOKEN`
313
+
314
+ Cross-repository writes require a fine-grained personal access token because
315
+ the source repository's default `GITHUB_TOKEN` cannot write to
316
+ `chiz0me/claude-plugins`.
317
+
318
+ 1. Limit repository access to `chiz0me/claude-plugins`.
319
+ 2. Grant only **Contents: Read and write**.
320
+ 3. Store it in `chiz0me/actions-warden` as the Actions secret
321
+ `MARKETPLACE_SYNC_TOKEN`.
322
+ 4. Rotate or replace it without placing its value in logs, issues, prompts, or
323
+ this repository.
324
+
325
+ ### Repository protections
326
+
327
+ For stronger remote enforcement, protect `main` with required CI and review
328
+ rules appropriate to the project. Add a tag ruleset that prevents update and
329
+ deletion of immutable `v*.*.*` tags while still allowing maintainers to create
330
+ new versions and the release workflow to move only floating `vN` tags. Do not
331
+ apply an immutable-tag rule broadly enough to block `vN`.
332
+
333
+ A protected publish environment is optional. Adding one requires the exact
334
+ environment name in both `publish-npm.environment` and npm's trusted publisher
335
+ settings, so make those changes together and test them before relying on the
336
+ next release. GitHub Action Marketplace enrollment is a one-time manual action
337
+ from a successfully published release; `action.yml` already contains the
338
+ required branding metadata.
package/SECURITY.md CHANGED
@@ -12,7 +12,7 @@ Please include:
12
12
 
13
13
  - A clear description of the issue.
14
14
  - A minimal reproduction (workflow YAML, command line, expected vs. actual).
15
- - The version of `actions-warden` (`npx actions-warden --version`).
15
+ - The version of `actions-warden` (`npx --yes actions-warden@0.3.0 --version`).
16
16
  - Any disclosure constraints on your side.
17
17
 
18
18
  We aim to acknowledge within **3 business days** and to ship a fix or a
@@ -22,10 +22,13 @@ documented mitigation within **30 days** of triage for high-severity issues.
22
22
 
23
23
  In scope:
24
24
 
25
- - Path traversal or arbitrary file write in `pin`, `upgrade`, or `report`.
26
- - Bypasses of the `--dry-run` guard.
25
+ - Path traversal or arbitrary file write in `pin`, `upgrade`, `report`, or
26
+ organization checkpoint/report output.
27
+ - Bypasses of the explicit `--write` authorization boundary.
27
28
  - Logging or persisting credentials anywhere (cache, output, stderr).
28
29
  - Workflow parser crashes on attacker-controlled YAML.
30
+ - Organization scans that silently omit repositories or workflow files while
31
+ reporting complete coverage.
29
32
  - Network requests sent to hosts other than `api.github.com`.
30
33
 
31
34
  Out of scope:
@@ -40,7 +43,26 @@ Out of scope:
40
43
  - All runtime dependencies are pinned to exact versions (`save-exact=true`).
41
44
  - `ignore-scripts=true` in `.npmrc` to disable arbitrary postinstall scripts.
42
45
  - CI runs `npm audit --audit-level=high` on every PR.
46
+ - CI lints the source, runs tests across Linux, macOS, Windows, and supported
47
+ Node.js versions, and verifies the committed Action bundle is reproducible.
43
48
  - Releases are published with `npm publish --provenance`.
49
+ - The bundled Action runs on GitHub's managed Node.js runtime and does not
50
+ install dependencies in consumer workflows.
51
+ - Repository policy and baseline files should be protected with CODEOWNERS or
52
+ branch rules; changes to them can intentionally alter which findings fail CI.
53
+ - Parser failures are operational errors and cannot be suppressed by a finding
54
+ baseline.
55
+ - Organization scans read count- and size-bounded Git blobs in memory and never
56
+ clone, check out, or execute code from scanned repositories. Truncated trees,
57
+ exceeded limits, and unreadable blobs are operational errors.
58
+ - Organization checkpoints are opt-in, guarded atomic writes. They omit tokens
59
+ and raw workflow YAML, validate tool/rule/scope/policy identity, and reuse an
60
+ error-free result only after a fresh repository/default-branch tree-SHA
61
+ match. They still contain redacted repository and finding evidence and should
62
+ be protected like organization reports.
63
+ - Explicit CLI agent mode creates scope-keyed report and checkpoint files with
64
+ the same guarded writer and `0600` creation mode. Its stdout receipt contains
65
+ only paths and bounded counts; the artifact files remain sensitive.
44
66
 
45
67
  ## Responsible-disclosure timeline
46
68