@intentius/chant-lexicon-github 0.57.0 → 0.58.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/dist/components/generate-op-pipeline.d.ts +22 -12
- package/dist/components/generate-op-pipeline.d.ts.map +1 -1
- package/dist/integrity.json +2 -2
- package/dist/lint/audit-catalog.d.ts.map +1 -1
- package/dist/lint/audit-lineage.d.ts +10 -0
- package/dist/lint/audit-lineage.d.ts.map +1 -0
- package/dist/manifest.json +1 -1
- package/package.json +2 -2
- package/src/components/generate-op-pipeline.test.ts +115 -2
- package/src/components/generate-op-pipeline.ts +69 -21
- package/src/components/generate-pipeline.test.ts +2 -2
- package/src/lint/audit-catalog.ts +5 -1
- package/src/lint/audit-lineage.ts +177 -0
|
@@ -4,21 +4,27 @@
|
|
|
4
4
|
* The Op counterpart to `./generate-pipeline.ts` (#891): that module
|
|
5
5
|
* synthesizes a `workflow_dispatch`-triggered pipeline from a deploy-time
|
|
6
6
|
* component graph, this one synthesizes a cron-triggered workflow per
|
|
7
|
-
* stateless Op
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* stateless Op. An Op's cadence is an `OpSchedule` on the Op itself
|
|
8
|
+
* (`packages/core/src/op/types.ts`), runtime-neutral data each reader
|
|
9
|
+
* interprets; this module is the reader that turns it into a cron a GitHub
|
|
10
|
+
* runner fires (`WorkflowAuditOp`, `PipelineAuditOp`, `ReconcileOp`, … all
|
|
11
|
+
* accept an optional `schedule` precisely for this).
|
|
11
12
|
*
|
|
12
13
|
* GitHub Actions' `on.schedule` is workflow-scoped, not job-scoped, so unlike
|
|
13
14
|
* the component generator (one combined pipeline for the whole graph) this
|
|
14
15
|
* emits one workflow file per `ScheduledOpSpec`. Each workflow:
|
|
15
|
-
* - triggers on `
|
|
16
|
-
* runs stay available for
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
16
|
+
* - triggers on its `ScheduledOpSpec`'s trigger (#2084): `cron` (the Op's
|
|
17
|
+
* schedule, plus `workflow_dispatch` so manual runs stay available for
|
|
18
|
+
* testing/dry-runs), `pull_request` (optionally filtered to `branches`,
|
|
19
|
+
* no `workflow_dispatch` — a PR event needs no manual escape hatch), or
|
|
20
|
+
* `push` (filtered to `branches`, defaulting to the repository's default
|
|
21
|
+
* branch);
|
|
22
|
+
* - declares only the `permissions:` its `findingMode` (and, for a
|
|
23
|
+
* `pull_request` trigger, whether that mode posts a comment) needs —
|
|
24
|
+
* `report` stays read-only, `issue`/`pull-request` add the write scope
|
|
25
|
+
* the Op's own activity uses (`gh issue create` / `gh pr create`, see
|
|
26
|
+
* `@intentius/chant/op`'s `reconcilePr` activity) — never a blanket
|
|
27
|
+
* `write-all`;
|
|
22
28
|
* - runs exactly one invocation, `chant run <name>` by default — never
|
|
23
29
|
* inlined audit/reconcile logic. The finding-mode itself is already baked
|
|
24
30
|
* into the Op's own activity args at build time by the composite that
|
|
@@ -34,7 +40,11 @@ export type { GenerateGithubOpOptions, GenerateGithubOpResult };
|
|
|
34
40
|
* `./generate-pipeline.ts`'s `GithubPipelineDoc` split.
|
|
35
41
|
*/
|
|
36
42
|
export interface GithubOpPipelineDoc {
|
|
37
|
-
/**
|
|
43
|
+
/**
|
|
44
|
+
* The `on:` trigger mapping, per {@link ScheduledOpSpec}'s trigger kind
|
|
45
|
+
* (#2084): `{ schedule, workflow_dispatch }` for cron, `{ pull_request }`
|
|
46
|
+
* for `pull_request`, `{ push }` for `push`.
|
|
47
|
+
*/
|
|
38
48
|
on: Record<string, unknown>;
|
|
39
49
|
/** The `env:` mapping, when `options.variables` is set. */
|
|
40
50
|
env?: Record<string, unknown>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate-op-pipeline.d.ts","sourceRoot":"","sources":["../../src/components/generate-op-pipeline.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"generate-op-pipeline.d.ts","sourceRoot":"","sources":["../../src/components/generate-op-pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAIH,OAAO,KAAK,EACV,wBAAwB,IAAI,uBAAuB,EAEnD,aAAa,EACb,gBAAgB,IAAI,sBAAsB,EAE1C,eAAe,EAChB,MAAM,0BAA0B,CAAC;AAElC,YAAY,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,CAAC;AAEhE;;;;;;GAMG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;;OAIG;IACH,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC5B,2DAA2D;IAC3D,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,6DAA6D;IAC7D,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC;;;;OAIG;IACH,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,8DAA8D;IAC9D,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,qFAAqF;AACrF,MAAM,WAAW,oBAAoB;IACnC,4FAA4F;IAC5F,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,mBAAmB,CAAC;CAC1B;AA+DD;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,GAAG,EAAE,eAAe,EAAE,EACtB,OAAO,GAAE,uBAA4B,GACpC;IAAE,KAAK,EAAE,oBAAoB,EAAE,CAAC;IAAC,IAAI,EAAE,aAAa,EAAE,CAAA;CAAE,CA+C1D;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,mBAAmB,GAAG,MAAM,CAQnE;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,eAAe,EAAE,EACtB,OAAO,GAAE,uBAA4B,GACpC,sBAAsB,CAMxB"}
|
package/dist/integrity.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"algorithm": "sha256",
|
|
3
3
|
"artifacts": {
|
|
4
|
-
"manifest.json": "
|
|
4
|
+
"manifest.json": "dd75f9469a0b8058a7620ebef4b937fd9a6a60e0912e3afd1a0e2a3631685773",
|
|
5
5
|
"meta.json": "dc3977afc2b6ddc4904de906d1b1e448b786b2aced9d82f8eade2ff915203344",
|
|
6
6
|
"types/index.d.ts": "f207946d7ab52f712d0c09995366440a6bba8b39358057b0da38ff9c0acca429",
|
|
7
7
|
"rules/deprecated-action-version.ts": "d41e6e532ab7f623af1bee4ac5279fcb2baada7defa1c5d022a5bc71983e8797",
|
|
@@ -77,5 +77,5 @@
|
|
|
77
77
|
"skills/chant-github-patterns.md": "bb3abef289a8fdfcf07d6bb2d7289dcb2f38bc0cb0321ea320b78b45a6f548c0",
|
|
78
78
|
"skills/chant-github-security.md": "aab111cb0871cad30281ce48d7da23663689619351029219e2be019a1a61e394"
|
|
79
79
|
},
|
|
80
|
-
"composite": "
|
|
80
|
+
"composite": "f6bd3cec37abf68646e70479861823f42af9002b807edda16bf4c873c0e3273a"
|
|
81
81
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit-catalog.d.ts","sourceRoot":"","sources":["../../src/lint/audit-catalog.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAA4H,KAAK,QAAQ,
|
|
1
|
+
{"version":3,"file":"audit-catalog.d.ts","sourceRoot":"","sources":["../../src/lint/audit-catalog.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAA4H,KAAK,QAAQ,EAAgB,MAAM,gCAAgC,CAAC;AAGvM,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CA6DvD,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prior art for these audit rules: the open-source tools whose checks cover the
|
|
3
|
+
* same condition, credited per rule. See packages/core/src/audit/prior-art.ts for
|
|
4
|
+
* the registry, the relation vocabulary, and why this is credit rather than
|
|
5
|
+
* authority. Kept by hand; the prior-art sweep (scripts/prior-art-sweep.ts) reports
|
|
6
|
+
* when a credited tool's index no longer lists a rule cited here.
|
|
7
|
+
*/
|
|
8
|
+
import type { Lineage } from "@intentius/chant/audit/catalog";
|
|
9
|
+
export declare const githubAuditLineage: Record<string, Lineage[]>;
|
|
10
|
+
//# sourceMappingURL=audit-lineage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-lineage.d.ts","sourceRoot":"","sources":["../../src/lint/audit-lineage.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAC;AAE9D,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,CAuKxD,CAAC"}
|
package/dist/manifest.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intentius/chant-lexicon-github",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.58.0",
|
|
4
4
|
"description": "GitHub Actions lexicon for chant — declarative IaC in TypeScript",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://intentius.io/chant",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"typescript": "^5.9.3"
|
|
62
62
|
},
|
|
63
63
|
"peerDependencies": {
|
|
64
|
-
"@intentius/chant": "^0.
|
|
64
|
+
"@intentius/chant": "^0.58.0",
|
|
65
65
|
"typescript": "^5.9.3"
|
|
66
66
|
}
|
|
67
67
|
}
|
|
@@ -108,6 +108,84 @@ describe("generateGithubOpPipeline: least-privilege permissions per finding-mode
|
|
|
108
108
|
});
|
|
109
109
|
});
|
|
110
110
|
|
|
111
|
+
describe("generateGithubOpPipeline: trigger kinds (#2084)", () => {
|
|
112
|
+
test("a legacy `{ schedule }` spec with no `trigger` is unchanged", () => {
|
|
113
|
+
const specs: ScheduledOpSpec[] = [{ name: "actions-audit", schedule: "0 6 * * *", findingMode: "issue" }];
|
|
114
|
+
const result = generateGithubOpPipeline(specs);
|
|
115
|
+
const doc = parseFile(result.files[0].yaml);
|
|
116
|
+
|
|
117
|
+
expect(doc.on).toEqual({ schedule: [{ cron: "0 6 * * *" }], workflow_dispatch: {} });
|
|
118
|
+
expect(doc.permissions).toEqual({ contents: "read", issues: "write" });
|
|
119
|
+
expect(result.jobs[0].trigger).toEqual({ kind: "cron", schedule: "0 6 * * *" });
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test("pull_request trigger: filters to branches, has no workflow_dispatch", () => {
|
|
123
|
+
const specs: ScheduledOpSpec[] = [
|
|
124
|
+
{ name: "tf-plan", trigger: { kind: "pull_request", branches: ["main"] } },
|
|
125
|
+
];
|
|
126
|
+
const result = generateGithubOpPipeline(specs);
|
|
127
|
+
const doc = parseFile(result.files[0].yaml);
|
|
128
|
+
|
|
129
|
+
expect(doc.on).toEqual({ pull_request: { branches: ["main"] } });
|
|
130
|
+
expect(doc.on).not.toHaveProperty("workflow_dispatch");
|
|
131
|
+
expect(result.jobs[0].trigger).toEqual({ kind: "pull_request", branches: ["main"] });
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test("pull_request trigger with no branches filter triggers on every PR", () => {
|
|
135
|
+
const result = generateGithubOpPipeline([{ name: "tf-plan", trigger: { kind: "pull_request" } }]);
|
|
136
|
+
const doc = parseFile(result.files[0].yaml);
|
|
137
|
+
expect(doc.on).toEqual({ pull_request: {} });
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test("pull_request trigger with a comment-posting finding mode gets pull-requests: write", () => {
|
|
141
|
+
const specs: ScheduledOpSpec[] = [
|
|
142
|
+
{ name: "tf-plan", trigger: { kind: "pull_request" }, findingMode: "issue" },
|
|
143
|
+
];
|
|
144
|
+
const result = generateGithubOpPipeline(specs);
|
|
145
|
+
const doc = parseFile(result.files[0].yaml);
|
|
146
|
+
expect(doc.permissions).toEqual({ contents: "read", issues: "write", "pull-requests": "write" });
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
test("pull_request trigger stays read-only when findingMode is report", () => {
|
|
150
|
+
const result = generateGithubOpPipeline([{ name: "tf-plan", trigger: { kind: "pull_request" } }]);
|
|
151
|
+
const doc = parseFile(result.files[0].yaml);
|
|
152
|
+
expect(doc.permissions).toEqual({ contents: "read" });
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test("push trigger: filters to branches", () => {
|
|
156
|
+
const specs: ScheduledOpSpec[] = [
|
|
157
|
+
{ name: "tf-apply", trigger: { kind: "push", branches: ["release"] } },
|
|
158
|
+
];
|
|
159
|
+
const result = generateGithubOpPipeline(specs);
|
|
160
|
+
const doc = parseFile(result.files[0].yaml);
|
|
161
|
+
|
|
162
|
+
expect(doc.on).toEqual({ push: { branches: ["release"] } });
|
|
163
|
+
expect(doc.on).not.toHaveProperty("workflow_dispatch");
|
|
164
|
+
expect(result.jobs[0].trigger).toEqual({ kind: "push", branches: ["release"] });
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
test("push trigger defaults to the repository default branch (main) when branches is omitted", () => {
|
|
168
|
+
const result = generateGithubOpPipeline([{ name: "tf-apply", trigger: { kind: "push" } }]);
|
|
169
|
+
const doc = parseFile(result.files[0].yaml);
|
|
170
|
+
expect(doc.on).toEqual({ push: { branches: ["main"] } });
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test("push trigger is read-only by default", () => {
|
|
174
|
+
const result = generateGithubOpPipeline([{ name: "tf-apply", trigger: { kind: "push" } }]);
|
|
175
|
+
const doc = parseFile(result.files[0].yaml);
|
|
176
|
+
expect(doc.permissions).toEqual({ contents: "read" });
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
test("push trigger honors an elevated findingMode, same as cron", () => {
|
|
180
|
+
const specs: ScheduledOpSpec[] = [
|
|
181
|
+
{ name: "tf-apply", trigger: { kind: "push" }, findingMode: "pull-request" },
|
|
182
|
+
];
|
|
183
|
+
const result = generateGithubOpPipeline(specs);
|
|
184
|
+
const doc = parseFile(result.files[0].yaml);
|
|
185
|
+
expect(doc.permissions).toEqual({ contents: "write", "pull-requests": "write" });
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
|
|
111
189
|
describe("generateGithubOpPipeline: concurrency guards against overlapping runs", () => {
|
|
112
190
|
test("each file's concurrency group is scoped to its own job", () => {
|
|
113
191
|
const result = generateGithubOpPipeline([{ name: "actions-audit", schedule: "0 6 * * *" }]);
|
|
@@ -123,7 +201,7 @@ describe("generateGithubOpPipeline: a cross-cutting change is one generator edit
|
|
|
123
201
|
{ name: "prod-reconcile", schedule: "0 * * * *" },
|
|
124
202
|
];
|
|
125
203
|
const result = generateGithubOpPipeline(specs, {
|
|
126
|
-
runCommand: ["chant", "run", "{name}", "--
|
|
204
|
+
runCommand: ["chant", "run", "{name}", "--json"],
|
|
127
205
|
beforeScript: ["npm ci"],
|
|
128
206
|
extraScript: ["echo done"],
|
|
129
207
|
});
|
|
@@ -133,7 +211,7 @@ describe("generateGithubOpPipeline: a cross-cutting change is one generator edit
|
|
|
133
211
|
const jobName = Object.keys(doc.jobs!)[0];
|
|
134
212
|
const runLines = doc.jobs![jobName].steps.filter((s) => typeof s.run === "string").map((s) => s.run as string);
|
|
135
213
|
expect(runLines[0]).toBe("npm ci");
|
|
136
|
-
expect(runLines[1]).toContain("--
|
|
214
|
+
expect(runLines[1]).toContain("--json");
|
|
137
215
|
expect(runLines[2]).toBe("echo done");
|
|
138
216
|
}
|
|
139
217
|
});
|
|
@@ -144,3 +222,38 @@ describe("generateGithubOpPipeline: a cross-cutting change is one generator edit
|
|
|
144
222
|
expect(result.jobs).toEqual([]);
|
|
145
223
|
});
|
|
146
224
|
});
|
|
225
|
+
|
|
226
|
+
describe("generateGithubOpPipeline: the Op's own schedule (#2120)", () => {
|
|
227
|
+
test("an Op that declares its cadence needs no cron on the spec — opSchedule supplies it", () => {
|
|
228
|
+
const specs: ScheduledOpSpec[] = [{ name: "prod-watch", opSchedule: { cron: "*/10 * * * *", overlap: "skip" } }];
|
|
229
|
+
const result = generateGithubOpPipeline(specs);
|
|
230
|
+
const doc = parseFile(result.files[0].yaml);
|
|
231
|
+
|
|
232
|
+
expect(doc.on).toEqual({ schedule: [{ cron: "*/10 * * * *" }], workflow_dispatch: {} });
|
|
233
|
+
expect(result.jobs[0].trigger).toEqual({ kind: "cron", schedule: "*/10 * * * *" });
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
test("an explicit spec `schedule` still wins over the Op's own", () => {
|
|
237
|
+
const specs: ScheduledOpSpec[] = [
|
|
238
|
+
{ name: "prod-watch", schedule: "0 6 * * *", opSchedule: { cron: "*/10 * * * *" } },
|
|
239
|
+
];
|
|
240
|
+
const doc = parseFile(generateGithubOpPipeline(specs).files[0].yaml);
|
|
241
|
+
expect(doc.on).toEqual({ schedule: [{ cron: "0 6 * * *" }], workflow_dispatch: {} });
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
test("an explicit `trigger` overrides the Op's own cadence entirely", () => {
|
|
245
|
+
const specs: ScheduledOpSpec[] = [
|
|
246
|
+
{ name: "tf-plan", trigger: { kind: "pull_request" }, opSchedule: { cron: "*/10 * * * *" } },
|
|
247
|
+
];
|
|
248
|
+
const result = generateGithubOpPipeline(specs);
|
|
249
|
+
const doc = parseFile(result.files[0].yaml);
|
|
250
|
+
expect(doc.on).toEqual({ pull_request: {} });
|
|
251
|
+
expect(result.jobs[0].trigger).toEqual({ kind: "pull_request" });
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
test("an Op with no cadence anywhere is still an error naming the Op", () => {
|
|
255
|
+
expect(() => generateGithubOpPipeline([{ name: "no-cadence" }])).toThrow(
|
|
256
|
+
/Scheduled Op "no-cadence" has neither/,
|
|
257
|
+
);
|
|
258
|
+
});
|
|
259
|
+
});
|
|
@@ -4,21 +4,27 @@
|
|
|
4
4
|
* The Op counterpart to `./generate-pipeline.ts` (#891): that module
|
|
5
5
|
* synthesizes a `workflow_dispatch`-triggered pipeline from a deploy-time
|
|
6
6
|
* component graph, this one synthesizes a cron-triggered workflow per
|
|
7
|
-
* stateless Op
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* stateless Op. An Op's cadence is an `OpSchedule` on the Op itself
|
|
8
|
+
* (`packages/core/src/op/types.ts`), runtime-neutral data each reader
|
|
9
|
+
* interprets; this module is the reader that turns it into a cron a GitHub
|
|
10
|
+
* runner fires (`WorkflowAuditOp`, `PipelineAuditOp`, `ReconcileOp`, … all
|
|
11
|
+
* accept an optional `schedule` precisely for this).
|
|
11
12
|
*
|
|
12
13
|
* GitHub Actions' `on.schedule` is workflow-scoped, not job-scoped, so unlike
|
|
13
14
|
* the component generator (one combined pipeline for the whole graph) this
|
|
14
15
|
* emits one workflow file per `ScheduledOpSpec`. Each workflow:
|
|
15
|
-
* - triggers on `
|
|
16
|
-
* runs stay available for
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
16
|
+
* - triggers on its `ScheduledOpSpec`'s trigger (#2084): `cron` (the Op's
|
|
17
|
+
* schedule, plus `workflow_dispatch` so manual runs stay available for
|
|
18
|
+
* testing/dry-runs), `pull_request` (optionally filtered to `branches`,
|
|
19
|
+
* no `workflow_dispatch` — a PR event needs no manual escape hatch), or
|
|
20
|
+
* `push` (filtered to `branches`, defaulting to the repository's default
|
|
21
|
+
* branch);
|
|
22
|
+
* - declares only the `permissions:` its `findingMode` (and, for a
|
|
23
|
+
* `pull_request` trigger, whether that mode posts a comment) needs —
|
|
24
|
+
* `report` stays read-only, `issue`/`pull-request` add the write scope
|
|
25
|
+
* the Op's own activity uses (`gh issue create` / `gh pr create`, see
|
|
26
|
+
* `@intentius/chant/op`'s `reconcilePr` activity) — never a blanket
|
|
27
|
+
* `write-all`;
|
|
22
28
|
* - runs exactly one invocation, `chant run <name>` by default — never
|
|
23
29
|
* inlined audit/reconcile logic. The finding-mode itself is already baked
|
|
24
30
|
* into the Op's own activity args at build time by the composite that
|
|
@@ -26,11 +32,13 @@
|
|
|
26
32
|
*/
|
|
27
33
|
|
|
28
34
|
import { emitYAML } from "@intentius/chant/yaml";
|
|
35
|
+
import { resolveOpTrigger } from "@intentius/chant/lexicon";
|
|
29
36
|
import type {
|
|
30
37
|
ComponentPipelineOptions as GenerateGithubOpOptions,
|
|
31
38
|
OpFindingMode,
|
|
32
39
|
OpPipelineJob,
|
|
33
40
|
OpPipelineResult as GenerateGithubOpResult,
|
|
41
|
+
OpTrigger,
|
|
34
42
|
ScheduledOpSpec,
|
|
35
43
|
} from "@intentius/chant/lexicon";
|
|
36
44
|
|
|
@@ -44,7 +52,11 @@ export type { GenerateGithubOpOptions, GenerateGithubOpResult };
|
|
|
44
52
|
* `./generate-pipeline.ts`'s `GithubPipelineDoc` split.
|
|
45
53
|
*/
|
|
46
54
|
export interface GithubOpPipelineDoc {
|
|
47
|
-
/**
|
|
55
|
+
/**
|
|
56
|
+
* The `on:` trigger mapping, per {@link ScheduledOpSpec}'s trigger kind
|
|
57
|
+
* (#2084): `{ schedule, workflow_dispatch }` for cron, `{ pull_request }`
|
|
58
|
+
* for `pull_request`, `{ push }` for `push`.
|
|
59
|
+
*/
|
|
48
60
|
on: Record<string, unknown>;
|
|
49
61
|
/** The `env:` mapping, when `options.variables` is set. */
|
|
50
62
|
env?: Record<string, unknown>;
|
|
@@ -75,13 +87,40 @@ function toJobName(opName: string): string {
|
|
|
75
87
|
const DEFAULT_IMAGE = "node:22-slim";
|
|
76
88
|
|
|
77
89
|
/**
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
90
|
+
* Default branch assumed for a `push` trigger with no `branches` override
|
|
91
|
+
* (#2084). There is no generator constant for a downstream project's actual
|
|
92
|
+
* default branch — this operates on a `ScheduledOpSpec`, not a git checkout
|
|
93
|
+
* — so "main" is the documented default; set `trigger.branches` explicitly
|
|
94
|
+
* for a project whose default branch is something else (e.g. "master").
|
|
95
|
+
*/
|
|
96
|
+
const DEFAULT_PUSH_BRANCH = "main";
|
|
97
|
+
|
|
98
|
+
/** This trigger's `on:` mapping (#2084): cron unchanged, `pull_request`/`push` new. */
|
|
99
|
+
function onFor(trigger: OpTrigger): Record<string, unknown> {
|
|
100
|
+
switch (trigger.kind) {
|
|
101
|
+
case "cron":
|
|
102
|
+
return { schedule: [{ cron: trigger.schedule }], workflow_dispatch: {} };
|
|
103
|
+
case "pull_request":
|
|
104
|
+
// No `workflow_dispatch`: a PR trigger needs no manual-dispatch escape
|
|
105
|
+
// hatch (revisit in review if that's wrong).
|
|
106
|
+
return { pull_request: trigger.branches ? { branches: trigger.branches } : {} };
|
|
107
|
+
case "push":
|
|
108
|
+
return { push: { branches: trigger.branches ?? [DEFAULT_PUSH_BRANCH] } };
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Least-privilege `permissions:` for a scheduled Op's finding-mode and
|
|
114
|
+
* trigger. `report` needs no write access; `issue` needs only `issues:
|
|
115
|
+
* write`; `pull-request` (and `merge-request`, generated the same way when a
|
|
116
|
+
* GitLab-authored spec is targeted at github) needs `contents: write` to
|
|
117
|
+
* push the reconcile branch plus `pull-requests: write` to open the PR. A
|
|
118
|
+
* `pull_request` trigger reports its finding as a comment on the triggering
|
|
119
|
+
* PR itself (#2084): any mode but `report` posts something to act on a
|
|
120
|
+
* finding, so on that trigger every such mode also gets `pull-requests:
|
|
121
|
+
* write` for the comment, whether or not its own scope already included it.
|
|
83
122
|
*/
|
|
84
|
-
function
|
|
123
|
+
function permissionsForMode(mode: OpFindingMode): Record<string, "read" | "write"> {
|
|
85
124
|
switch (mode) {
|
|
86
125
|
case "issue":
|
|
87
126
|
return { contents: "read", issues: "write" };
|
|
@@ -93,6 +132,14 @@ function permissionsFor(mode: OpFindingMode): Record<string, "read" | "write"> {
|
|
|
93
132
|
}
|
|
94
133
|
}
|
|
95
134
|
|
|
135
|
+
function permissionsFor(mode: OpFindingMode, trigger: OpTrigger): Record<string, "read" | "write"> {
|
|
136
|
+
const base = permissionsForMode(mode);
|
|
137
|
+
if (trigger.kind === "pull_request" && mode !== "report") {
|
|
138
|
+
return { ...base, "pull-requests": "write" };
|
|
139
|
+
}
|
|
140
|
+
return base;
|
|
141
|
+
}
|
|
142
|
+
|
|
96
143
|
/**
|
|
97
144
|
* Build one `GithubOpPipelineDoc` per scheduled Op: cron trigger,
|
|
98
145
|
* least-privilege `permissions:` for its finding-mode, one job that runs
|
|
@@ -113,8 +160,9 @@ export function buildGithubOpPipelineDocs(
|
|
|
113
160
|
|
|
114
161
|
for (const spec of ops) {
|
|
115
162
|
const findingMode = spec.findingMode ?? "report";
|
|
163
|
+
const trigger = resolveOpTrigger(spec);
|
|
116
164
|
const jobName = toJobName(spec.name);
|
|
117
|
-
jobs.push({ jobName, op: spec.name,
|
|
165
|
+
jobs.push({ jobName, op: spec.name, trigger, findingMode });
|
|
118
166
|
|
|
119
167
|
const runParts = runCommand.map((part) => part.replace("{name}", spec.name));
|
|
120
168
|
|
|
@@ -129,12 +177,12 @@ export function buildGithubOpPipelineDocs(
|
|
|
129
177
|
for (const line of extraScript) steps.push({ run: line });
|
|
130
178
|
|
|
131
179
|
const doc: GithubOpPipelineDoc = {
|
|
132
|
-
on:
|
|
180
|
+
on: onFor(trigger),
|
|
133
181
|
...(options.variables && Object.keys(options.variables).length > 0 ? { env: options.variables } : {}),
|
|
134
182
|
// One run at a time per Op — a slow audit must not overlap its own next
|
|
135
183
|
// scheduled trigger.
|
|
136
184
|
concurrency: { group: jobName, "cancel-in-progress": false },
|
|
137
|
-
permissions: permissionsFor(findingMode),
|
|
185
|
+
permissions: permissionsFor(findingMode, trigger),
|
|
138
186
|
jobsDoc: {
|
|
139
187
|
[jobName]: {
|
|
140
188
|
"runs-on": "ubuntu-latest",
|
|
@@ -222,7 +222,7 @@ describe("generateGithubPipeline: a cross-cutting change is one generator edit,
|
|
|
222
222
|
test("changing the trigger command (runCommand) updates every job's script uniformly", () => {
|
|
223
223
|
const components = pilotComponents();
|
|
224
224
|
const result = generateGithubPipeline(components, {
|
|
225
|
-
runCommand: ["chant", "run", "--components", "{name}", "--env", "staging", "--
|
|
225
|
+
runCommand: ["chant", "run", "--components", "{name}", "--env", "staging", "--verbose"],
|
|
226
226
|
});
|
|
227
227
|
const jobs = parsedJobs(result.yaml);
|
|
228
228
|
|
|
@@ -230,7 +230,7 @@ describe("generateGithubPipeline: a cross-cutting change is one generator edit,
|
|
|
230
230
|
const lines = runLines(jobs[job.jobName]);
|
|
231
231
|
// The runCommand prefix reflects in every job; output-threading flags
|
|
232
232
|
// (--seed-outputs/--dump-outputs) may be appended per the dependency graph.
|
|
233
|
-
expect(lines[0].startsWith(`chant run --components ${job.component} --env staging --
|
|
233
|
+
expect(lines[0].startsWith(`chant run --components ${job.component} --env staging --verbose`)).toBe(true);
|
|
234
234
|
}
|
|
235
235
|
});
|
|
236
236
|
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* The github lexicon's chant audit catalog — metadata for its post-synth rules
|
|
3
3
|
* (GHA GitHub Actions rules). Contributed via githubPlugin.auditCatalog() (#687).
|
|
4
4
|
*/
|
|
5
|
-
import { auditRule, GH_INJECTION, GH_PWN, GH_SECRETS, GH_THIRD_PARTY, GH_TOKEN, SCORECARD_PINNED, SCORECARD_TOKEN, SCORECARD_VULN, type RuleMeta } from "@intentius/chant/audit/catalog";
|
|
5
|
+
import { auditRule, GH_INJECTION, GH_PWN, GH_SECRETS, GH_THIRD_PARTY, GH_TOKEN, SCORECARD_PINNED, SCORECARD_TOKEN, SCORECARD_VULN, type RuleMeta, applyLineage } from "@intentius/chant/audit/catalog";
|
|
6
|
+
import { githubAuditLineage } from "./audit-lineage";
|
|
6
7
|
|
|
7
8
|
export const githubAuditCatalog: Record<string, RuleMeta> = {
|
|
8
9
|
GHA006: auditRule("GHA006", "report-only", "guidance", "Duplicate workflow name", "Give each workflow a unique `name:`.", { category: "correctness" }),
|
|
@@ -66,3 +67,6 @@ export const githubAuditCatalog: Record<string, RuleMeta> = {
|
|
|
66
67
|
GHA067: auditRule("GHA067", "report-only", "guidance", "Unconditional heavy step with no path filter", "Scope the trigger's paths, or add an if: guard to the step.", { category: "efficiency" }),
|
|
67
68
|
GHA068: auditRule("GHA068", "report-only", "guidance", "Pull-request workflow missing a concurrency group", "Add a concurrency group with cancel-in-progress: true.", { category: "efficiency" }),
|
|
68
69
|
};
|
|
70
|
+
|
|
71
|
+
// Prior art credits live beside the rules in ./audit-lineage.ts (see core audit/prior-art.ts).
|
|
72
|
+
applyLineage(githubAuditCatalog, githubAuditLineage);
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prior art for these audit rules: the open-source tools whose checks cover the
|
|
3
|
+
* same condition, credited per rule. See packages/core/src/audit/prior-art.ts for
|
|
4
|
+
* the registry, the relation vocabulary, and why this is credit rather than
|
|
5
|
+
* authority. Kept by hand; the prior-art sweep (scripts/prior-art-sweep.ts) reports
|
|
6
|
+
* when a credited tool's index no longer lists a rule cited here.
|
|
7
|
+
*/
|
|
8
|
+
import type { Lineage } from "@intentius/chant/audit/catalog";
|
|
9
|
+
|
|
10
|
+
export const githubAuditLineage: Record<string, Lineage[]> = {
|
|
11
|
+
GHA009: [
|
|
12
|
+
{ tool: "actionlint", rule: "Unexpected empty mappings", url: "https://github.com/rhysd/actionlint/blob/main/docs/checks.md#unexpected-empty-mappings", relation: "overlaps" },
|
|
13
|
+
],
|
|
14
|
+
GHA011: [
|
|
15
|
+
{ tool: "actionlint", rule: "Job dependencies validation", url: "https://github.com/rhysd/actionlint/blob/main/docs/checks.md#job-dependencies-validation", relation: "equivalent" },
|
|
16
|
+
],
|
|
17
|
+
GHA013: [
|
|
18
|
+
{ tool: "poutine", rule: "default_permissions_on_risky_events", url: "https://boostsecurityio.github.io/poutine/rules/default_permissions_on_risky_events/", relation: "overlaps" },
|
|
19
|
+
{ tool: "zizmor", rule: "excessive-permissions", url: "https://docs.zizmor.sh/audits/#excessive-permissions", relation: "overlaps" },
|
|
20
|
+
{ tool: "scorecard", rule: "Token-Permissions", url: "https://github.com/ossf/scorecard/blob/main/docs/checks.md#token-permissions", relation: "overlaps" },
|
|
21
|
+
],
|
|
22
|
+
GHA017: [
|
|
23
|
+
{ tool: "zizmor", rule: "excessive-permissions", url: "https://docs.zizmor.sh/audits/#excessive-permissions", relation: "overlaps" },
|
|
24
|
+
{ tool: "scorecard", rule: "Token-Permissions", url: "https://github.com/ossf/scorecard/blob/main/docs/checks.md#token-permissions", relation: "overlaps" },
|
|
25
|
+
],
|
|
26
|
+
GHA018: [
|
|
27
|
+
{ tool: "scorecard", rule: "Dangerous-Workflow", url: "https://github.com/ossf/scorecard/blob/main/docs/checks.md#dangerous-workflow", relation: "overlaps" },
|
|
28
|
+
{ tool: "octoscan", rule: "dangerous-checkout", url: "https://github.com/synacktiv/octoscan#dangerous-checkout", relation: "overlaps" },
|
|
29
|
+
{ tool: "poutine", rule: "untrusted_checkout_exec", url: "https://boostsecurityio.github.io/poutine/rules/untrusted_checkout_exec/", relation: "overlaps" },
|
|
30
|
+
],
|
|
31
|
+
GHA019: [
|
|
32
|
+
{ tool: "actionlint", rule: "Job dependencies validation", url: "https://github.com/rhysd/actionlint/blob/main/docs/checks.md#job-dependencies-validation", relation: "equivalent" },
|
|
33
|
+
],
|
|
34
|
+
GHA021: [
|
|
35
|
+
{ tool: "zizmor", rule: "unpinned-uses", url: "https://docs.zizmor.sh/audits/#unpinned-uses", relation: "overlaps" },
|
|
36
|
+
{ tool: "scorecard", rule: "Pinned-Dependencies", url: "https://github.com/ossf/scorecard/blob/main/docs/checks.md#pinned-dependencies", relation: "overlaps" },
|
|
37
|
+
],
|
|
38
|
+
GHA023: [
|
|
39
|
+
{ tool: "actionlint", rule: "Check deprecated workflow commands", url: "https://github.com/rhysd/actionlint/blob/main/docs/checks.md#check-deprecated-workflow-commands", relation: "overlaps" },
|
|
40
|
+
],
|
|
41
|
+
GHA024: [
|
|
42
|
+
{ tool: "zizmor", rule: "concurrency-limits", url: "https://docs.zizmor.sh/audits/#concurrency-limits", relation: "overlaps" },
|
|
43
|
+
],
|
|
44
|
+
GHA025: [
|
|
45
|
+
{ tool: "zizmor", rule: "dangerous-triggers", url: "https://docs.zizmor.sh/audits/#dangerous-triggers", relation: "overlaps" },
|
|
46
|
+
],
|
|
47
|
+
GHA026: [
|
|
48
|
+
{ tool: "zizmor", rule: "secrets-outside-env", url: "https://docs.zizmor.sh/audits/#secrets-outside-env", relation: "equivalent" },
|
|
49
|
+
],
|
|
50
|
+
GHA028: [
|
|
51
|
+
{ tool: "actionlint", rule: "Missing required keys and key duplicates", url: "https://github.com/rhysd/actionlint/blob/main/docs/checks.md#missing-required-keys-and-key-duplicates", relation: "overlaps" },
|
|
52
|
+
],
|
|
53
|
+
GHA029: [
|
|
54
|
+
{ tool: "zizmor", rule: "unpinned-uses", url: "https://docs.zizmor.sh/audits/#unpinned-uses", relation: "overlaps" },
|
|
55
|
+
{ tool: "scorecard", rule: "Pinned-Dependencies", url: "https://github.com/ossf/scorecard/blob/main/docs/checks.md#pinned-dependencies", relation: "overlaps" },
|
|
56
|
+
],
|
|
57
|
+
GHA030: [
|
|
58
|
+
{ tool: "zizmor", rule: "unpinned-images", url: "https://docs.zizmor.sh/audits/#unpinned-images", relation: "overlaps" },
|
|
59
|
+
],
|
|
60
|
+
GHA031: [
|
|
61
|
+
{ tool: "zizmor", rule: "typosquat-uses", url: "https://docs.zizmor.sh/audits/#typosquat-uses", relation: "equivalent" },
|
|
62
|
+
],
|
|
63
|
+
GHA032: [
|
|
64
|
+
{ tool: "zizmor", rule: "archived-uses", url: "https://docs.zizmor.sh/audits/#archived-uses", relation: "overlaps" },
|
|
65
|
+
{ tool: "zizmor", rule: "known-vulnerable-actions", url: "https://docs.zizmor.sh/audits/#known-vulnerable-actions", relation: "overlaps" },
|
|
66
|
+
],
|
|
67
|
+
GHA033: [
|
|
68
|
+
{ tool: "zizmor", rule: "excessive-permissions", url: "https://docs.zizmor.sh/audits/#excessive-permissions", relation: "overlaps" },
|
|
69
|
+
{ tool: "scorecard", rule: "Token-Permissions", url: "https://github.com/ossf/scorecard/blob/main/docs/checks.md#token-permissions", relation: "overlaps" },
|
|
70
|
+
],
|
|
71
|
+
GHA034: [
|
|
72
|
+
{ tool: "zizmor", rule: "excessive-permissions", url: "https://docs.zizmor.sh/audits/#excessive-permissions", relation: "overlaps" },
|
|
73
|
+
{ tool: "scorecard", rule: "Token-Permissions", url: "https://github.com/ossf/scorecard/blob/main/docs/checks.md#token-permissions", relation: "overlaps" },
|
|
74
|
+
],
|
|
75
|
+
GHA036: [
|
|
76
|
+
{ tool: "zizmor", rule: "template-injection", url: "https://docs.zizmor.sh/audits/#template-injection", relation: "equivalent" },
|
|
77
|
+
{ tool: "poutine", rule: "injection", url: "https://boostsecurityio.github.io/poutine/rules/injection/", relation: "equivalent" },
|
|
78
|
+
{ tool: "actionlint", rule: "Script injection by potentially untrusted inputs", url: "https://github.com/rhysd/actionlint/blob/main/docs/checks.md#script-injection-by-potentially-untrusted-inputs", relation: "equivalent" },
|
|
79
|
+
{ tool: "octoscan", rule: "expression-injection", url: "https://github.com/synacktiv/octoscan#expression-injection", relation: "equivalent" },
|
|
80
|
+
{ tool: "scorecard", rule: "Dangerous-Workflow", url: "https://github.com/ossf/scorecard/blob/main/docs/checks.md#dangerous-workflow", relation: "overlaps" },
|
|
81
|
+
],
|
|
82
|
+
GHA037: [
|
|
83
|
+
{ tool: "zizmor", rule: "github-env", url: "https://docs.zizmor.sh/audits/#github-env", relation: "equivalent" },
|
|
84
|
+
{ tool: "octoscan", rule: "dangerous-write", url: "https://github.com/synacktiv/octoscan#dangerous-write", relation: "overlaps" },
|
|
85
|
+
],
|
|
86
|
+
GHA038: [
|
|
87
|
+
{ tool: "scorecard", rule: "Dangerous-Workflow", url: "https://github.com/ossf/scorecard/blob/main/docs/checks.md#dangerous-workflow", relation: "overlaps" },
|
|
88
|
+
{ tool: "octoscan", rule: "dangerous-checkout", url: "https://github.com/synacktiv/octoscan#dangerous-checkout", relation: "overlaps" },
|
|
89
|
+
],
|
|
90
|
+
GHA039: [
|
|
91
|
+
{ tool: "zizmor", rule: "bot-conditions", url: "https://docs.zizmor.sh/audits/#bot-conditions", relation: "overlaps" },
|
|
92
|
+
{ tool: "octoscan", rule: "bot-check", url: "https://github.com/synacktiv/octoscan#bot-check", relation: "overlaps" },
|
|
93
|
+
{ tool: "poutine", rule: "confused_deputy_auto_merge", url: "https://boostsecurityio.github.io/poutine/rules/confused_deputy_auto_merge/", relation: "overlaps" },
|
|
94
|
+
],
|
|
95
|
+
GHA040: [
|
|
96
|
+
{ tool: "poutine", rule: "pr_runs_on_self_hosted", url: "https://boostsecurityio.github.io/poutine/rules/pr_runs_on_self_hosted/", relation: "equivalent" },
|
|
97
|
+
{ tool: "zizmor", rule: "self-hosted-runner", url: "https://docs.zizmor.sh/audits/#self-hosted-runner", relation: "overlaps" },
|
|
98
|
+
{ tool: "octoscan", rule: "runner-label", url: "https://github.com/synacktiv/octoscan#runner-label", relation: "overlaps" },
|
|
99
|
+
],
|
|
100
|
+
GHA041: [
|
|
101
|
+
{ tool: "zizmor", rule: "secrets-inherit", url: "https://docs.zizmor.sh/audits/#secrets-inherit", relation: "equivalent" },
|
|
102
|
+
],
|
|
103
|
+
GHA042: [
|
|
104
|
+
{ tool: "zizmor", rule: "overprovisioned-secrets", url: "https://docs.zizmor.sh/audits/#overprovisioned-secrets", relation: "equivalent" },
|
|
105
|
+
{ tool: "poutine", rule: "job_all_secrets", url: "https://boostsecurityio.github.io/poutine/rules/job_all_secrets/", relation: "equivalent" },
|
|
106
|
+
],
|
|
107
|
+
GHA043: [
|
|
108
|
+
{ tool: "zizmor", rule: "secrets-outside-env", url: "https://docs.zizmor.sh/audits/#secrets-outside-env", relation: "equivalent" },
|
|
109
|
+
],
|
|
110
|
+
GHA044: [
|
|
111
|
+
{ tool: "zizmor", rule: "hardcoded-container-credentials", url: "https://docs.zizmor.sh/audits/#hardcoded-container-credentials", relation: "equivalent" },
|
|
112
|
+
{ tool: "actionlint", rule: "Hardcoded credentials", url: "https://github.com/rhysd/actionlint/blob/main/docs/checks.md#hardcoded-credentials", relation: "equivalent" },
|
|
113
|
+
{ tool: "octoscan", rule: "credentials", url: "https://github.com/synacktiv/octoscan#credentials", relation: "equivalent" },
|
|
114
|
+
],
|
|
115
|
+
GHA046: [
|
|
116
|
+
{ tool: "actionlint", rule: "Constant conditions at `if:`", url: "https://github.com/rhysd/actionlint/blob/main/docs/checks.md#constant-conditions-at-if", relation: "equivalent" },
|
|
117
|
+
{ tool: "zizmor", rule: "unsound-condition", url: "https://docs.zizmor.sh/audits/#unsound-condition", relation: "overlaps" },
|
|
118
|
+
{ tool: "poutine", rule: "if_always_true", url: "https://boostsecurityio.github.io/poutine/rules/if_always_true/", relation: "overlaps" },
|
|
119
|
+
],
|
|
120
|
+
GHA047: [
|
|
121
|
+
{ tool: "zizmor", rule: "unsound-contains", url: "https://docs.zizmor.sh/audits/#unsound-contains", relation: "equivalent" },
|
|
122
|
+
],
|
|
123
|
+
GHA048: [
|
|
124
|
+
{ tool: "zizmor", rule: "obfuscation", url: "https://docs.zizmor.sh/audits/#obfuscation", relation: "overlaps" },
|
|
125
|
+
],
|
|
126
|
+
GHA049: [
|
|
127
|
+
{ tool: "zizmor", rule: "artipacked", url: "https://docs.zizmor.sh/audits/#artipacked", relation: "equivalent" },
|
|
128
|
+
{ tool: "octoscan", rule: "dangerous-artefact", url: "https://github.com/synacktiv/octoscan#dangerous-artefact", relation: "overlaps" },
|
|
129
|
+
],
|
|
130
|
+
GHA050: [
|
|
131
|
+
{ tool: "zizmor", rule: "cache-poisoning", url: "https://docs.zizmor.sh/audits/#cache-poisoning", relation: "overlaps" },
|
|
132
|
+
],
|
|
133
|
+
GHA051: [
|
|
134
|
+
{ tool: "zizmor", rule: "use-trusted-publishing", url: "https://docs.zizmor.sh/audits/#use-trusted-publishing", relation: "overlaps" },
|
|
135
|
+
],
|
|
136
|
+
GHA052: [
|
|
137
|
+
{ tool: "poutine", rule: "unverified_script_exec", url: "https://boostsecurityio.github.io/poutine/rules/unverified_script_exec/", relation: "equivalent" },
|
|
138
|
+
{ tool: "scorecard", rule: "Pinned-Dependencies", url: "https://github.com/ossf/scorecard/blob/main/docs/checks.md#pinned-dependencies", relation: "overlaps" },
|
|
139
|
+
],
|
|
140
|
+
GHA053: [
|
|
141
|
+
{ tool: "zizmor", rule: "insecure-commands", url: "https://docs.zizmor.sh/audits/#insecure-commands", relation: "equivalent" },
|
|
142
|
+
{ tool: "octoscan", rule: "unsecure-commands", url: "https://github.com/synacktiv/octoscan#unsecure-commands", relation: "equivalent" },
|
|
143
|
+
],
|
|
144
|
+
GHA054: [
|
|
145
|
+
{ tool: "zizmor", rule: "misfeature", url: "https://docs.zizmor.sh/audits/#misfeature", relation: "overlaps" },
|
|
146
|
+
],
|
|
147
|
+
GHA055: [
|
|
148
|
+
{ tool: "zizmor", rule: "superfluous-actions", url: "https://docs.zizmor.sh/audits/#superfluous-actions", relation: "overlaps" },
|
|
149
|
+
],
|
|
150
|
+
GHA056: [
|
|
151
|
+
{ tool: "zizmor", rule: "anonymous-definition", url: "https://docs.zizmor.sh/audits/#anonymous-definition", relation: "overlaps" },
|
|
152
|
+
],
|
|
153
|
+
GHA057: [
|
|
154
|
+
{ tool: "zizmor", rule: "dependabot-execution", url: "https://docs.zizmor.sh/audits/#dependabot-execution", relation: "equivalent" },
|
|
155
|
+
],
|
|
156
|
+
GHA058: [
|
|
157
|
+
{ tool: "zizmor", rule: "dependabot-cooldown", url: "https://docs.zizmor.sh/audits/#dependabot-cooldown", relation: "equivalent" },
|
|
158
|
+
],
|
|
159
|
+
GHA059: [
|
|
160
|
+
{ tool: "zizmor", rule: "ref-version-mismatch", url: "https://docs.zizmor.sh/audits/#ref-version-mismatch", relation: "equivalent" },
|
|
161
|
+
],
|
|
162
|
+
GHA060: [
|
|
163
|
+
{ tool: "zizmor", rule: "github-app", url: "https://docs.zizmor.sh/audits/#github-app", relation: "overlaps" },
|
|
164
|
+
],
|
|
165
|
+
GHA061: [
|
|
166
|
+
{ tool: "zizmor", rule: "forbidden-uses", url: "https://docs.zizmor.sh/audits/#forbidden-uses", relation: "equivalent" },
|
|
167
|
+
],
|
|
168
|
+
GHA062: [
|
|
169
|
+
{ tool: "zizmor", rule: "known-vulnerable-actions", url: "https://docs.zizmor.sh/audits/#known-vulnerable-actions", relation: "equivalent" },
|
|
170
|
+
{ tool: "poutine", rule: "known_vulnerability_in_build_component", url: "https://boostsecurityio.github.io/poutine/rules/known_vulnerability_in_build_component/", relation: "equivalent" },
|
|
171
|
+
{ tool: "octoscan", rule: "known-vulnerability", url: "https://github.com/synacktiv/octoscan#known-vulnerability", relation: "equivalent" },
|
|
172
|
+
{ tool: "scorecard", rule: "Vulnerabilities", url: "https://github.com/ossf/scorecard/blob/main/docs/checks.md#vulnerabilities", relation: "overlaps" },
|
|
173
|
+
],
|
|
174
|
+
GHA068: [
|
|
175
|
+
{ tool: "zizmor", rule: "concurrency-limits", url: "https://docs.zizmor.sh/audits/#concurrency-limits", relation: "overlaps" },
|
|
176
|
+
],
|
|
177
|
+
};
|