actions-warden 0.2.0 → 0.4.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 (63) hide show
  1. package/AGENTS.md +199 -0
  2. package/CONTRIBUTING.md +109 -0
  3. package/README.md +284 -224
  4. package/RELEASING.md +338 -0
  5. package/SECURITY.md +30 -3
  6. package/docs/AI-AGENTS.md +474 -0
  7. package/docs/CLI.md +474 -0
  8. package/docs/CONFIGURATION.md +340 -0
  9. package/docs/DEVELOPMENT.md +373 -0
  10. package/docs/GITHUB-ACTION.md +285 -0
  11. package/docs/JAVASCRIPT-API.md +357 -0
  12. package/docs/OUTPUTS.md +414 -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 +151 -40
  19. package/src/action.js +306 -0
  20. package/src/cli.js +494 -56
  21. package/src/commands/audit.js +189 -36
  22. package/src/commands/org-scan.js +544 -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 +174 -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 +461 -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/path-equality.js +30 -0
  45. package/src/lib/paths.js +35 -12
  46. package/src/lib/redact.js +65 -4
  47. package/src/lib/resolver.js +225 -43
  48. package/src/lib/targets.js +28 -0
  49. package/src/lib/triggers.js +12 -0
  50. package/src/lib/writer.js +48 -8
  51. package/src/rules/excessive-permissions.js +24 -33
  52. package/src/rules/index.js +19 -1
  53. package/src/rules/pull-request-target-checkout.js +149 -18
  54. package/src/rules/reusable-workflow-secrets.js +32 -0
  55. package/src/rules/script-injection.js +77 -12
  56. package/src/rules/secrets-in-env.js +101 -18
  57. package/src/rules/unpinned-action.js +3 -2
  58. package/src/rules/unpinned-container-image.js +39 -0
  59. package/src/rules/unpinned-docker-action.js +30 -0
  60. package/src/rules/untrusted-self-hosted-runner.js +109 -0
  61. package/src/rules/workflow-run-artifact-execution.js +122 -0
  62. package/src/rules/workflow-structure.js +396 -0
  63. package/src/version.js +3 -0
@@ -0,0 +1,357 @@
1
+ # JavaScript API
2
+
3
+ actions-warden is an ECMAScript module for Node.js 20 or newer. Public command
4
+ functions return structured objects and do not write reports to stdout.
5
+
6
+ ```sh
7
+ npm install actions-warden
8
+ ```
9
+
10
+ ```js
11
+ import { audit } from 'actions-warden';
12
+
13
+ const result = await audit({
14
+ cwd: '/path/to/repository',
15
+ severity: 'high',
16
+ explain: true,
17
+ });
18
+
19
+ if (result.status === 'FAIL') {
20
+ for (const finding of result.findings) {
21
+ console.log(finding.id, finding.ruleId, finding.file, finding.line);
22
+ }
23
+ }
24
+ ```
25
+
26
+ From CommonJS, use a dynamic import:
27
+
28
+ ```js
29
+ const { audit } = await import('actions-warden');
30
+ ```
31
+
32
+ The raw API result does not include `schemaVersion`; renderers add the versioned
33
+ wire format.
34
+
35
+ ## Command functions
36
+
37
+ ### `audit(options)`
38
+
39
+ ```js
40
+ const result = await audit({
41
+ cwd,
42
+ workflows, // string[] of files, directories, or globs
43
+ severity, // low | medium | high | critical
44
+ explain, // boolean
45
+ configPath, // string, false to disable policy, or undefined for discovery
46
+ baseline, // baseline path
47
+ ignoreBaseline, // boolean; primarily used while creating a baseline
48
+ });
49
+ ```
50
+
51
+ Returns:
52
+
53
+ ```js
54
+ {
55
+ files,
56
+ findings,
57
+ allFindings,
58
+ summary,
59
+ baseline,
60
+ configPath,
61
+ status
62
+ }
63
+ ```
64
+
65
+ `findings` reflects severity and baseline filtering. `allFindings` is the
66
+ unfiltered rule output used to create a baseline.
67
+
68
+ ### `auditSources(options)`
69
+
70
+ Audit caller-supplied YAML without writing it to disk:
71
+
72
+ ```js
73
+ import { auditSources, DEFAULT_CONFIG } from 'actions-warden';
74
+
75
+ const result = await auditSources({
76
+ cwd: '/virtual/root',
77
+ sources: [
78
+ {
79
+ file: '/virtual/root/acme/service/.github/workflows/ci.yml',
80
+ source: workflowYaml,
81
+ },
82
+ ],
83
+ severity: 'high',
84
+ explain: true,
85
+ config: DEFAULT_CONFIG,
86
+ });
87
+ ```
88
+
89
+ Every source needs a unique string `file` and string `source`. The function uses
90
+ the same parser, ignore directives, identities, and rules as local audit. It
91
+ does not execute supplied YAML.
92
+
93
+ When passing a custom config, use the normalized shape returned by `loadConfig`,
94
+ not raw YAML keys.
95
+
96
+ ### `pin(options)`
97
+
98
+ ```js
99
+ const result = await pin({
100
+ cwd,
101
+ workflows,
102
+ dryRun: true, // default
103
+ token,
104
+ fix, // optional stable change ID
105
+ });
106
+ ```
107
+
108
+ Returns `{ changes, errors, status }`. Set `dryRun: false` only after explicit
109
+ write authorization.
110
+
111
+ ### `verify(options)`
112
+
113
+ ```js
114
+ const result = await verify({ cwd, workflows, token });
115
+ ```
116
+
117
+ Returns `{ files, checks, warnings, errors, status }`. Warnings do not make the
118
+ status fail.
119
+
120
+ ### `upgrade(options)`
121
+
122
+ ```js
123
+ const result = await upgrade({
124
+ cwd,
125
+ workflows,
126
+ dryRun: true, // default
127
+ token,
128
+ mode: 'minor', // major | minor | patch
129
+ minAgeDays: 7,
130
+ fix,
131
+ });
132
+ ```
133
+
134
+ Returns `{ changes, skipped, errors, status }`.
135
+
136
+ ### `report(options)`
137
+
138
+ ```js
139
+ const result = await report({
140
+ cwd,
141
+ workflows,
142
+ token,
143
+ mode: 'minor',
144
+ severity: 'high',
145
+ explain: true,
146
+ skipResolve: false,
147
+ minAgeDays: 7,
148
+ configPath,
149
+ baseline,
150
+ });
151
+ ```
152
+
153
+ Returns `{ audit, pin, upgrade, offline, status }`. Both mutation phases are
154
+ always dry-runs.
155
+
156
+ ### `scanOrganization(options)`
157
+
158
+ ```js
159
+ const result = await scanOrganization({
160
+ organization: 'my-org',
161
+ cwd: process.cwd(),
162
+ token: process.env.GITHUB_TOKEN,
163
+ repositories: ['service-*', 'my-org/platform-*'],
164
+ visibility: 'all',
165
+ includeArchived: false,
166
+ includeDisabled: false,
167
+ includeForks: false,
168
+ maxRepositories: 100,
169
+ concurrency: 4,
170
+ severity: 'high',
171
+ explain: true,
172
+ configPath: '.actions-warden.yml',
173
+ baseline: '.actions-warden-baseline.json',
174
+ checkpointPath: '.actions-warden-org-checkpoint.json',
175
+ resume: false,
176
+ onProgress(event) {
177
+ console.error(event.type, event.repository ?? '');
178
+ },
179
+ });
180
+ ```
181
+
182
+ Returns:
183
+
184
+ ```js
185
+ {
186
+ organization,
187
+ scope,
188
+ repositories,
189
+ findings,
190
+ errors,
191
+ summary,
192
+ baseline,
193
+ configPath,
194
+ status
195
+ }
196
+ ```
197
+
198
+ `findings` and `errors` are flattened for ingestion. `repositories` retains
199
+ per-repository identity, revision, files, findings, errors, summary, and status.
200
+
201
+ The organization scan is read-only and does not clone, check out, execute, or
202
+ persist raw remote workflow sources. Repository failures are accumulated when
203
+ possible; failures that prevent organization discovery throw.
204
+
205
+ `checkpointPath` explicitly enables atomic checkpoint writes inside `cwd`.
206
+ Set `resume: true` to require, validate, and update an existing checkpoint at
207
+ that path. The organization, selection and audit options, normalized policy,
208
+ baseline contents, analysis generation, and rule catalog must match.
209
+ Concurrency, token, and compatible package-version changes are allowed. The
210
+ producing package version remains metadata, and compatible older checkpoints
211
+ are atomically migrated on the first successful resume. Resume performs fresh
212
+ discovery and tree reads; it reuses only error-free results whose repository,
213
+ default branch, and tree SHA remain unchanged. Checkpoints contain redacted
214
+ report data and revision metadata, never tokens or raw YAML.
215
+
216
+ `onProgress` may be synchronous or asynchronous and is awaited in event order
217
+ at each emission point. A callback error rejects the scan; repository results
218
+ whose completion event was reached have already been checkpointed. Event
219
+ objects are shallow-frozen and use these `type` values:
220
+
221
+ | type | important fields |
222
+ |---|---|
223
+ | `scan-started` | `organization` |
224
+ | `checkpoint-loaded` | `repositories` |
225
+ | `checkpoint-created` | none |
226
+ | `discovery-started` | `organization` |
227
+ | `discovery-completed` | `discovered`, `eligible`, `selected` |
228
+ | `repository-started` | `repository`, `position`, `total` |
229
+ | `request-retry` | optional `repository`, `attempt`, `maxRetries`, `reason`, `delayMs`, optional `status` |
230
+ | `repository-completed` | `repository`, `completed`, `total`, `reused`, `status`, `files`, `findings`, `errors` |
231
+ | `scan-completed` | `organization`, `status`, `completed`, `total`, `reused`, `findings`, `errors`, `elapsedMs` |
232
+
233
+ Progress is observational and is not included in the returned result or its
234
+ rendered wire formats.
235
+
236
+ The CLI's `--agent-mode` is an output-routing adapter, not a
237
+ `scanOrganization()` option. JavaScript callers already control checkpoint
238
+ paths, progress callbacks, rendering, persistence, and how much of the returned
239
+ object enters an AI context. Implement the same bounded behavior by saving the
240
+ rendered report and returning only `status`, `summary`, and the saved path to
241
+ the agent.
242
+
243
+ ## Renderers
244
+
245
+ Use the matching renderer to obtain the CLI-compatible wire format:
246
+
247
+ ```js
248
+ import { audit, renderAudit } from 'actions-warden';
249
+
250
+ const result = await audit({ cwd: '/repo', explain: true });
251
+
252
+ const json = renderAudit(result, {
253
+ format: 'json',
254
+ explain: true,
255
+ cwd: '/repo',
256
+ });
257
+
258
+ const toon = renderAudit(result, {
259
+ format: 'toon',
260
+ explain: true,
261
+ cwd: '/repo',
262
+ });
263
+ ```
264
+
265
+ Command/render pairs are:
266
+
267
+ | command | renderer |
268
+ |---|---|
269
+ | `audit` | `renderAudit` |
270
+ | `pin` | `renderPin` |
271
+ | `upgrade` | `renderUpgrade` |
272
+ | `verify` | `renderVerify` |
273
+ | `report` | `renderReport` |
274
+ | `scanOrganization` | `renderOrganizationScan` |
275
+
276
+ Lower-level formatters are also exported: `format`, `renderToon`, `renderJson`,
277
+ `renderText`, `renderSarif`, `summarize`, and `SEVERITY_ORDER`.
278
+
279
+ Every renderer applies credential redaction. See [output contracts](./OUTPUTS.md)
280
+ before depending on the serialized structure.
281
+
282
+ ## Parser and policy utilities
283
+
284
+ The package root exports:
285
+
286
+ ```js
287
+ import {
288
+ collectImages,
289
+ collectUses,
290
+ discoverWorkflows,
291
+ isIgnored,
292
+ loadConfig,
293
+ parseActionRef,
294
+ parseIgnoreDirectives,
295
+ parseWorkflowFile,
296
+ parseWorkflowSource,
297
+ } from 'actions-warden';
298
+ ```
299
+
300
+ Additional exports include baseline helpers, organization GitHub readers
301
+ (`listOrganizationRepositories`, `fetchRepositoryWorkflowTree`, and
302
+ `fetchRepositoryWorkflows`), redaction, the live rule catalog, and organization
303
+ size limits. A validated workflow-tree snapshot can be supplied to
304
+ `fetchRepositoryWorkflows` to avoid a duplicate tree request. See
305
+ [src/index.js](../src/index.js) for the exact export list in the checked-out
306
+ version.
307
+
308
+ Command entry points are also available as explicit package subpaths:
309
+
310
+ ```js
311
+ import { audit } from 'actions-warden/commands/audit';
312
+ import { scanOrganization } from 'actions-warden/commands/org-scan';
313
+ ```
314
+
315
+ ## Error handling
316
+
317
+ Command functions use two error channels:
318
+
319
+ - per-file or per-repository operational problems are usually returned in an
320
+ `errors` array with status `FAIL`;
321
+ - invalid top-level input, unsafe paths, invalid policy, failed target
322
+ discovery, or inability to list an organization rejects the promise.
323
+
324
+ Handle both:
325
+
326
+ ```js
327
+ try {
328
+ const result = await scanOrganization(options);
329
+
330
+ if (result.status === 'FAIL') {
331
+ for (const error of result.errors) {
332
+ console.error(error.repository, error.path, error.error);
333
+ }
334
+ }
335
+ } catch (error) {
336
+ console.error('scan could not start:', error.message);
337
+ }
338
+ ```
339
+
340
+ Do not log tokens or raw credentials in an error handler. Built-in renderers
341
+ redact their output; arbitrary caller logging does not.
342
+
343
+ ## Concurrency
344
+
345
+ Network-backed helpers use bounded concurrency. `pin` and `verify` resolve up to
346
+ four references at once. Organization scans accept `concurrency` from 1 through
347
+ 16 and default to 4.
348
+
349
+ Do not mutate shared workflow files concurrently through multiple `pin` or
350
+ `upgrade` calls. Run a single plan/write cycle for a repository.
351
+
352
+ ## Related guides
353
+
354
+ - [CLI reference](./CLI.md)
355
+ - [Output contracts](./OUTPUTS.md)
356
+ - [AI and coding agents](./AI-AGENTS.md)
357
+ - [Developer guide](./DEVELOPMENT.md)