@intentius/chant-lexicon-github 0.0.18 → 0.0.22
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/integrity.json +14 -4
- package/dist/manifest.json +1 -1
- package/dist/rules/gha020.ts +40 -0
- package/dist/rules/gha021.ts +48 -0
- package/dist/rules/gha022.ts +50 -0
- package/dist/rules/gha023.ts +44 -0
- package/dist/rules/gha024.ts +42 -0
- package/dist/rules/gha025.ts +42 -0
- package/dist/rules/gha026.ts +40 -0
- package/dist/rules/gha027.ts +57 -0
- package/dist/rules/gha028.ts +37 -0
- package/dist/skills/chant-github.md +1 -1
- package/dist/skills/github-actions-security.md +87 -0
- package/package.json +20 -2
- package/src/codegen/docs.test.ts +19 -0
- package/src/codegen/generate.test.ts +12 -0
- package/src/codegen/package.test.ts +8 -0
- package/src/coverage.test.ts +23 -0
- package/src/import/roundtrip.test.ts +206 -0
- package/src/lint/post-synth/gha006.test.ts +56 -0
- package/src/lint/post-synth/gha009.test.ts +56 -0
- package/src/lint/post-synth/gha011.test.ts +61 -0
- package/src/lint/post-synth/gha017.test.ts +51 -0
- package/src/lint/post-synth/gha018.test.ts +66 -0
- package/src/lint/post-synth/gha019.test.ts +66 -0
- package/src/lint/post-synth/gha020.test.ts +66 -0
- package/src/lint/post-synth/gha020.ts +40 -0
- package/src/lint/post-synth/gha021.test.ts +67 -0
- package/src/lint/post-synth/gha021.ts +48 -0
- package/src/lint/post-synth/gha022.test.ts +45 -0
- package/src/lint/post-synth/gha022.ts +50 -0
- package/src/lint/post-synth/gha023.test.ts +52 -0
- package/src/lint/post-synth/gha023.ts +44 -0
- package/src/lint/post-synth/gha024.test.ts +67 -0
- package/src/lint/post-synth/gha024.ts +42 -0
- package/src/lint/post-synth/gha025.test.ts +65 -0
- package/src/lint/post-synth/gha025.ts +42 -0
- package/src/lint/post-synth/gha026.test.ts +65 -0
- package/src/lint/post-synth/gha026.ts +40 -0
- package/src/lint/post-synth/gha027.test.ts +48 -0
- package/src/lint/post-synth/gha027.ts +57 -0
- package/src/lint/post-synth/gha028.test.ts +48 -0
- package/src/lint/post-synth/gha028.ts +37 -0
- package/src/lint/rules/deprecated-action-version.test.ts +26 -0
- package/src/lint/rules/detect-secrets.test.ts +25 -0
- package/src/lint/rules/extract-inline-structs.test.ts +25 -0
- package/src/lint/rules/file-job-limit.test.ts +28 -0
- package/src/lint/rules/job-timeout.test.ts +31 -0
- package/src/lint/rules/missing-recommended-inputs.test.ts +26 -0
- package/src/lint/rules/no-hardcoded-secrets.test.ts +31 -0
- package/src/lint/rules/no-raw-expressions.test.ts +31 -0
- package/src/lint/rules/suggest-cache.test.ts +25 -0
- package/src/lint/rules/use-condition-builders.test.ts +31 -0
- package/src/lint/rules/use-matrix-builder.test.ts +25 -0
- package/src/lint/rules/use-typed-actions.test.ts +39 -0
- package/src/lint/rules/validate-concurrency.test.ts +31 -0
- package/src/plugin.test.ts +1 -1
- package/src/plugin.ts +30 -2
- package/src/skills/github-actions-security.md +87 -0
- package/src/validate.ts +14 -1
- package/src/variables.test.ts +48 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
---
|
|
2
|
+
skill: github-actions-security
|
|
3
|
+
description: GitHub Actions security best practices — secret scanning, OIDC, permissions hardening, supply chain security
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# GitHub Actions Security Playbook
|
|
7
|
+
|
|
8
|
+
## Permissions Hardening
|
|
9
|
+
|
|
10
|
+
Always set the minimum required permissions at the workflow level:
|
|
11
|
+
|
|
12
|
+
```typescript
|
|
13
|
+
new Workflow({
|
|
14
|
+
name: "CI",
|
|
15
|
+
on: { push: { branches: ["main"] } },
|
|
16
|
+
permissions: { contents: "read" },
|
|
17
|
+
});
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
For deployments that need write access, scope it to the job:
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
new Job({
|
|
24
|
+
"runs-on": "ubuntu-latest",
|
|
25
|
+
permissions: { contents: "read", "id-token": "write" },
|
|
26
|
+
steps: [...],
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Pin Actions by SHA
|
|
31
|
+
|
|
32
|
+
Never use mutable tags like `@v4`. Pin to a full commit SHA:
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
new Step({
|
|
36
|
+
uses: "actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11", // v4.1.1
|
|
37
|
+
})
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## OIDC for Cloud Providers
|
|
41
|
+
|
|
42
|
+
Use OpenID Connect instead of long-lived secrets:
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
// AWS
|
|
46
|
+
new Step({
|
|
47
|
+
uses: "aws-actions/configure-aws-credentials@v4",
|
|
48
|
+
with: {
|
|
49
|
+
"role-to-assume": "arn:aws:iam::123456789012:role/deploy",
|
|
50
|
+
"aws-region": "us-east-1",
|
|
51
|
+
},
|
|
52
|
+
})
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Secret Scanning
|
|
56
|
+
|
|
57
|
+
- Never echo secrets in `run:` steps
|
|
58
|
+
- Use `environment` protection rules for production secrets
|
|
59
|
+
- Rotate secrets regularly and audit access logs
|
|
60
|
+
|
|
61
|
+
## Supply Chain Security
|
|
62
|
+
|
|
63
|
+
- Use `permissions: {}` (empty) as a baseline, then grant only what each job needs
|
|
64
|
+
- Avoid `pull_request_target` with `actions/checkout` (code injection risk)
|
|
65
|
+
- Use Dependabot or Renovate to keep action versions current
|
|
66
|
+
- Add `concurrency` blocks to prevent parallel deploys
|
|
67
|
+
|
|
68
|
+
## Concurrency Control
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
new Concurrency({
|
|
72
|
+
group: "${{ github.workflow }}-${{ github.ref }}",
|
|
73
|
+
"cancel-in-progress": true,
|
|
74
|
+
})
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Job Timeouts
|
|
78
|
+
|
|
79
|
+
Always set timeouts to prevent runaway jobs:
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
new Job({
|
|
83
|
+
"runs-on": "ubuntu-latest",
|
|
84
|
+
"timeout-minutes": 30,
|
|
85
|
+
steps: [...],
|
|
86
|
+
});
|
|
87
|
+
```
|
package/src/validate.ts
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* Validate generated lexicon-github artifacts.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { dirname } from "path";
|
|
5
|
+
import { dirname, join } from "path";
|
|
6
|
+
import { existsSync } from "fs";
|
|
6
7
|
import { fileURLToPath } from "url";
|
|
7
8
|
import { validateLexiconArtifacts, type ValidateResult } from "@intentius/chant/codegen/validate";
|
|
8
9
|
|
|
@@ -23,6 +24,18 @@ const REQUIRED_NAMES = [
|
|
|
23
24
|
*/
|
|
24
25
|
export async function validate(opts?: { basePath?: string }): Promise<ValidateResult> {
|
|
25
26
|
const basePath = opts?.basePath ?? dirname(dirname(fileURLToPath(import.meta.url)));
|
|
27
|
+
const generatedDir = join(basePath, "src", "generated");
|
|
28
|
+
|
|
29
|
+
if (!existsSync(generatedDir)) {
|
|
30
|
+
return {
|
|
31
|
+
success: false,
|
|
32
|
+
checks: [{
|
|
33
|
+
name: "generated-dir",
|
|
34
|
+
ok: false,
|
|
35
|
+
error: 'src/generated/ not found — run "chant dev generate" first',
|
|
36
|
+
}],
|
|
37
|
+
};
|
|
38
|
+
}
|
|
26
39
|
|
|
27
40
|
return validateLexiconArtifacts({
|
|
28
41
|
lexiconJsonFilename: "lexicon-github.json",
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
import { GitHub, Runner } from "./variables";
|
|
3
|
+
|
|
4
|
+
describe("GitHub context variables", () => {
|
|
5
|
+
test("GitHub.Ref resolves to github.ref expression", () => {
|
|
6
|
+
expect(GitHub.Ref.toString()).toBe("${{ github.ref }}");
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
test("GitHub.Sha resolves to github.sha expression", () => {
|
|
10
|
+
expect(GitHub.Sha.toString()).toBe("${{ github.sha }}");
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test("GitHub.Actor resolves to github.actor expression", () => {
|
|
14
|
+
expect(GitHub.Actor.toString()).toBe("${{ github.actor }}");
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("GitHub has all expected properties", () => {
|
|
18
|
+
const keys = Object.keys(GitHub);
|
|
19
|
+
expect(keys).toContain("Ref");
|
|
20
|
+
expect(keys).toContain("Sha");
|
|
21
|
+
expect(keys).toContain("Actor");
|
|
22
|
+
expect(keys).toContain("Repository");
|
|
23
|
+
expect(keys).toContain("EventName");
|
|
24
|
+
expect(keys).toContain("Token");
|
|
25
|
+
expect(keys).toContain("Workspace");
|
|
26
|
+
expect(keys.length).toBe(25);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe("Runner context variables", () => {
|
|
31
|
+
test("Runner.Os resolves to runner.os expression", () => {
|
|
32
|
+
expect(Runner.Os.toString()).toBe("${{ runner.os }}");
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("Runner.Arch resolves to runner.arch expression", () => {
|
|
36
|
+
expect(Runner.Arch.toString()).toBe("${{ runner.arch }}");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test("Runner has all expected properties", () => {
|
|
40
|
+
const keys = Object.keys(Runner);
|
|
41
|
+
expect(keys).toContain("Os");
|
|
42
|
+
expect(keys).toContain("Arch");
|
|
43
|
+
expect(keys).toContain("Name");
|
|
44
|
+
expect(keys).toContain("Temp");
|
|
45
|
+
expect(keys).toContain("ToolCache");
|
|
46
|
+
expect(keys.length).toBe(5);
|
|
47
|
+
});
|
|
48
|
+
});
|