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,373 @@
|
|
|
1
|
+
# Developer guide
|
|
2
|
+
|
|
3
|
+
This guide is for contributors changing actions-warden itself. For public CLI
|
|
4
|
+
usage, start with the [CLI reference](./CLI.md).
|
|
5
|
+
|
|
6
|
+
## Prerequisites
|
|
7
|
+
|
|
8
|
+
- Node.js 20 or newer;
|
|
9
|
+
- npm with the lockfile committed by the repository;
|
|
10
|
+
- optional: `prek` or Python `pre-commit` for local hooks.
|
|
11
|
+
|
|
12
|
+
Install exactly the locked dependency graph:
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
npm ci
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The repository's `.npmrc` pins dependencies exactly and disables package
|
|
19
|
+
lifecycle scripts during installation.
|
|
20
|
+
|
|
21
|
+
Install hooks with either runner:
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
prek install --hook-type pre-commit --hook-type pre-push
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
or:
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
pre-commit install --hook-type pre-commit
|
|
31
|
+
pre-commit install --hook-type pre-push
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Validation commands
|
|
35
|
+
|
|
36
|
+
| command | purpose |
|
|
37
|
+
|---|---|
|
|
38
|
+
| `npm test` | Run the complete Vitest suite once |
|
|
39
|
+
| `npm run test:watch` | Run Vitest in watch mode |
|
|
40
|
+
| `npm run lint` | Lint source, scripts, and tests |
|
|
41
|
+
| `npm run check:yaml` | Parse repository YAML files |
|
|
42
|
+
| `npm run check:docs` | Validate local documentation links |
|
|
43
|
+
| `npm run check:package` | Inspect the exact npm tarball manifest, required files, executable mode, and size bounds |
|
|
44
|
+
| `npm run verify-deps` | Require exact runtime and development versions |
|
|
45
|
+
| `npm run verify-version-sync` | Keep package, lockfile, bundled runtime, plugin, and public invocations aligned |
|
|
46
|
+
| `npm run build:action` | Rebuild the committed Action bundle |
|
|
47
|
+
| `npm run check:action-bundle` | Compare a clean rebuild with working and staged bundles |
|
|
48
|
+
| `npm run audit` | Run the configured npm vulnerability threshold |
|
|
49
|
+
| `npm run release:prepare -- X.Y.Z` | Update all local stable-version sources without committing or publishing |
|
|
50
|
+
| `npm run release:check` | Gate a staged release candidate against npm state and every release validation |
|
|
51
|
+
|
|
52
|
+
Before opening a pull request, run:
|
|
53
|
+
|
|
54
|
+
```sh
|
|
55
|
+
npm run verify-version-sync
|
|
56
|
+
npm run verify-deps
|
|
57
|
+
npm run check:yaml
|
|
58
|
+
npm run check:docs
|
|
59
|
+
npm run check:package
|
|
60
|
+
npm run lint
|
|
61
|
+
npm test
|
|
62
|
+
npm run audit
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
If code reachable from `src/action.js` changed, also run:
|
|
66
|
+
|
|
67
|
+
```sh
|
|
68
|
+
npm run build:action
|
|
69
|
+
npm run check:action-bundle
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Repository map
|
|
73
|
+
|
|
74
|
+
```text
|
|
75
|
+
src/
|
|
76
|
+
cli.js CLI parsing, exit codes, and output routing
|
|
77
|
+
action.js GitHub Action input/output adapter
|
|
78
|
+
index.js public JavaScript exports
|
|
79
|
+
version.js runtime version embedded in the Action bundle
|
|
80
|
+
commands/ audit, pin, verify, upgrade, report, org-scan
|
|
81
|
+
lib/
|
|
82
|
+
parser.js YAML-to-normalized-workflow model
|
|
83
|
+
targets.js, paths.js repository target discovery
|
|
84
|
+
path-equality.js real-path destination/control comparisons
|
|
85
|
+
config.js, baseline.js policy and accepted-finding controls
|
|
86
|
+
resolver.js, cache.js GitHub API, caching, ref and ownership checks
|
|
87
|
+
github-org.js read-only organization/tree/blob access
|
|
88
|
+
agent-mode.js explicit bounded AI-agent CLI defaults
|
|
89
|
+
org-checkpoint.js validated atomic organization resume state
|
|
90
|
+
org-progress.js human rendering of structured scan progress
|
|
91
|
+
identity.js stable IDs and semantic fingerprints
|
|
92
|
+
patcher.js, writer.js source-range rewrites and guarded atomic writes
|
|
93
|
+
formatter.js, redact.js structured output and credential redaction
|
|
94
|
+
annotations.js GitHub workflow annotations
|
|
95
|
+
concurrency.js bounded async work
|
|
96
|
+
rules/ one module per audit rule
|
|
97
|
+
|
|
98
|
+
test/ Vitest suites and hostile/edge-case fixtures
|
|
99
|
+
scripts/ repository validation and release helpers
|
|
100
|
+
docs/ user, integration, AI, and developer guides
|
|
101
|
+
examples/ copyable GitHub workflow examples
|
|
102
|
+
skills/actions-warden/ Claude Code skill
|
|
103
|
+
dist/ generated, committed GitHub Action bundle
|
|
104
|
+
action.yml public Action inputs, outputs, and runtime
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Data flow
|
|
108
|
+
|
|
109
|
+
A local audit follows this path:
|
|
110
|
+
|
|
111
|
+
```text
|
|
112
|
+
targets → source read → YAML parser → normalized workflow model
|
|
113
|
+
→ ignore directives → rules → stable identities
|
|
114
|
+
→ severity/baseline filtering → renderer
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Pin and upgrade add:
|
|
118
|
+
|
|
119
|
+
```text
|
|
120
|
+
normalized action refs → GitHub resolution and ownership verification
|
|
121
|
+
→ source-range patch plan → reparse
|
|
122
|
+
→ guarded atomic writer only when dryRun=false
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
An organization scan follows:
|
|
126
|
+
|
|
127
|
+
```text
|
|
128
|
+
repository listing → default-branch Git tree → bounded YAML blob reads
|
|
129
|
+
→ in-memory auditSources → repository aggregation
|
|
130
|
+
|
|
131
|
+
optional checkpoint → identity validation → fresh tree SHA comparison
|
|
132
|
+
→ reuse unchanged error-free result or rescan
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Remote repositories are never cloned, checked out, imported, or executed.
|
|
136
|
+
|
|
137
|
+
## Safety invariants
|
|
138
|
+
|
|
139
|
+
Changes must preserve these boundaries:
|
|
140
|
+
|
|
141
|
+
- `pin` and `upgrade` default to dry-run at the command and API layers.
|
|
142
|
+
- CLI and Action mutation require an explicit write option.
|
|
143
|
+
- CLI integers and 16-hex change IDs use strict whole-value parsing. Commander
|
|
144
|
+
usage failures and application-level invocation failures both exit `2`.
|
|
145
|
+
- A resolver failure must not fall back to a guessed tag or SHA.
|
|
146
|
+
- Commits must be verified as belonging to the referenced repository.
|
|
147
|
+
- Workflow rewrites operate on parsed scalar ranges and reparse before write.
|
|
148
|
+
- Writes and configured paths remain inside the real repository root.
|
|
149
|
+
- Symlink escapes and explicit targets that match nothing fail.
|
|
150
|
+
- CLI report, baseline, and checkpoint destinations are preflighted before
|
|
151
|
+
network or workflow mutation, cannot replace selected/default-discovery
|
|
152
|
+
workflows or reserved policy paths, and are checked against active controls
|
|
153
|
+
again before output is written.
|
|
154
|
+
- Credentials are redacted in JSON, TOON, text, SARIF, annotations, and
|
|
155
|
+
top-level errors.
|
|
156
|
+
- Finding and change IDs exclude absolute checkout paths.
|
|
157
|
+
- Parser errors remain findings and cannot be baselined away.
|
|
158
|
+
- Organization coverage failures remain visible and make status fail.
|
|
159
|
+
- Organization source reads remain count- and size-bounded and bypass disk
|
|
160
|
+
cache.
|
|
161
|
+
- Organization resume never trusts a stale result without fresh discovery and
|
|
162
|
+
a matching repository identity, default branch, and tree SHA. Failed results
|
|
163
|
+
are never reused.
|
|
164
|
+
- Organization result compatibility is controlled by
|
|
165
|
+
`ORGANIZATION_ANALYSIS_GENERATION`, not by the package version. Producer
|
|
166
|
+
versions remain checkpoint metadata and compatible checkpoints migrate
|
|
167
|
+
through the guarded atomic writer.
|
|
168
|
+
- Checkpoints use guarded atomic writes, remain inside the working directory,
|
|
169
|
+
omit tokens and raw YAML, and cannot replace active policy or baseline files.
|
|
170
|
+
- Progress stays outside deterministic report serialization; CLI progress is
|
|
171
|
+
stderr-only and Action progress cannot form workflow commands.
|
|
172
|
+
- Agent mode is an explicit opt-in, never a TTY, parent-process, CI, or
|
|
173
|
+
vendor-environment heuristic. Explicit CLI output and progress options win.
|
|
174
|
+
Its automatic artifact key uses the validated checkpoint compatibility
|
|
175
|
+
identity, and its stdout receipt never includes findings or repository result
|
|
176
|
+
arrays.
|
|
177
|
+
- Attacker-controlled values cannot forge TOON lines or GitHub annotations.
|
|
178
|
+
|
|
179
|
+
Tests should demonstrate the fail-closed behavior for any change touching these
|
|
180
|
+
boundaries.
|
|
181
|
+
|
|
182
|
+
## Adding or changing a rule
|
|
183
|
+
|
|
184
|
+
A rule module exports:
|
|
185
|
+
|
|
186
|
+
```js
|
|
187
|
+
export const id = 'example-rule';
|
|
188
|
+
export const severity = 'high';
|
|
189
|
+
export const description = 'Short sentence describing the risk.';
|
|
190
|
+
|
|
191
|
+
export function check(workflow, context = {}) {
|
|
192
|
+
return [{
|
|
193
|
+
id,
|
|
194
|
+
severity,
|
|
195
|
+
line: 1,
|
|
196
|
+
fields: {
|
|
197
|
+
type: id,
|
|
198
|
+
sev: severity,
|
|
199
|
+
evidence: 'structured-value',
|
|
200
|
+
},
|
|
201
|
+
explain: 'one concise remediation',
|
|
202
|
+
}];
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Then:
|
|
207
|
+
|
|
208
|
+
1. register the module in `src/rules/index.js`;
|
|
209
|
+
2. add focused tests for safe and unsafe cases;
|
|
210
|
+
3. test trigger, expression, quoting, and malformed-input boundaries relevant
|
|
211
|
+
to the rule;
|
|
212
|
+
4. when behavior depends on a moving GitHub or first-party action contract,
|
|
213
|
+
verify it against a primary source, record the date/version boundary in code,
|
|
214
|
+
and test the last unsafe plus first safe version;
|
|
215
|
+
5. test at least one legitimate pattern that must remain clean so remediation
|
|
216
|
+
guidance does not become a false-positive generator;
|
|
217
|
+
6. update the rule table and accuracy boundaries in `docs/CONFIGURATION.md`,
|
|
218
|
+
plus the Claude skill;
|
|
219
|
+
7. run `actions-warden rules --format=json` and ensure the ID is not redacted;
|
|
220
|
+
8. rebuild the Action bundle.
|
|
221
|
+
|
|
222
|
+
Keep `fields` structured and concise. They become JSON evidence, TOON fields,
|
|
223
|
+
SARIF messages, and Action annotations. Never place raw secret values in them.
|
|
224
|
+
Suggestions must name a safe end state, preserve legitimate use cases, and
|
|
225
|
+
avoid claiming that one mitigation closes risks outside the rule's evidence.
|
|
226
|
+
|
|
227
|
+
## Adding or changing a command
|
|
228
|
+
|
|
229
|
+
A command normally needs changes in:
|
|
230
|
+
|
|
231
|
+
1. `src/commands/<name>.js` for the API result and renderer;
|
|
232
|
+
2. `src/cli.js` for public CLI flags and exit semantics;
|
|
233
|
+
3. `src/index.js` and `package.json` exports;
|
|
234
|
+
4. `src/action.js` and `action.yml` if the Action exposes it;
|
|
235
|
+
5. command, CLI, Action, annotation, and output-format tests;
|
|
236
|
+
6. `docs/CLI.md`, `docs/OUTPUTS.md`, API docs, and relevant examples;
|
|
237
|
+
7. the Claude skill and version-sync checker when its command surface changes;
|
|
238
|
+
8. the committed Action bundle.
|
|
239
|
+
|
|
240
|
+
Return operational problems as structured `errors` when useful work can
|
|
241
|
+
continue. Throw when top-level input or discovery makes the requested scope
|
|
242
|
+
undefined or unsafe.
|
|
243
|
+
|
|
244
|
+
Every non-JSON renderer must expose enough information to explain a `FAIL`
|
|
245
|
+
status. Every JSON renderer must include `schemaVersion`, command data, and
|
|
246
|
+
top-level `status`.
|
|
247
|
+
|
|
248
|
+
## Parser and patcher changes
|
|
249
|
+
|
|
250
|
+
The parser retains both a normalized model for rules and source locations for
|
|
251
|
+
precise rewrites. When changing it:
|
|
252
|
+
|
|
253
|
+
- cover workflow files and composite `action.yml` metadata;
|
|
254
|
+
- preserve and recursively inspect steps inside `parallel` groups, while
|
|
255
|
+
retaining background/control-step declarations for structure checks;
|
|
256
|
+
- include quoted, unquoted, inline, multiline, and malformed YAML cases;
|
|
257
|
+
- avoid evaluating expressions;
|
|
258
|
+
- preserve source offsets for patchable `uses` values;
|
|
259
|
+
- test Windows and POSIX path normalization where relevant.
|
|
260
|
+
|
|
261
|
+
The patcher should make the smallest source-range replacement. Do not reserialize
|
|
262
|
+
the entire YAML document: that would destroy comments, formatting, anchors, and
|
|
263
|
+
reviewable diffs.
|
|
264
|
+
|
|
265
|
+
Property tests in `test/patcher-properties.test.js` exercise rewrite stability.
|
|
266
|
+
|
|
267
|
+
## GitHub API changes
|
|
268
|
+
|
|
269
|
+
All runtime requests belong in the resolver or organization GitHub layer and
|
|
270
|
+
must remain pinned to `https://api.github.com`.
|
|
271
|
+
|
|
272
|
+
For new endpoints:
|
|
273
|
+
|
|
274
|
+
- validate status and response shape;
|
|
275
|
+
- paginate boundedly;
|
|
276
|
+
- validate identities, SHAs, sizes, encodings, and UTF-8 as applicable;
|
|
277
|
+
- set timeouts and bounded retries through the shared fetch layer;
|
|
278
|
+
- preserve authentication-isolated cache keys;
|
|
279
|
+
- decide explicitly whether private content may be cached;
|
|
280
|
+
- accumulate scoped errors when continued reporting is safe;
|
|
281
|
+
- add tests with mocked responses for truncation and malformed data.
|
|
282
|
+
|
|
283
|
+
Never execute content to determine what it contains.
|
|
284
|
+
|
|
285
|
+
When changing organization resume behavior, cover interrupted writes,
|
|
286
|
+
checkpoint schema and identity mismatches, hostile fields, changed tree SHAs,
|
|
287
|
+
previous repository errors, output equivalence, and concurrent completions.
|
|
288
|
+
Persist each completed result before emitting its completion event so an
|
|
289
|
+
observer failure or process interruption loses at most in-flight work.
|
|
290
|
+
|
|
291
|
+
`ORGANIZATION_ANALYSIS_GENERATION` in `src/lib/org-checkpoint.js` is the manual
|
|
292
|
+
semantic compatibility switch. Increment it whenever organization workflow
|
|
293
|
+
selection, parsing, finding identity, rule evaluation, suppression, summaries,
|
|
294
|
+
or other persisted repository-result semantics can change. Keep it unchanged
|
|
295
|
+
for documentation, release metadata, progress rendering, authentication,
|
|
296
|
+
concurrency, or internal refactors that preserve those results. The rule
|
|
297
|
+
catalog hash independently invalidates catalog changes. A generation change
|
|
298
|
+
must add tests proving the old checkpoint fails closed and agent mode selects a
|
|
299
|
+
new artifact key; a compatible format migration must prove the old checkpoint
|
|
300
|
+
is validated, rewritten atomically, and still subject to fresh tree checks.
|
|
301
|
+
|
|
302
|
+
## Output compatibility
|
|
303
|
+
|
|
304
|
+
JSON is the public machine interface. Raw command results and serialized JSON
|
|
305
|
+
are deliberately different: renderers add repository-relative paths,
|
|
306
|
+
redaction, and `schemaVersion`.
|
|
307
|
+
|
|
308
|
+
When changing output:
|
|
309
|
+
|
|
310
|
+
- retain top-level `status`;
|
|
311
|
+
- treat new fields as additive where possible;
|
|
312
|
+
- update `docs/OUTPUTS.md`;
|
|
313
|
+
- test all four formats;
|
|
314
|
+
- prevent embedded control characters from forging records;
|
|
315
|
+
- ensure error details are visible in every format;
|
|
316
|
+
- verify stable IDs remain stable unless their semantic input changed.
|
|
317
|
+
|
|
318
|
+
## GitHub Action bundle
|
|
319
|
+
|
|
320
|
+
Never edit `dist/index.js` or `dist/package.json` by hand. They are generated:
|
|
321
|
+
|
|
322
|
+
```sh
|
|
323
|
+
npm run build:action
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
Commit source and its rebuilt bundle together when the Action runtime changes.
|
|
327
|
+
`npm run check:action-bundle` performs a clean build in a temporary directory
|
|
328
|
+
and compares both working-tree and staged bundle files.
|
|
329
|
+
|
|
330
|
+
A docs-only, test-only, or CLI-only change that is unreachable from
|
|
331
|
+
`src/action.js` does not require a bundle rebuild.
|
|
332
|
+
|
|
333
|
+
## Tests and fixtures
|
|
334
|
+
|
|
335
|
+
Tests use Vitest and should be deterministic and network-independent. Mock
|
|
336
|
+
GitHub responses rather than relying on live repositories. Use temporary
|
|
337
|
+
directories for writes and assert both file content and failure behavior.
|
|
338
|
+
|
|
339
|
+
Useful focused runs:
|
|
340
|
+
|
|
341
|
+
```sh
|
|
342
|
+
npx vitest --run test/audit.test.js
|
|
343
|
+
npx vitest --run test/org-scan.test.js
|
|
344
|
+
npx vitest --run -t "stable id"
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
Keep hostile input in fixtures or inline strings when it clarifies the threat
|
|
348
|
+
being tested. Tests should not contain real credentials.
|
|
349
|
+
|
|
350
|
+
## Documentation maintenance
|
|
351
|
+
|
|
352
|
+
README is the landing page, not the full manual. Put durable detail in the
|
|
353
|
+
focused guide that owns it, then link from README.
|
|
354
|
+
|
|
355
|
+
When CLI, Action, config, output, or API behavior changes:
|
|
356
|
+
|
|
357
|
+
1. update the owning reference document;
|
|
358
|
+
2. update examples and AI guidance that depend on it;
|
|
359
|
+
3. run `npm run check:docs`;
|
|
360
|
+
4. run live `--help` and at least one documented example against
|
|
361
|
+
`node src/cli.js`.
|
|
362
|
+
|
|
363
|
+
Keep examples copyable, use exact package versions for `npx`, and use full
|
|
364
|
+
commit placeholders for the Action itself.
|
|
365
|
+
|
|
366
|
+
## Releases
|
|
367
|
+
|
|
368
|
+
The authoritative [release runbook](../RELEASING.md) defines maintainer and
|
|
369
|
+
agent authorization, SemVer selection, live-state preflight, preparation,
|
|
370
|
+
trusted publication, monitoring, verification, and partial-failure recovery.
|
|
371
|
+
Do not bump versions as part of an unrelated contribution. `release:prepare`
|
|
372
|
+
is local-only; `release:check` is intended for a reviewed, staged candidate and
|
|
373
|
+
will fail a stale or already-published version.
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
# GitHub Action guide
|
|
2
|
+
|
|
3
|
+
The bundled JavaScript Action runs the same commands as the CLI on GitHub's
|
|
4
|
+
managed Node 24 runtime. Consumer workflows do not run `npm install`.
|
|
5
|
+
|
|
6
|
+
## Repository audit
|
|
7
|
+
|
|
8
|
+
Pin every third-party Action, including actions-warden itself, to a reviewed
|
|
9
|
+
full commit SHA:
|
|
10
|
+
|
|
11
|
+
```yaml
|
|
12
|
+
name: actions-warden
|
|
13
|
+
|
|
14
|
+
on:
|
|
15
|
+
pull_request:
|
|
16
|
+
push:
|
|
17
|
+
branches: [main]
|
|
18
|
+
|
|
19
|
+
permissions:
|
|
20
|
+
contents: read
|
|
21
|
+
|
|
22
|
+
jobs:
|
|
23
|
+
audit:
|
|
24
|
+
runs-on: ubuntu-latest
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # actions-warden-ref: v5.0.0
|
|
27
|
+
with:
|
|
28
|
+
persist-credentials: false
|
|
29
|
+
|
|
30
|
+
- name: Audit workflows
|
|
31
|
+
uses: chiz0me/actions-warden@<FULL_COMMIT_SHA>
|
|
32
|
+
with:
|
|
33
|
+
command: audit
|
|
34
|
+
severity: high
|
|
35
|
+
explain: 'true'
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Findings are written to the log, job summary, and native annotations. Audit
|
|
39
|
+
findings fail the step by default.
|
|
40
|
+
|
|
41
|
+
## Inputs
|
|
42
|
+
|
|
43
|
+
GitHub passes Action inputs as strings. Boolean values should be quoted in YAML
|
|
44
|
+
for clarity.
|
|
45
|
+
|
|
46
|
+
| input | default | applies to | behavior |
|
|
47
|
+
|---|---|---|---|
|
|
48
|
+
| `command` | `audit` | all | `audit`, `pin`, `upgrade`, `verify`, `report`, `org-scan`, or `rules` |
|
|
49
|
+
| `workflow` | discovery | repository commands | One path/glob per line or a JSON string array |
|
|
50
|
+
| `severity` | all levels | `audit`, `report`, `org-scan` | Minimum `low`, `medium`, `high`, or `critical` |
|
|
51
|
+
| `format` | `toon` | all | `toon`, `json`, `text`, or `sarif` |
|
|
52
|
+
| `mode` | `minor` | `upgrade`, `report` | `major`, `minor`, or `patch` |
|
|
53
|
+
| `min-age` | `7` | `upgrade`, `report` | Non-negative cooldown in days |
|
|
54
|
+
| `write` | `false` | `pin`, `upgrade` | Apply changes in the checked-out workspace |
|
|
55
|
+
| `fix` | none | `pin`, `upgrade` | Limit work to one exact planned ID |
|
|
56
|
+
| `explain` | `false` | `audit`, `report`, `org-scan` | Include remediation hints |
|
|
57
|
+
| `offline` | `false` | `report` | Skip pin and upgrade network work |
|
|
58
|
+
| `fail-on-findings` | `true` | `audit`, `report`, `org-scan` | Allow findings to be advisory when `false` |
|
|
59
|
+
| `annotations` | `true` | all | Emit native workflow-command annotations |
|
|
60
|
+
| `config` | auto | `audit`, `report`, `org-scan` | Policy path inside the working directory |
|
|
61
|
+
| `ignore-config` | `false` | `audit`, `report`, `org-scan` | Ignore repository policy |
|
|
62
|
+
| `baseline` | policy/default | `audit`, `report`, `org-scan` | Accepted-finding baseline |
|
|
63
|
+
| `organization` | none | `org-scan` | Required organization login |
|
|
64
|
+
| `repository` | all eligible | `org-scan` | One repository glob per line or a JSON string array |
|
|
65
|
+
| `visibility` | `all` | `org-scan` | `all`, `public`, `private`, or `internal` |
|
|
66
|
+
| `include-archived` | `false` | `org-scan` | Include archived repositories |
|
|
67
|
+
| `include-disabled` | `false` | `org-scan` | Include disabled repositories |
|
|
68
|
+
| `include-forks` | `false` | `org-scan` | Include forked repositories |
|
|
69
|
+
| `max-repos` | none | `org-scan` | Positive repository limit |
|
|
70
|
+
| `concurrency` | `4` | `org-scan` | Concurrent repository scans from 1 through 16 |
|
|
71
|
+
| `checkpoint-path` | none | `org-scan` | Create or replace an atomic resumable checkpoint |
|
|
72
|
+
| `resume-from` | none | `org-scan` | Resume from and update an existing checkpoint |
|
|
73
|
+
| `progress` | `true` | `org-scan` | Show discovery, repository, retry, and completion updates in the step log |
|
|
74
|
+
| `output-path` | none | all | Save the selected format inside the working directory |
|
|
75
|
+
| `token` | none | network commands | GitHub API token; normally `${{ github.token }}` or a secret |
|
|
76
|
+
| `working-directory` | `$GITHUB_WORKSPACE` | all | Scan and path-safety root |
|
|
77
|
+
|
|
78
|
+
`node-version` is a deprecated compatibility input. It is accepted but ignored
|
|
79
|
+
because the Action runtime is declared by the bundle.
|
|
80
|
+
|
|
81
|
+
Multivalue inputs can be line-separated:
|
|
82
|
+
|
|
83
|
+
```yaml
|
|
84
|
+
with:
|
|
85
|
+
workflow: |
|
|
86
|
+
.github/workflows/release.yml
|
|
87
|
+
.github/actions
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
or JSON:
|
|
91
|
+
|
|
92
|
+
```yaml
|
|
93
|
+
with:
|
|
94
|
+
repository: '["service-*", "platform-*"]'
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Outputs and failure policy
|
|
98
|
+
|
|
99
|
+
| output | description |
|
|
100
|
+
|---|---|
|
|
101
|
+
| `status` | Semantic `OK` or `FAIL` |
|
|
102
|
+
| `findings` | Finding count for `audit`, `report`, and `org-scan` |
|
|
103
|
+
| `report-path` | Absolute saved path when `output-path` is supplied |
|
|
104
|
+
| `annotations` | Number of annotations emitted |
|
|
105
|
+
| `annotations-skipped` | Number omitted by the per-level cap |
|
|
106
|
+
|
|
107
|
+
Give the step an `id` to read outputs:
|
|
108
|
+
|
|
109
|
+
```yaml
|
|
110
|
+
- name: Audit workflows
|
|
111
|
+
id: warden
|
|
112
|
+
uses: chiz0me/actions-warden@<FULL_COMMIT_SHA>
|
|
113
|
+
with:
|
|
114
|
+
command: audit
|
|
115
|
+
fail-on-findings: 'false'
|
|
116
|
+
|
|
117
|
+
- name: Show result
|
|
118
|
+
run: echo "status=${{ steps.warden.outputs.status }} findings=${{ steps.warden.outputs.findings }}"
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
`fail-on-findings: 'false'` changes step failure only for security findings in
|
|
122
|
+
`audit`, `report`, and `org-scan`. It does not hide findings or change the
|
|
123
|
+
`status` output.
|
|
124
|
+
|
|
125
|
+
Operational failures always fail the step, including:
|
|
126
|
+
|
|
127
|
+
- invalid or unparseable workflow YAML;
|
|
128
|
+
- organization repository or blob read failures;
|
|
129
|
+
- ref resolution and ownership-verification failures;
|
|
130
|
+
- unsafe paths or writes;
|
|
131
|
+
- errors in the pin or upgrade phases of `report`.
|
|
132
|
+
|
|
133
|
+
`pin`, `upgrade`, and `verify` errors always fail. Warnings from `verify` do
|
|
134
|
+
not fail.
|
|
135
|
+
|
|
136
|
+
## Native annotations
|
|
137
|
+
|
|
138
|
+
Annotations are independent of `format`:
|
|
139
|
+
|
|
140
|
+
| result | annotation level |
|
|
141
|
+
|---|---|
|
|
142
|
+
| critical or high finding | error |
|
|
143
|
+
| medium finding | warning |
|
|
144
|
+
| low finding | notice |
|
|
145
|
+
| verification warning | warning |
|
|
146
|
+
| resolver, verification, scan, or write error | error |
|
|
147
|
+
|
|
148
|
+
Local findings attach to the repository-relative file and source line.
|
|
149
|
+
Organization findings belong to other repositories, so they omit a local file
|
|
150
|
+
attachment while retaining repository, remote path, source URL, rule, and
|
|
151
|
+
stable ID in the message and saved report.
|
|
152
|
+
|
|
153
|
+
The Action emits at most 10 annotations per level per step, with more severe
|
|
154
|
+
records first. All omitted records remain in normal output and saved reports.
|
|
155
|
+
Set `annotations: 'false'` to disable annotations without changing status or
|
|
156
|
+
failure behavior.
|
|
157
|
+
|
|
158
|
+
Workflow-command data is escaped and credential-like values are redacted before
|
|
159
|
+
emission.
|
|
160
|
+
|
|
161
|
+
## Save a report artifact
|
|
162
|
+
|
|
163
|
+
`output-path` writes the selected format in addition to stdout:
|
|
164
|
+
|
|
165
|
+
```yaml
|
|
166
|
+
- name: Generate report
|
|
167
|
+
id: warden
|
|
168
|
+
uses: chiz0me/actions-warden@<FULL_COMMIT_SHA>
|
|
169
|
+
with:
|
|
170
|
+
command: report
|
|
171
|
+
format: json
|
|
172
|
+
output-path: reports/actions-warden.json
|
|
173
|
+
fail-on-findings: 'false'
|
|
174
|
+
token: ${{ github.token }}
|
|
175
|
+
|
|
176
|
+
- name: Upload report
|
|
177
|
+
if: always()
|
|
178
|
+
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # actions-warden-ref: v5.0.0
|
|
179
|
+
with:
|
|
180
|
+
name: actions-warden-report
|
|
181
|
+
path: ${{ steps.warden.outputs.report-path }}
|
|
182
|
+
if-no-files-found: error
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
The output path must remain inside `working-directory`, its parent directory
|
|
186
|
+
must already exist, and an existing destination must be a regular file rather
|
|
187
|
+
than a directory or symlink.
|
|
188
|
+
|
|
189
|
+
## Organization report
|
|
190
|
+
|
|
191
|
+
The default repository `GITHUB_TOKEN` generally sees only the repository where
|
|
192
|
+
the workflow runs. Use a GitHub App installation token or a fine-grained token
|
|
193
|
+
whose repository access covers the intended organization scope.
|
|
194
|
+
|
|
195
|
+
```yaml
|
|
196
|
+
name: organization Actions report
|
|
197
|
+
|
|
198
|
+
on:
|
|
199
|
+
workflow_dispatch:
|
|
200
|
+
schedule:
|
|
201
|
+
- cron: '23 7 * * 1'
|
|
202
|
+
|
|
203
|
+
permissions:
|
|
204
|
+
contents: read
|
|
205
|
+
|
|
206
|
+
jobs:
|
|
207
|
+
scan:
|
|
208
|
+
runs-on: ubuntu-latest
|
|
209
|
+
steps:
|
|
210
|
+
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # actions-warden-ref: v5.0.0
|
|
211
|
+
with:
|
|
212
|
+
persist-credentials: false
|
|
213
|
+
|
|
214
|
+
- name: Scan organization
|
|
215
|
+
id: warden
|
|
216
|
+
uses: chiz0me/actions-warden@<FULL_COMMIT_SHA>
|
|
217
|
+
with:
|
|
218
|
+
command: org-scan
|
|
219
|
+
organization: ${{ github.repository_owner }}
|
|
220
|
+
token: ${{ secrets.ACTIONS_WARDEN_ORG_TOKEN }}
|
|
221
|
+
severity: high
|
|
222
|
+
explain: 'true'
|
|
223
|
+
format: json
|
|
224
|
+
output-path: actions-warden-org-report.json
|
|
225
|
+
checkpoint-path: .actions-warden-org-checkpoint.json
|
|
226
|
+
fail-on-findings: 'false'
|
|
227
|
+
|
|
228
|
+
- name: Upload organization report
|
|
229
|
+
if: always()
|
|
230
|
+
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # actions-warden-ref: v5.0.0
|
|
231
|
+
with:
|
|
232
|
+
name: actions-warden-org-report
|
|
233
|
+
path: |
|
|
234
|
+
actions-warden-org-report.json
|
|
235
|
+
.actions-warden-org-checkpoint.json
|
|
236
|
+
if-no-files-found: error
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Keep the token in Actions secrets, avoid printing it, and grant only metadata
|
|
240
|
+
and contents read access for selected repositories. The complete copyable file
|
|
241
|
+
is [examples/org-scan.yml](../examples/org-scan.yml).
|
|
242
|
+
|
|
243
|
+
The Action writes live progress to the step log separately from the selected
|
|
244
|
+
report format. Set `progress: 'false'` to disable it.
|
|
245
|
+
|
|
246
|
+
CLI `--agent-mode` is not an Action input. The Action already has explicit
|
|
247
|
+
`output-path`, checkpoint, progress, summary, and output channels; configure
|
|
248
|
+
those inputs directly when an agent generates a workflow.
|
|
249
|
+
|
|
250
|
+
`checkpoint-path` starts a new checkpoint. To resume, restore that file into
|
|
251
|
+
the working directory before the actions-warden step, remove
|
|
252
|
+
`checkpoint-path`, and set `resume-from` to the restored path. The Action does
|
|
253
|
+
not itself retain files between ephemeral runners; use a protected artifact or
|
|
254
|
+
other caller-managed storage. Scope, policy, baseline, analysis generation, and
|
|
255
|
+
rule identity must match; a compatible package-version change is allowed and
|
|
256
|
+
atomically refreshes checkpoint metadata on the first successful resume. Fresh
|
|
257
|
+
repository discovery and tree checks still occur, and changed or previously
|
|
258
|
+
failed repositories are rescanned. Checkpoints hold redacted report evidence
|
|
259
|
+
about repositories and findings, so protect them like the organization report.
|
|
260
|
+
`checkpoint-path` and `resume-from` are mutually exclusive and cannot equal
|
|
261
|
+
`output-path`.
|
|
262
|
+
|
|
263
|
+
## Mutation workflows
|
|
264
|
+
|
|
265
|
+
With `write: 'true'`, `pin` and `upgrade` modify only the runner's checked-out
|
|
266
|
+
working tree. The Action does not commit, push, or open a pull request.
|
|
267
|
+
|
|
268
|
+
A safe automation flow is:
|
|
269
|
+
|
|
270
|
+
1. run the command without `write` and retain the report;
|
|
271
|
+
2. require review or select one `fix` ID;
|
|
272
|
+
3. run with `write: 'true'`;
|
|
273
|
+
4. run `verify` and repository tests;
|
|
274
|
+
5. create a pull request using a separately reviewed workflow.
|
|
275
|
+
|
|
276
|
+
See [examples/upgrade-pr.yml](../examples/upgrade-pr.yml) for an opt-in,
|
|
277
|
+
cooldown-aware upgrade pull request workflow. It uses explicit contents and
|
|
278
|
+
pull-request write permissions only in the mutation job.
|
|
279
|
+
|
|
280
|
+
## Bundle integrity
|
|
281
|
+
|
|
282
|
+
The repository commits `dist/index.js` because GitHub Actions executes the
|
|
283
|
+
bundle directly. Releases verify that a clean rebuild matches the committed
|
|
284
|
+
bundle. Consumers should pin the Action to a full commit SHA, then use
|
|
285
|
+
actions-warden's metadata comment to retain the reviewed release name.
|