@intentius/chant-lexicon-github 0.13.1 → 0.15.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": "763818e9d77e6f0af8caa3f5d8b96e6185bafceb739029a9cf7252b9e40b4f0f",
4
+ "manifest.json": "e5e13815e9d6d26aa1408c7df281a66a311d881476f7a69235f4f9bde7cfd530",
5
5
  "meta.json": "798c5f5f37b0174756d2451299acfdc95c014f11d06d32b9153bbdfa9f3a580c",
6
6
  "types/index.d.ts": "39c0791f0e58025bcef82e0f7a92240508f899fc2771d5dd51b2369b4d330f13",
7
7
  "rules/deprecated-action-version.ts": "d41e6e532ab7f623af1bee4ac5279fcb2baada7defa1c5d022a5bc71983e8797",
@@ -67,5 +67,5 @@
67
67
  "skills/chant-github-patterns.md": "bb3abef289a8fdfcf07d6bb2d7289dcb2f38bc0cb0321ea320b78b45a6f548c0",
68
68
  "skills/chant-github-security.md": "aab111cb0871cad30281ce48d7da23663689619351029219e2be019a1a61e394"
69
69
  },
70
- "composite": "c61770f5aa4e06fb26e8ec58d68e8666c1891a3aeaf24d22f2f702a5feda395a"
70
+ "composite": "d55b5a0d07beebfcdc9186cdeaeb18362d4fa0a2ee7a1724a6be5f5b9d0efa79"
71
71
  }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * The github lexicon's chant audit catalog — metadata for its post-synth rules
3
+ * (GHA GitHub Actions rules). Contributed via githubPlugin.auditCatalog() (#687).
4
+ */
5
+ import { type RuleMeta } from "@intentius/chant/audit/catalog";
6
+ export declare const githubAuditCatalog: Record<string, RuleMeta>;
7
+ //# sourceMappingURL=audit-catalog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit-catalog.d.ts","sourceRoot":"","sources":["../../src/lint/audit-catalog.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAA4G,KAAK,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAEzK,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CA8CvD,CAAC"}
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "github",
3
- "version": "0.13.1",
3
+ "version": "0.15.0",
4
4
  "chantVersion": ">=0.1.0",
5
5
  "namespace": "GitHub",
6
6
  "intrinsics": [
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAiC,MAAM,0BAA0B,CAAC;AA0B7F,eAAO,MAAM,YAAY,EAAE,aA4S1B,CAAC"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAiC,MAAM,0BAA0B,CAAC;AA2B7F,eAAO,MAAM,YAAY,EAAE,aA6S1B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/chant-lexicon-github",
3
- "version": "0.13.1",
3
+ "version": "0.15.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,6 +61,6 @@
61
61
  "typescript": "^5.9.3"
62
62
  },
63
63
  "peerDependencies": {
64
- "@intentius/chant": "^0.13.1"
64
+ "@intentius/chant": "^0.15.0"
65
65
  }
66
66
  }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * The github lexicon's chant audit catalog — metadata for its post-synth rules
3
+ * (GHA GitHub Actions rules). Contributed via githubPlugin.auditCatalog() (#687).
4
+ */
5
+ import { auditRule, GH_INJECTION, GH_PWN, GH_SECRETS, GH_THIRD_PARTY, GH_TOKEN, SCORECARD_PINNED, SCORECARD_TOKEN, type RuleMeta } from "@intentius/chant/audit/catalog";
6
+
7
+ export const githubAuditCatalog: Record<string, RuleMeta> = {
8
+ GHA006: auditRule("GHA006", "report-only", "guidance", "Duplicate workflow name", "Give each workflow a unique `name:`.", { category: "correctness" }),
9
+ GHA009: auditRule("GHA009", "merge-worthy", "guidance", "Empty matrix dimension", "Remove the empty matrix axis or give it values; an empty axis produces zero jobs.", { category: "correctness" }),
10
+ GHA011: auditRule("GHA011", "merge-worthy", "guidance", "needs references a non-existent job", "Fix the `needs:` target to name a real job.", { category: "correctness" }),
11
+ GHA013: auditRule("GHA013", "merge-worthy", "guidance", "Missing job permissions on a sensitive trigger", "Add an explicit least-privilege `permissions:` block to jobs under `pull_request_target`/`workflow_dispatch`.", { authority: [SCORECARD_TOKEN, GH_TOKEN] }),
12
+ GHA017: auditRule("GHA017", "merge-worthy", "deterministic", "No explicit permissions block", "Add a top-level `permissions: { contents: read }` and widen only where a job needs it.", { authority: [SCORECARD_TOKEN, GH_TOKEN] }),
13
+ GHA018: auditRule("GHA018", "merge-worthy", "guidance", "pull_request_target checks out untrusted code", "Don't check out / run PR head code under `pull_request_target`; split into a privileged + unprivileged workflow.", { authority: [GH_PWN] }),
14
+ GHA019: auditRule("GHA019", "merge-worthy", "guidance", "Circular needs chain", "Break the cycle in the job dependency graph.", { category: "correctness" }),
15
+ GHA021: auditRule("GHA021", "merge-worthy", "deterministic", "actions/checkout not pinned to a SHA", "Pin `actions/checkout` to a full 40-char commit SHA.", { authority: [SCORECARD_PINNED, GH_THIRD_PARTY] }),
16
+ GHA022: auditRule("GHA022", "report-only", "guidance", "Job without timeout-minutes", "Add `timeout-minutes:` to bound runaway jobs.", { category: "best-practice" }),
17
+ GHA023: auditRule("GHA023", "report-only", "guidance", "Deprecated ::set-output", "Replace `::set-output` with `$GITHUB_OUTPUT`.", { category: "best-practice" }),
18
+ GHA024: auditRule("GHA024", "report-only", "guidance", "Missing concurrency block", "Add a `concurrency:` group to deploy workflows.", { category: "best-practice" }),
19
+ GHA025: auditRule("GHA025", "merge-worthy", "guidance", "Unrestricted pull_request_target", "Gate `pull_request_target` jobs and avoid running untrusted code with elevated scope.", { authority: [GH_PWN] }),
20
+ GHA026: auditRule("GHA026", "report-only", "guidance", "Secret used without environment protection", "Move secret-consuming jobs behind a protected `environment:`.", { category: "best-practice" }),
21
+ GHA027: auditRule("GHA027", "report-only", "guidance", "Cleanup step without if: always()", "Add `if: always()` to cleanup steps.", { category: "best-practice" }),
22
+ GHA028: auditRule("GHA028", "merge-worthy", "guidance", "Workflow with no triggers", "Add an `on:` trigger; the workflow never runs without one.", { category: "correctness" }),
23
+ GHA029: auditRule("GHA029", "merge-worthy", "deterministic", "Action not pinned to a commit SHA", "Pin the action to a full commit SHA instead of a tag/branch.", { authority: [SCORECARD_PINNED, GH_THIRD_PARTY] }),
24
+ GHA030: auditRule("GHA030", "merge-worthy", "deterministic", "Container image not pinned to a digest", "Pin the image to an immutable `@sha256:` digest.", { authority: [SCORECARD_PINNED] }),
25
+ GHA031: auditRule("GHA031", "merge-worthy", "guidance", "Possible action impersonation", "Verify the action owner/slug; it resembles a well-known action.", { authority: [SCORECARD_PINNED] }),
26
+ GHA032: auditRule("GHA032", "merge-worthy", "guidance", "Archived/abandoned or vulnerable action", "Replace the archived action or one with a disclosed security issue.", { category: "security" }),
27
+ GHA033: auditRule("GHA033", "merge-worthy", "deterministic", "Blanket write-all permissions", "Replace `write-all` with the specific scopes the jobs need (default `contents: read`).", { authority: [SCORECARD_TOKEN, GH_TOKEN] }),
28
+ GHA034: auditRule("GHA034", "merge-worthy", "guidance", "Write permissions granted workflow-wide", "Move write scopes to the single job that needs them; keep the workflow least-privilege.", { authority: [SCORECARD_TOKEN, GH_TOKEN] }),
29
+ GHA035: auditRule("GHA035", "merge-worthy", "guidance", "Elevated token on an untrusted-code trigger", "Drop the elevated `permissions:` on triggers that can run untrusted code.", { authority: [GH_PWN, SCORECARD_TOKEN] }),
30
+ GHA036: auditRule("GHA036", "merge-worthy", "guidance", "Untrusted input interpolated into run:", "Pass untrusted `${{ }}` values via an `env:` var and reference `\"$VAR\"`, never inline in the script.", { authority: [GH_INJECTION] }),
31
+ GHA037: auditRule("GHA037", "merge-worthy", "guidance", "Untrusted input written to GITHUB_ENV/GITHUB_PATH", "Don't write untrusted input to `$GITHUB_ENV`/`$GITHUB_PATH`; sanitize or avoid.", { authority: [GH_INJECTION] }),
32
+ GHA038: auditRule("GHA038", "merge-worthy", "guidance", "workflow_run checks out untrusted code in a privileged context", "Avoid checking out untrusted code under `workflow_run`; treat it as privileged.", { authority: [GH_PWN] }),
33
+ GHA039: auditRule("GHA039", "merge-worthy", "guidance", "Auth gate on a spoofable author field", "Gate on a non-spoofable identity, not a commit-author field.", { authority: [GH_PWN] }),
34
+ GHA040: auditRule("GHA040", "merge-worthy", "guidance", "Self-hosted runner on an untrusted-code trigger", "Don't run untrusted-code triggers on self-hosted runners.", { authority: [GH_PWN] }),
35
+ GHA041: auditRule("GHA041", "merge-worthy", "guidance", "Blanket secrets: inherit", "Pass only the specific secrets the reusable workflow needs.", { authority: [GH_SECRETS] }),
36
+ GHA042: auditRule("GHA042", "merge-worthy", "guidance", "Entire secrets context passed", "Pass named secrets instead of the whole `secrets` context.", { authority: [GH_SECRETS] }),
37
+ GHA043: auditRule("GHA043", "merge-worthy", "guidance", "Secret consumed without an environment gate", "Put secret-consuming jobs behind a protected environment.", { authority: [GH_SECRETS] }),
38
+ GHA044: auditRule("GHA044", "merge-worthy", "guidance", "Hardcoded registry/container credential", "Remove the hardcoded credential, move it to a secret, and rotate it (responsible disclosure first).", { authority: [GH_SECRETS] }),
39
+ GHA045: auditRule("GHA045", "merge-worthy", "guidance", "Secret interpolated into run:", "Reference secrets via `env:`, not inline in the shell command.", { authority: [GH_INJECTION, GH_SECRETS] }),
40
+ GHA046: auditRule("GHA046", "merge-worthy", "guidance", "Constant/unsound guard condition", "Fix the always-true/false `if:` — it may neutralize a security gate.", { category: "correctness" }),
41
+ GHA047: auditRule("GHA047", "merge-worthy", "guidance", "Ineffective contains() guard (reversed args)", "Swap the `contains()` arguments so the guard actually filters.", { category: "correctness" }),
42
+ GHA048: auditRule("GHA048", "merge-worthy", "guidance", "Obfuscated guard condition", "Simplify the indirect `if:` so its effect is reviewable.", { category: "correctness" }),
43
+ GHA049: auditRule("GHA049", "merge-worthy", "guidance", "Persisted checkout credentials reachable by an artifact", "Use `persist-credentials: false` or exclude `.git` from uploaded artifacts.", { authority: [GH_SECRETS] }),
44
+ GHA050: auditRule("GHA050", "merge-worthy", "guidance", "Cache populated in a privileged context", "Don't populate caches from untrusted code paths (poisoning risk).", { authority: [GH_PWN] }),
45
+ GHA051: auditRule("GHA051", "report-only", "guidance", "Long-lived token instead of OIDC", "Migrate publish/release to OIDC short-lived credentials.", { category: "best-practice" }),
46
+ GHA052: auditRule("GHA052", "merge-worthy", "guidance", "Software piped to a shell without verification", "Verify a checksum/signature before executing fetched scripts.", { authority: [SCORECARD_PINNED] }),
47
+ GHA053: auditRule("GHA053", "merge-worthy", "guidance", "Re-enables unsafe set-env/add-path", "Remove `ACTIONS_ALLOW_UNSECURE_COMMANDS`; use `$GITHUB_ENV`/`$GITHUB_PATH`.", { authority: [GH_INJECTION] }),
48
+ GHA054: auditRule("GHA054", "merge-worthy", "guidance", "Feature with a known security footgun", "Replace the flagged feature with the safe alternative.", { category: "security" }),
49
+ GHA055: auditRule("GHA055", "report-only", "guidance", "Runtime install of a tool already on the runner", "Drop the redundant install to save time.", { category: "best-practice" }),
50
+ GHA056: auditRule("GHA056", "report-only", "guidance", "Workflow without a name", "Add a `name:` to the workflow.", { category: "best-practice" }),
51
+ GHA057: auditRule("GHA057", "merge-worthy", "guidance", "Dependency update can execute untrusted code", "Disable the option that lets dependency updates run external code.", { authority: [GH_PWN] }),
52
+ GHA058: auditRule("GHA058", "report-only", "guidance", "Dependency update has no cooldown window", "Add a cooldown so new releases aren't merged instantly.", { category: "best-practice" }),
53
+ };
package/src/plugin.ts CHANGED
@@ -8,6 +8,7 @@
8
8
  import type { LexiconPlugin, IntrinsicDef, InitTemplateSet } from "@intentius/chant/lexicon";
9
9
  import type { LintRule } from "@intentius/chant/lint/rule";
10
10
  import { postSynthChecks as postSynthCheckList } from "./lint/post-synth";
11
+ import { githubAuditCatalog } from "./lint/audit-catalog";
11
12
  import { createSkillsLoader, createDiffTool, createCatalogResource } from "@intentius/chant/lexicon-plugin-helpers";
12
13
  import { join, dirname } from "path";
13
14
  import { fileURLToPath } from "url";
@@ -33,6 +34,7 @@ import { githubContextTools } from "./mcp/context-tools";
33
34
 
34
35
  export const githubPlugin: LexiconPlugin = {
35
36
  name: "github",
37
+ auditCatalog: () => githubAuditCatalog,
36
38
  serializer: githubSerializer,
37
39
 
38
40
  lintRules(): LintRule[] {