@intentius/chant-lexicon-github 0.46.0 → 0.49.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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "algorithm": "sha256",
3
3
  "artifacts": {
4
- "manifest.json": "d12cde366cee278153cdca686887bae8ea84d524e15a6578f5fd8c97c27eed29",
4
+ "manifest.json": "f13c6d5badc99a05feb6c2f1172e984576192151b1a62a157d770efa5b1c541c",
5
5
  "meta.json": "798c5f5f37b0174756d2451299acfdc95c014f11d06d32b9153bbdfa9f3a580c",
6
6
  "types/index.d.ts": "39c0791f0e58025bcef82e0f7a92240508f899fc2771d5dd51b2369b4d330f13",
7
7
  "rules/deprecated-action-version.ts": "d41e6e532ab7f623af1bee4ac5279fcb2baada7defa1c5d022a5bc71983e8797",
@@ -20,7 +20,7 @@
20
20
  "rules/gha006.ts": "7e2977d34851ce2ed9f91c4dd78ecda8d4de099ceaac1cd9ab74960a193133a3",
21
21
  "rules/gha009.ts": "0f9a261e886309679494da32b72a0d9ea20017e5da75a9d59a04846a0e4d78db",
22
22
  "rules/gha011.ts": "53b7ca79b5bf56e5793a9b7a44ae0ac766eb5acf0669005e7810cb7762bab79c",
23
- "rules/gha013.ts": "47c1b28565aced3370b85d00a03b6110a32e451661e34badd731ffbb24b45d9a",
23
+ "rules/gha013.ts": "cd9119154d7b9de94ae3f4d41ad5f09c5757e25a5effddd3d580ef5d1466a8fc",
24
24
  "rules/gha017.ts": "ec62dac7f9d69f36978cb461c4ba391a7e445d410bbfa497cddcb1b12cfd5904",
25
25
  "rules/gha018.ts": "2c71c5accb96bccfa01075e7f8f4ad66e6d9a68a81b86f89a68cabb9c144e140",
26
26
  "rules/gha019.ts": "d0b33d63812c9cba2ebf9f2d85d8cf22068ea96460cd2143bdfb4d2a4d58fe2e",
@@ -67,5 +67,5 @@
67
67
  "skills/chant-github-patterns.md": "bb3abef289a8fdfcf07d6bb2d7289dcb2f38bc0cb0321ea320b78b45a6f548c0",
68
68
  "skills/chant-github-security.md": "aab111cb0871cad30281ce48d7da23663689619351029219e2be019a1a61e394"
69
69
  },
70
- "composite": "62ffd94116aacdd7220d8e01cfaee61c07ec575b7b6dea5a6ca18cb129aaf533"
70
+ "composite": "b77a589c318cd23862c320a4e9ba5459696b8192700a8f9a23ff49519149d90b"
71
71
  }
@@ -1 +1 @@
1
- {"version":3,"file":"gha013.d.ts","sourceRoot":"","sources":["../../../src/lint/post-synth/gha013.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAyC,MAAM,kCAAkC,CAAC;AAG9G,eAAO,MAAM,MAAM,EAAE,cA6BpB,CAAC"}
1
+ {"version":3,"file":"gha013.d.ts","sourceRoot":"","sources":["../../../src/lint/post-synth/gha013.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAyC,MAAM,kCAAkC,CAAC;AAG9G,eAAO,MAAM,MAAM,EAAE,cAoCpB,CAAC"}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "github",
3
- "version": "0.46.0",
3
+ "version": "0.49.0",
4
4
  "chantVersion": ">=0.1.0",
5
5
  "namespace": "GitHub",
6
6
  "intrinsics": [
@@ -6,7 +6,7 @@
6
6
  */
7
7
 
8
8
  import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
9
- import { getPrimaryOutput, extractJobs, extractTriggers } from "./yaml-helpers";
9
+ import { getPrimaryOutput, extractJobs, extractTriggers, extractJobPermissions } from "./yaml-helpers";
10
10
 
11
11
  export const gha013: PostSynthCheck = {
12
12
  id: "GHA013",
@@ -21,9 +21,16 @@ export const gha013: PostSynthCheck = {
21
21
 
22
22
  if (!triggers["pull_request_target"] && !triggers["workflow_dispatch"]) continue;
23
23
 
24
+ // chant #1544 — `extractJobs`' `ParsedJob.permissions` field is never
25
+ // populated (its regex-based per-job scan only fills `needs`/`steps`),
26
+ // so this rule read `job.permissions` as always undefined and warned
27
+ // on every job regardless of whether it declared one. `extractJobPermissions`
28
+ // (below) is the structural, `parseYAML`-backed extractor that other
29
+ // permissions-aware checks already use — it actually sees the block.
24
30
  const jobs = extractJobs(yaml);
25
- for (const [jobName, job] of jobs) {
26
- if (!job.permissions) {
31
+ const jobPermissions = extractJobPermissions(yaml);
32
+ for (const [jobName] of jobs) {
33
+ if (!jobPermissions.has(jobName)) {
27
34
  diagnostics.push({
28
35
  checkId: "GHA013",
29
36
  severity: "warning",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/chant-lexicon-github",
3
- "version": "0.46.0",
3
+ "version": "0.49.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.46.0",
64
+ "@intentius/chant": "^0.49.0",
65
65
  "typescript": "^5.9.3"
66
66
  }
67
67
  }
@@ -50,6 +50,23 @@ jobs:
50
50
  expect(diags[0].message).toContain("deploy");
51
51
  });
52
52
 
53
+ test("does not flag a job that declares job-level permissions (#1544)", () => {
54
+ const yaml = `name: Deploy
55
+ on:
56
+ workflow_dispatch:
57
+ jobs:
58
+ deploy:
59
+ runs-on: ubuntu-latest
60
+ permissions:
61
+ contents: read
62
+ id-token: write
63
+ steps:
64
+ - run: echo deploy
65
+ `;
66
+ const diags = gha013.check(makeCtx(yaml));
67
+ expect(diags).toHaveLength(0);
68
+ });
69
+
53
70
  test("does not flag when trigger is not sensitive", () => {
54
71
  const yaml = `name: CI
55
72
  on:
@@ -6,7 +6,7 @@
6
6
  */
7
7
 
8
8
  import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
9
- import { getPrimaryOutput, extractJobs, extractTriggers } from "./yaml-helpers";
9
+ import { getPrimaryOutput, extractJobs, extractTriggers, extractJobPermissions } from "./yaml-helpers";
10
10
 
11
11
  export const gha013: PostSynthCheck = {
12
12
  id: "GHA013",
@@ -21,9 +21,16 @@ export const gha013: PostSynthCheck = {
21
21
 
22
22
  if (!triggers["pull_request_target"] && !triggers["workflow_dispatch"]) continue;
23
23
 
24
+ // chant #1544 — `extractJobs`' `ParsedJob.permissions` field is never
25
+ // populated (its regex-based per-job scan only fills `needs`/`steps`),
26
+ // so this rule read `job.permissions` as always undefined and warned
27
+ // on every job regardless of whether it declared one. `extractJobPermissions`
28
+ // (below) is the structural, `parseYAML`-backed extractor that other
29
+ // permissions-aware checks already use — it actually sees the block.
24
30
  const jobs = extractJobs(yaml);
25
- for (const [jobName, job] of jobs) {
26
- if (!job.permissions) {
31
+ const jobPermissions = extractJobPermissions(yaml);
32
+ for (const [jobName] of jobs) {
33
+ if (!jobPermissions.has(jobName)) {
27
34
  diagnostics.push({
28
35
  checkId: "GHA013",
29
36
  severity: "warning",