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.
- package/AGENTS.md +199 -0
- package/CONTRIBUTING.md +109 -0
- package/README.md +284 -224
- package/RELEASING.md +338 -0
- package/SECURITY.md +30 -3
- package/docs/AI-AGENTS.md +474 -0
- package/docs/CLI.md +474 -0
- package/docs/CONFIGURATION.md +340 -0
- package/docs/DEVELOPMENT.md +373 -0
- package/docs/GITHUB-ACTION.md +285 -0
- package/docs/JAVASCRIPT-API.md +357 -0
- package/docs/OUTPUTS.md +414 -0
- package/docs/README.md +27 -0
- package/examples/org-scan.yml +42 -0
- package/examples/upgrade-pr.yml +57 -0
- package/llms.txt +38 -0
- package/package.json +32 -10
- package/skills/actions-warden/SKILL.md +151 -40
- package/src/action.js +306 -0
- package/src/cli.js +494 -56
- package/src/commands/audit.js +189 -36
- package/src/commands/org-scan.js +544 -0
- package/src/commands/pin.js +59 -56
- package/src/commands/report.js +122 -10
- package/src/commands/upgrade.js +102 -62
- package/src/commands/verify.js +193 -0
- package/src/index.js +21 -4
- package/src/lib/action-status.js +27 -0
- package/src/lib/agent-mode.js +174 -0
- package/src/lib/annotations.js +250 -0
- package/src/lib/baseline.js +103 -0
- package/src/lib/cache.js +47 -10
- package/src/lib/concurrency.js +27 -0
- package/src/lib/config.js +185 -0
- package/src/lib/execution.js +71 -0
- package/src/lib/formatter.js +127 -8
- package/src/lib/github-org.js +374 -0
- package/src/lib/identity.js +62 -0
- package/src/lib/ignore.js +7 -6
- package/src/lib/org-checkpoint.js +461 -0
- package/src/lib/org-progress.js +60 -0
- package/src/lib/parser.js +326 -52
- package/src/lib/patcher.js +199 -0
- package/src/lib/path-equality.js +30 -0
- package/src/lib/paths.js +35 -12
- package/src/lib/redact.js +65 -4
- package/src/lib/resolver.js +225 -43
- package/src/lib/targets.js +28 -0
- package/src/lib/triggers.js +12 -0
- package/src/lib/writer.js +48 -8
- package/src/rules/excessive-permissions.js +24 -33
- package/src/rules/index.js +19 -1
- package/src/rules/pull-request-target-checkout.js +149 -18
- package/src/rules/reusable-workflow-secrets.js +32 -0
- package/src/rules/script-injection.js +77 -12
- package/src/rules/secrets-in-env.js +101 -18
- package/src/rules/unpinned-action.js +3 -2
- package/src/rules/unpinned-container-image.js +39 -0
- package/src/rules/unpinned-docker-action.js +30 -0
- package/src/rules/untrusted-self-hosted-runner.js +109 -0
- package/src/rules/workflow-run-artifact-execution.js +122 -0
- package/src/rules/workflow-structure.js +396 -0
- 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)
|