redlinegate 0.0.1

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.
Files changed (198) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +188 -0
  3. package/commands/redline-init.md +54 -0
  4. package/commands/redline-verify.md +69 -0
  5. package/dist/bin/redline.js +605 -0
  6. package/dist/bin/redline.js.map +1 -0
  7. package/dist/commands/exempt.js +45 -0
  8. package/dist/commands/exempt.js.map +1 -0
  9. package/dist/commands/init.js +615 -0
  10. package/dist/commands/init.js.map +1 -0
  11. package/dist/commands/policy.js +25 -0
  12. package/dist/commands/policy.js.map +1 -0
  13. package/dist/commands/remove.js +427 -0
  14. package/dist/commands/remove.js.map +1 -0
  15. package/dist/commands/review.js +72 -0
  16. package/dist/commands/review.js.map +1 -0
  17. package/dist/commands/sync.js +42 -0
  18. package/dist/commands/sync.js.map +1 -0
  19. package/dist/commands/verify.js +478 -0
  20. package/dist/commands/verify.js.map +1 -0
  21. package/dist/config/redline-json.js +187 -0
  22. package/dist/config/redline-json.js.map +1 -0
  23. package/dist/core/errors.js +25 -0
  24. package/dist/core/errors.js.map +1 -0
  25. package/dist/core/git.js +152 -0
  26. package/dist/core/git.js.map +1 -0
  27. package/dist/core/log.js +22 -0
  28. package/dist/core/log.js.map +1 -0
  29. package/dist/core/severity.js +16 -0
  30. package/dist/core/severity.js.map +1 -0
  31. package/dist/core/version.js +15 -0
  32. package/dist/core/version.js.map +1 -0
  33. package/dist/detect/scan.js +79 -0
  34. package/dist/detect/scan.js.map +1 -0
  35. package/dist/detect/stack.js +71 -0
  36. package/dist/detect/stack.js.map +1 -0
  37. package/dist/enforce/ladder.js +148 -0
  38. package/dist/enforce/ladder.js.map +1 -0
  39. package/dist/exempt/parse.js +86 -0
  40. package/dist/exempt/parse.js.map +1 -0
  41. package/dist/metrics/options.js +257 -0
  42. package/dist/metrics/options.js.map +1 -0
  43. package/dist/metrics/run.js +72 -0
  44. package/dist/metrics/run.js.map +1 -0
  45. package/dist/platforms/azure/client.js +53 -0
  46. package/dist/platforms/azure/client.js.map +1 -0
  47. package/dist/platforms/azure/index.js +69 -0
  48. package/dist/platforms/azure/index.js.map +1 -0
  49. package/dist/platforms/azure/install.js +917 -0
  50. package/dist/platforms/azure/install.js.map +1 -0
  51. package/dist/platforms/azure/policy-types.js +70 -0
  52. package/dist/platforms/azure/policy-types.js.map +1 -0
  53. package/dist/platforms/azure/verify.js +309 -0
  54. package/dist/platforms/azure/verify.js.map +1 -0
  55. package/dist/platforms/detect.js +40 -0
  56. package/dist/platforms/detect.js.map +1 -0
  57. package/dist/platforms/github/client.js +49 -0
  58. package/dist/platforms/github/client.js.map +1 -0
  59. package/dist/platforms/github/index.js +54 -0
  60. package/dist/platforms/github/index.js.map +1 -0
  61. package/dist/platforms/github/install.js +558 -0
  62. package/dist/platforms/github/install.js.map +1 -0
  63. package/dist/platforms/github/push.js +102 -0
  64. package/dist/platforms/github/push.js.map +1 -0
  65. package/dist/platforms/github/remote.js +36 -0
  66. package/dist/platforms/github/remote.js.map +1 -0
  67. package/dist/platforms/github/verify.js +366 -0
  68. package/dist/platforms/github/verify.js.map +1 -0
  69. package/dist/platforms/http.js +58 -0
  70. package/dist/platforms/http.js.map +1 -0
  71. package/dist/platforms/pull-request-templates.js +157 -0
  72. package/dist/platforms/pull-request-templates.js.map +1 -0
  73. package/dist/platforms/remote.js +21 -0
  74. package/dist/platforms/remote.js.map +1 -0
  75. package/dist/platforms/resolve.js +69 -0
  76. package/dist/platforms/resolve.js.map +1 -0
  77. package/dist/platforms/shape.js +19 -0
  78. package/dist/platforms/shape.js.map +1 -0
  79. package/dist/platforms/types.js +24 -0
  80. package/dist/platforms/types.js.map +1 -0
  81. package/dist/policy/checks.js +91 -0
  82. package/dist/policy/checks.js.map +1 -0
  83. package/dist/policy/diff.js +50 -0
  84. package/dist/policy/diff.js.map +1 -0
  85. package/dist/registry/discover.js +74 -0
  86. package/dist/registry/discover.js.map +1 -0
  87. package/dist/registry/serialize.js +41 -0
  88. package/dist/registry/serialize.js.map +1 -0
  89. package/dist/registry/types.js +2 -0
  90. package/dist/registry/types.js.map +1 -0
  91. package/dist/remove/host.js +294 -0
  92. package/dist/remove/host.js.map +1 -0
  93. package/dist/render/commands.js +139 -0
  94. package/dist/render/commands.js.map +1 -0
  95. package/dist/render/manifest.js +82 -0
  96. package/dist/render/manifest.js.map +1 -0
  97. package/dist/render/markers.js +166 -0
  98. package/dist/render/markers.js.map +1 -0
  99. package/dist/render/profile.js +23 -0
  100. package/dist/render/profile.js.map +1 -0
  101. package/dist/render/standards.js +169 -0
  102. package/dist/render/standards.js.map +1 -0
  103. package/dist/render/vendors.js +199 -0
  104. package/dist/render/vendors.js.map +1 -0
  105. package/dist/review/engines/api.js +95 -0
  106. package/dist/review/engines/api.js.map +1 -0
  107. package/dist/review/engines/embedded.js +20 -0
  108. package/dist/review/engines/embedded.js.map +1 -0
  109. package/dist/review/engines/types.js +2 -0
  110. package/dist/review/engines/types.js.map +1 -0
  111. package/dist/review/glob.js +29 -0
  112. package/dist/review/glob.js.map +1 -0
  113. package/dist/review/prompt.js +45 -0
  114. package/dist/review/prompt.js.map +1 -0
  115. package/dist/review/rules.js +25 -0
  116. package/dist/review/rules.js.map +1 -0
  117. package/dist/review/schema.js +84 -0
  118. package/dist/review/schema.js.map +1 -0
  119. package/dist/review/scope.js +27 -0
  120. package/dist/review/scope.js.map +1 -0
  121. package/dist/sarif/map.js +58 -0
  122. package/dist/sarif/map.js.map +1 -0
  123. package/dist/sarif/parse.js +88 -0
  124. package/dist/sarif/parse.js.map +1 -0
  125. package/dist/sarif/types.js +2 -0
  126. package/dist/sarif/types.js.map +1 -0
  127. package/dist/sync/host.js +21 -0
  128. package/dist/sync/host.js.map +1 -0
  129. package/dist/sync/plan.js +47 -0
  130. package/dist/sync/plan.js.map +1 -0
  131. package/dist/sync/render.js +57 -0
  132. package/dist/sync/render.js.map +1 -0
  133. package/dist/sync/run.js +118 -0
  134. package/dist/sync/run.js.map +1 -0
  135. package/dist/verify/host.js +52 -0
  136. package/dist/verify/host.js.map +1 -0
  137. package/dist/verify/remote.js +146 -0
  138. package/dist/verify/remote.js.map +1 -0
  139. package/package.json +52 -0
  140. package/platforms/azure/gate-template.yml +105 -0
  141. package/rulesets/redline-org-ruleset.json +46 -0
  142. package/rulesets/redline-ruleset.json +49 -0
  143. package/scripts/assign-rule-ids.mjs +129 -0
  144. package/scripts/build-baseline.mjs +124 -0
  145. package/scripts/build-correlation.mjs +74 -0
  146. package/scripts/build-dashboard.mjs +584 -0
  147. package/scripts/build-digest.mjs +164 -0
  148. package/scripts/build-inbox.mjs +133 -0
  149. package/scripts/build-registry.mjs +42 -0
  150. package/scripts/build-roi.mjs +181 -0
  151. package/scripts/check-pins.mjs +88 -0
  152. package/scripts/collect-telemetry.mjs +300 -0
  153. package/scripts/lib/__tests__/baseline.test.mjs +132 -0
  154. package/scripts/lib/__tests__/correlate.test.mjs +131 -0
  155. package/scripts/lib/__tests__/dora.test.mjs +110 -0
  156. package/scripts/lib/__tests__/exemptions.test.mjs +85 -0
  157. package/scripts/lib/__tests__/metrics-scanner.test.mjs +65 -0
  158. package/scripts/lib/__tests__/sarif.test.mjs +125 -0
  159. package/scripts/lib/__tests__/spend.test.mjs +64 -0
  160. package/scripts/lib/baseline.mjs +156 -0
  161. package/scripts/lib/correlate.mjs +125 -0
  162. package/scripts/lib/dora.mjs +133 -0
  163. package/scripts/lib/exemptions.mjs +79 -0
  164. package/scripts/lib/metrics.mjs +172 -0
  165. package/scripts/lib/rules.mjs +107 -0
  166. package/scripts/lib/sarif.mjs +108 -0
  167. package/scripts/lib/spend.mjs +69 -0
  168. package/scripts/measure-context.mjs +101 -0
  169. package/scripts/render-self.mjs +18 -0
  170. package/scripts/score-seeds.mjs +294 -0
  171. package/scripts/validate.mjs +394 -0
  172. package/standards/core.md +124 -0
  173. package/standards/manifest.json +214 -0
  174. package/standards/stacks/csharp.md +29 -0
  175. package/standards/stacks/go.md +30 -0
  176. package/standards/stacks/java.md +30 -0
  177. package/standards/stacks/javascript.md +42 -0
  178. package/standards/stacks/kotlin.md +27 -0
  179. package/standards/stacks/microservices.md +37 -0
  180. package/standards/stacks/nodejs.md +34 -0
  181. package/standards/stacks/python.md +29 -0
  182. package/standards/stacks/react-native.md +29 -0
  183. package/standards/stacks/react.md +46 -0
  184. package/standards/stacks/swift.md +26 -0
  185. package/standards/stacks/terraform.md +27 -0
  186. package/templates/CODEOWNERS +29 -0
  187. package/templates/azure/pull_request_template.md +72 -0
  188. package/templates/github/pull_request_template.md +72 -0
  189. package/templates/redline.yml +35 -0
  190. package/templates/repo-context.md +64 -0
  191. package/workflows/dashboard.yml +119 -0
  192. package/workflows/inbox.yml +97 -0
  193. package/workflows/redline-collect.yml +61 -0
  194. package/workflows/redline-gate.yml +301 -0
  195. package/workflows/redline-sync.yml +79 -0
  196. package/workflows/seed-canary.yml +231 -0
  197. package/workflows/verify-onboarding.yml +115 -0
  198. package/workflows/weekly-digest.yml +78 -0
@@ -0,0 +1,102 @@
1
+ import { RedlineError } from "../../core/errors.js";
2
+ const repoPath = (ref) => `/repos/${ref.org}/${ref.repo}`;
3
+ const ok = (status, body, what) => {
4
+ if (status >= 400 || body === null) {
5
+ throw new RedlineError('host', `${what} returned ${status}`);
6
+ }
7
+ return body;
8
+ };
9
+ // Commit a set of files onto a branch without a working copy.
10
+ //
11
+ // Sync runs against hundreds of repositories from one workflow. Cloning each to
12
+ // commit a handful of rendered files would dominate the run and needs write
13
+ // access to a checkout this process does not have, so the git data API does the
14
+ // work: one tree built from the files, one commit, one ref update. It is also
15
+ // atomic in a way a file-at-a-time contents API is not — a run that dies halfway
16
+ // leaves the branch untouched rather than half-updated.
17
+ export async function pushRemoteFiles(client, ref, push) {
18
+ // The base is always the default branch, never whatever the sync branch
19
+ // already points at: a stale sync branch must be re-based onto current main,
20
+ // or it would re-propose changes the repository already merged.
21
+ const baseRef = await client.rest('GET', `${repoPath(ref)}/git/ref/heads/${ref.defaultBranch}`);
22
+ const baseSha = ok(baseRef.status, baseRef.body, `reading heads/${ref.defaultBranch}`).object?.sha;
23
+ if (!baseSha)
24
+ throw new RedlineError('host', `${ref.defaultBranch} has no commit sha`);
25
+ const baseCommit = await client.rest('GET', `${repoPath(ref)}/git/commits/${baseSha}`);
26
+ const baseTree = ok(baseCommit.status, baseCommit.body, 'reading the base commit').tree?.sha;
27
+ if (!baseTree)
28
+ throw new RedlineError('host', 'base commit has no tree');
29
+ const tree = await client.rest('POST', `${repoPath(ref)}/git/trees`, {
30
+ base_tree: baseTree,
31
+ tree: push.files.map((file) => ({
32
+ path: file.path,
33
+ mode: '100644',
34
+ type: 'blob',
35
+ content: file.content,
36
+ })),
37
+ });
38
+ const treeSha = ok(tree.status, tree.body, 'creating the tree').sha;
39
+ if (!treeSha)
40
+ throw new RedlineError('host', 'tree creation returned no sha');
41
+ // An identical tree means the repository already carries exactly what this
42
+ // run would push. Committing it anyway would open a pull request with an
43
+ // empty diff on every scheduled run, on every already-current repository.
44
+ if (treeSha === baseTree)
45
+ return { commit: null, branch: push.branch };
46
+ const commit = await client.rest('POST', `${repoPath(ref)}/git/commits`, {
47
+ message: push.message,
48
+ tree: treeSha,
49
+ parents: [baseSha],
50
+ });
51
+ const commitSha = ok(commit.status, commit.body, 'creating the commit').sha;
52
+ if (!commitSha)
53
+ throw new RedlineError('host', 'commit creation returned no sha');
54
+ // Create the branch, or force it onto the new commit when a previous run left
55
+ // one. Force is safe here and only here: the branch is Redline's own, named
56
+ // by Redline, and its only content is a re-render of the same files.
57
+ const created = await client.rest('POST', `${repoPath(ref)}/git/refs`, {
58
+ ref: `refs/heads/${push.branch}`,
59
+ sha: commitSha,
60
+ });
61
+ if (created.status === 422) {
62
+ const updated = await client.rest('PATCH', `${repoPath(ref)}/git/refs/heads/${push.branch}`, { sha: commitSha, force: true });
63
+ ok(updated.status, updated.body ?? {}, `updating ${push.branch}`);
64
+ }
65
+ else {
66
+ ok(created.status, created.body ?? {}, `creating ${push.branch}`);
67
+ }
68
+ return { commit: commitSha, branch: push.branch };
69
+ }
70
+ // Open a pull request for a branch, or return the one already open for it.
71
+ //
72
+ // Idempotence is the whole requirement: sync runs on a schedule, and a target
73
+ // whose previous pull request is still unmerged must get an updated branch, not
74
+ // a second pull request competing with the first.
75
+ export async function openOrUpdatePullRequest(client, ref, pr) {
76
+ const existing = await client.rest('GET', `${repoPath(ref)}/pulls?state=open&head=${ref.org}:${pr.branch}`);
77
+ const open = existing.status < 400 ? (existing.body ?? []) : [];
78
+ if (open.length > 0 && open[0]) {
79
+ const found = open[0];
80
+ // The branch already carries the new commit; refresh the body so a reader
81
+ // sees the version this run proposes rather than the one it replaced.
82
+ await client.rest('PATCH', `${repoPath(ref)}/pulls/${found.number}`, {
83
+ title: pr.title,
84
+ body: pr.body,
85
+ });
86
+ return { number: found.number, url: found.html_url, created: false };
87
+ }
88
+ const created = await client.rest('POST', `${repoPath(ref)}/pulls`, { title: pr.title, body: pr.body, head: pr.branch, base: ref.defaultBranch });
89
+ const body = ok(created.status, created.body, 'opening the pull request');
90
+ if (typeof body.number !== 'number') {
91
+ throw new RedlineError('host', 'pull request creation returned no number');
92
+ }
93
+ if (pr.labels.length > 0) {
94
+ // Labels are a nicety. A repository that has not created them yet must not
95
+ // cost the pull request that carries the standards change.
96
+ await client.rest(`POST`, `${repoPath(ref)}/issues/${body.number}/labels`, {
97
+ labels: pr.labels,
98
+ });
99
+ }
100
+ return { number: body.number, url: body.html_url ?? '', created: true };
101
+ }
102
+ //# sourceMappingURL=push.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"push.js","sourceRoot":"","sources":["../../../cli/platforms/github/push.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import { RedlineError } from "../../core/errors.js";
2
+ import { parseRemoteConfig } from "../remote.js";
3
+ const repoPath = (ref) => `/repos/${ref.org}/${ref.repo}`;
4
+ // A file read at a ref, or null when the repository does not carry it. The
5
+ // distinction matters everywhere this is used: "no .redline.json" means not
6
+ // onboarded, and reporting that as a read failure would turn every un-onboarded
7
+ // repository in the estate into an error.
8
+ export async function readRemoteFile(client, ref, path) {
9
+ const response = await client.rest('GET', `${repoPath(ref)}/contents/${path}?ref=${encodeURIComponent(ref.defaultBranch)}`);
10
+ if (response.status === 404)
11
+ return null;
12
+ if (response.status >= 400) {
13
+ throw new RedlineError('host', `${ref.org}/${ref.repo}: reading ${path} returned ${response.status}`);
14
+ }
15
+ const body = response.body;
16
+ if (!body?.content || body.encoding !== 'base64') {
17
+ // A path that resolves to a directory comes back as an array, and a file
18
+ // over 1MB comes back with an empty content field. Both are real states
19
+ // that would otherwise decode to an empty string and parse as invalid.
20
+ throw new RedlineError('host', `${ref.org}/${ref.repo}: ${path} is not a readable file (no base64 content)`);
21
+ }
22
+ return {
23
+ content: Buffer.from(body.content, 'base64').toString('utf8'),
24
+ sha: body.sha ?? '',
25
+ };
26
+ }
27
+ export async function readRemoteConfig(client, ref) {
28
+ const file = await readRemoteFile(client, ref, '.redline.json');
29
+ if (!file)
30
+ return { config: null, ref: ref.defaultBranch };
31
+ return {
32
+ config: parseRemoteConfig(file.content, `${ref.org}/${ref.repo}`),
33
+ ref: ref.defaultBranch,
34
+ };
35
+ }
36
+ //# sourceMappingURL=remote.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote.js","sourceRoot":"","sources":["../../../cli/platforms/github/remote.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAA2B,MAAM,cAAc,CAAC;AAI1E,MAAM,QAAQ,GAAG,CAAC,GAAY,EAAU,EAAE,CAAC,UAAU,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;AAQ3E,2EAA2E;AAC3E,4EAA4E;AAC5E,gFAAgF;AAChF,0CAA0C;AAC1C,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,MAAkC,EAClC,GAAY,EACZ,IAAY;IAEZ,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,IAAI,CAChC,KAAK,EACL,GAAG,QAAQ,CAAC,GAAG,CAAC,aAAa,IAAI,QAAQ,kBAAkB,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,CACjF,CAAC;IACF,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC;IACzC,IAAI,QAAQ,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC;QAC3B,MAAM,IAAI,YAAY,CACpB,MAAM,EACN,GAAG,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,aAAa,IAAI,aAAa,QAAQ,CAAC,MAAM,EAAE,CACtE,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC;IAC3B,IAAI,CAAC,IAAI,EAAE,OAAO,IAAI,IAAI,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACjD,yEAAyE;QACzE,wEAAwE;QACxE,uEAAuE;QACvE,MAAM,IAAI,YAAY,CACpB,MAAM,EACN,GAAG,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,KAAK,IAAI,6CAA6C,CAC7E,CAAC;IACJ,CAAC;IACD,OAAO;QACL,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC;QAC7D,GAAG,EAAE,IAAI,CAAC,GAAG,IAAI,EAAE;KACpB,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,MAAkC,EAClC,GAAY;IAEZ,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC,MAAM,EAAE,GAAG,EAAE,eAAe,CAAC,CAAC;IAChE,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,CAAC,aAAa,EAAE,CAAC;IAC3D,OAAO;QACL,MAAM,EAAE,iBAAiB,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,EAAE,CAAC;QACjE,GAAG,EAAE,GAAG,CAAC,aAAa;KACvB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,366 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { RedlineError } from "../../core/errors.js";
4
+ import { REQUIRED_CHECK, RULESET_NAME } from "./install.js";
5
+ import { createHostShapeError, isNonNullObject } from "../shape.js";
6
+ const hostShapeError = createHostShapeError('GitHub');
7
+ function assertOk(status, path) {
8
+ if (status < 200 || status >= 300) {
9
+ throw new RedlineError('host', `GitHub returned HTTP ${status} reading ${path}`);
10
+ }
11
+ }
12
+ function parseRulesetList(body) {
13
+ if (!Array.isArray(body))
14
+ throw hostShapeError('the rulesets list');
15
+ return body.map((item) => {
16
+ if (!isNonNullObject(item) || typeof item['id'] !== 'number' || typeof item['name'] !== 'string') {
17
+ throw hostShapeError('a ruleset summary');
18
+ }
19
+ return { id: item['id'], name: item['name'] };
20
+ });
21
+ }
22
+ // `active`, `evaluate` or `disabled`. Anything but `active` means the ruleset
23
+ // is readable and not applying — the rules come back unchanged, so nothing
24
+ // else in this file can tell the difference. Absent is read as active: the
25
+ // field has always been present on a real ruleset, and guessing "not in force"
26
+ // from a shape surprise would fail a healthy repository.
27
+ function parseRulesetEnforcement(body) {
28
+ if (!isNonNullObject(body))
29
+ throw hostShapeError('a ruleset detail');
30
+ const enforcement = body['enforcement'];
31
+ return typeof enforcement === 'string' ? enforcement : 'active';
32
+ }
33
+ function parseRulesetRules(body) {
34
+ if (!isNonNullObject(body) || !Array.isArray(body['rules'])) {
35
+ throw hostShapeError('a ruleset detail');
36
+ }
37
+ return body['rules'].map((rule) => {
38
+ if (!isNonNullObject(rule) || typeof rule['type'] !== 'string') {
39
+ throw hostShapeError('a ruleset rule');
40
+ }
41
+ const parameters = rule['parameters'];
42
+ return {
43
+ type: rule['type'],
44
+ ...(isNonNullObject(parameters) ? { parameters } : {}),
45
+ };
46
+ });
47
+ }
48
+ function parsePullRequestParams(parameters) {
49
+ const p = parameters ?? {};
50
+ return {
51
+ requiredApprovingReviewCount: typeof p['required_approving_review_count'] === 'number'
52
+ ? p['required_approving_review_count']
53
+ : 0,
54
+ dismissStaleReviewsOnPush: p['dismiss_stale_reviews_on_push'] === true,
55
+ requireCodeOwnerReview: p['require_code_owner_review'] === true,
56
+ requiredReviewThreadResolution: p['required_review_thread_resolution'] === true,
57
+ };
58
+ }
59
+ function parseRequiredCheckContexts(parameters) {
60
+ const checks = parameters?.['required_status_checks'];
61
+ if (checks === undefined)
62
+ return [];
63
+ if (!Array.isArray(checks))
64
+ throw hostShapeError('required status checks');
65
+ return checks.map((c) => {
66
+ if (!isNonNullObject(c) || typeof c['context'] !== 'string') {
67
+ throw hostShapeError('a required status check');
68
+ }
69
+ return c['context'];
70
+ });
71
+ }
72
+ function parsePullRequestHeadSha(body) {
73
+ const head = isNonNullObject(body) ? body['head'] : undefined;
74
+ if (!isNonNullObject(head) || typeof head['sha'] !== 'string') {
75
+ throw hostShapeError('a pull request');
76
+ }
77
+ return head['sha'];
78
+ }
79
+ function parseCheckRunNames(body) {
80
+ if (!isNonNullObject(body) || !Array.isArray(body['check_runs'])) {
81
+ throw hostShapeError('a check-runs list');
82
+ }
83
+ return body['check_runs'].map((run) => {
84
+ if (!isNonNullObject(run) || typeof run['name'] !== 'string') {
85
+ throw hostShapeError('a check run');
86
+ }
87
+ return run['name'];
88
+ });
89
+ }
90
+ // null means GitHub did not report the block at all, which is not the same
91
+ // thing as reporting it empty — see readSecurityState.
92
+ function parseSecurityAnalysisStatuses(body) {
93
+ if (!isNonNullObject(body))
94
+ throw hostShapeError('a repository');
95
+ const analysis = body['security_and_analysis'];
96
+ if (analysis === undefined)
97
+ return null;
98
+ if (!isNonNullObject(analysis))
99
+ throw hostShapeError('security_and_analysis');
100
+ const statuses = {};
101
+ for (const [key, value] of Object.entries(analysis)) {
102
+ if (isNonNullObject(value) && typeof value['status'] === 'string') {
103
+ statuses[key] = value['status'];
104
+ }
105
+ }
106
+ return statuses;
107
+ }
108
+ function parsePullRequestNumbers(body) {
109
+ if (!Array.isArray(body))
110
+ throw hostShapeError('the pull request list');
111
+ return body.map((item) => {
112
+ if (!isNonNullObject(item) || typeof item['number'] !== 'number') {
113
+ throw hostShapeError('a pull request summary');
114
+ }
115
+ return item['number'];
116
+ });
117
+ }
118
+ // The caller workflow `installGate` writes. Its job id is half of the required
119
+ // check name — templates/redline.yml carries a DO NOT RENAME THE JOB warning
120
+ // for exactly this reason — so reading it back is what separates "the gate has
121
+ // not run on this pull request yet" from "nothing here can ever publish that
122
+ // check".
123
+ export const CALLER_WORKFLOW = '.github/workflows/redline.yml';
124
+ // The other half: whatever REQUIRED_CHECK puts after the separator, which is
125
+ // the aggregate job id inside workflows/redline-gate.yml. Derived rather than
126
+ // re-typed, so a change to the exported constant carries through.
127
+ const CHECK_SEPARATOR = ' / ';
128
+ const AGGREGATE_JOB = REQUIRED_CHECK.slice(REQUIRED_CHECK.indexOf(CHECK_SEPARATOR) + CHECK_SEPARATOR.length);
129
+ // Still not a YAML parse — this file is Redline's own, `init` rewrites it
130
+ // wholesale, and a zero-runtime-dependency CLI does not gain a YAML parser for
131
+ // one question. It is structural rather than shape-matched, which is a
132
+ // different thing: the scan finds the `jobs:` mapping and reads job ids from
133
+ // inside it and nowhere else.
134
+ //
135
+ // The version that pattern-matched a two-space key anywhere in the file took
136
+ // the last one it had seen before the `uses:` line, which in the shipped
137
+ // template is ` pull_request:` from the `on:` block. An inline comment on the
138
+ // job-id line — a plausible edit, given the DO NOT RENAME THE JOB banner four
139
+ // lines above it — was enough to fail the gate with "publishes pull_request /
140
+ // gate ... rename the job back", naming a job that exists nowhere about a job
141
+ // id that was already correct. Where this scan cannot attribute a name it
142
+ // returns null, whose message says the file could not be read for one; a
143
+ // message that admits it could not tell is honest, a fabricated job name is
144
+ // not.
145
+ const KEY = /^\s*(?:(["'])([\w.-]+)\1|([\w.-]+))\s*:(.*)$/;
146
+ const USES_THE_GATE = /^\s*uses:\s*["']?\S*redline-gate\.yml@/;
147
+ const indentOf = (line) => line.length - line.trimStart().length;
148
+ const skippable = (line) => line.trim() === '' || line.trimStart().startsWith('#');
149
+ const keyName = (line) => {
150
+ const match = KEY.exec(line);
151
+ return match?.[2] ?? match?.[3] ?? null;
152
+ };
153
+ function lines(body) {
154
+ return body.split('\n').map((line) => (line.endsWith('\r') ? line.slice(0, -1) : line));
155
+ }
156
+ // A workflow nothing triggers on a pull request publishes nothing, however
157
+ // correctly its job is named — and the ruleset still requires the check it
158
+ // will never send. `on:` is quoted in some hand-edited workflows because bare
159
+ // `on` is a YAML boolean, and the trigger may be inline (`on: [pull_request]`)
160
+ // or a key in the block below it.
161
+ export function triggersOnPullRequest(body) {
162
+ const all = lines(body);
163
+ for (let i = 0; i < all.length; i += 1) {
164
+ const line = all[i] ?? '';
165
+ if (skippable(line) || indentOf(line) !== 0 || keyName(line) !== 'on')
166
+ continue;
167
+ const inline = KEY.exec(line)?.[4] ?? '';
168
+ if (inline.trim() !== '')
169
+ return inline.includes('pull_request');
170
+ for (const rest of all.slice(i + 1)) {
171
+ if (skippable(rest))
172
+ continue;
173
+ if (indentOf(rest) === 0)
174
+ break;
175
+ if (rest.includes('pull_request'))
176
+ return true;
177
+ }
178
+ return false;
179
+ }
180
+ return false;
181
+ }
182
+ export function callerJobId(body) {
183
+ const all = lines(body);
184
+ let jobsIndent = null;
185
+ let jobIdIndent = null;
186
+ let jobId = null;
187
+ for (const line of all) {
188
+ if (skippable(line))
189
+ continue;
190
+ const indent = indentOf(line);
191
+ if (jobsIndent === null) {
192
+ // Flow style (`jobs: {redline-gate: …}`) leaves content after the colon
193
+ // and is deliberately not attributed: `init` does not write it, and a
194
+ // truthful "could not read a job" beats a guess.
195
+ if (keyName(line) === 'jobs' && (KEY.exec(line)?.[4] ?? '').trim() === '')
196
+ jobsIndent = indent;
197
+ continue;
198
+ }
199
+ // Back out to the level of `jobs:` or above and the block is over, so no
200
+ // later key can be mistaken for a job id.
201
+ if (indent <= jobsIndent)
202
+ break;
203
+ if (USES_THE_GATE.test(line) && jobId !== null)
204
+ return jobId;
205
+ const name = keyName(line);
206
+ if (name === null)
207
+ continue;
208
+ // The first key inside the block fixes the job-id column; only keys at
209
+ // exactly that column are job ids, so `uses:` and `with:` below one are
210
+ // never mistaken for another job.
211
+ if (jobIdIndent === null)
212
+ jobIdIndent = indent;
213
+ if (indent === jobIdIndent)
214
+ jobId = name;
215
+ }
216
+ return null;
217
+ }
218
+ // The same parse for a local file and a remote read. Sharing it is the point:
219
+ // a remote verify that re-derived "what does this file publish" independently
220
+ // would eventually disagree with the local one, and the two answers are the
221
+ // difference between a repository being reported healthy and being reported
222
+ // broken.
223
+ export function machineryFromBody(body) {
224
+ const base = { path: CALLER_WORKFLOW, expected: REQUIRED_CHECK };
225
+ if (body === null)
226
+ return { ...base, present: false, publishes: null };
227
+ const jobId = triggersOnPullRequest(body) ? callerJobId(body) : null;
228
+ return {
229
+ ...base,
230
+ present: true,
231
+ publishes: jobId === null ? null : `${jobId}${CHECK_SEPARATOR}${AGGREGATE_JOB}`,
232
+ };
233
+ }
234
+ export function createGitHubVerify(client) {
235
+ const repoPath = (ref) => `/repos/${ref.org}/${ref.repo}`;
236
+ return {
237
+ async readPolicy(ref) {
238
+ // Status is checked before the body is parsed, as azure/verify.ts does
239
+ // in all four of its methods. Without it a 403 error body ({"message":
240
+ // "Forbidden"}) fails the shape check instead, and the operator is sent
241
+ // after a host bug when the real problem is a token scope.
242
+ const listPath = `${repoPath(ref)}/rulesets`;
243
+ const list = await client.rest('GET', listPath);
244
+ assertOk(list.status, listPath);
245
+ const mine = parseRulesetList(list.body).find((r) => r.name === RULESET_NAME);
246
+ if (!mine)
247
+ return null;
248
+ const detailPath = `${repoPath(ref)}/rulesets/${mine.id}`;
249
+ const detail = await client.rest('GET', detailPath);
250
+ assertOk(detail.status, detailPath);
251
+ const rules = parseRulesetRules(detail.body);
252
+ const prParams = parsePullRequestParams(rules.find((r) => r.type === 'pull_request')?.parameters);
253
+ const checksRule = rules.find((r) => r.type === 'required_status_checks');
254
+ const enforcement = parseRulesetEnforcement(detail.body);
255
+ return {
256
+ requiredApprovals: prParams.requiredApprovingReviewCount,
257
+ dismissStaleReviews: prParams.dismissStaleReviewsOnPush,
258
+ requireCodeOwnerReview: prParams.requireCodeOwnerReview,
259
+ requireThreadResolution: prParams.requiredReviewThreadResolution,
260
+ requiredChecks: parseRequiredCheckContexts(checksRule?.parameters),
261
+ blocking: checksRule !== undefined,
262
+ ...(enforcement === 'active'
263
+ ? {}
264
+ : {
265
+ notEnforcedReason: `the ${RULESET_NAME} ruleset is set to "${enforcement}" rather than "active", so none of ` +
266
+ 'its rules apply and every setting below is readable but inert',
267
+ }),
268
+ // No unownedSettings: the ruleset read here is the one named `Redline`
269
+ // and `applyPolicy` writes every field above into it, so each one is
270
+ // Redline's own and `verify` may hold the repository to all of them.
271
+ };
272
+ },
273
+ readGateMachinery(cwd) {
274
+ const abs = join(cwd, CALLER_WORKFLOW);
275
+ if (!existsSync(abs))
276
+ return machineryFromBody(null);
277
+ // A local read that cannot be completed — a directory at the path, a
278
+ // file the process cannot open — is a finding about this repository, not
279
+ // an internal defect. Unguarded it escaped verify() as "redline failed
280
+ // unexpectedly" with the host exit code, for a read that never left the
281
+ // machine.
282
+ let body;
283
+ try {
284
+ body = readFileSync(abs, 'utf8');
285
+ }
286
+ catch (error) {
287
+ throw new RedlineError('failed', `cannot read ${CALLER_WORKFLOW}: ${error instanceof Error ? error.message : String(error)}`, 'restore it from redline init, or make it readable');
288
+ }
289
+ return machineryFromBody(body);
290
+ },
291
+ async readReportedCheckNames(ref, pr) {
292
+ const prPath = `${repoPath(ref)}/pulls/${pr}`;
293
+ const detail = await client.rest('GET', prPath);
294
+ assertOk(detail.status, prPath);
295
+ const sha = parsePullRequestHeadSha(detail.body);
296
+ const runsPath = `${repoPath(ref)}/commits/${sha}/check-runs?per_page=100`;
297
+ const runs = await client.rest('GET', runsPath);
298
+ assertOk(runs.status, runsPath);
299
+ return parseCheckRunNames(runs.body);
300
+ },
301
+ async readSecurityState(ref) {
302
+ const path = repoPath(ref);
303
+ const repo = await client.rest('GET', path);
304
+ assertOk(repo.status, path);
305
+ // GitHub omits security_and_analysis entirely for a requester without
306
+ // admin permission. "This token cannot see it" is not "it is off", and
307
+ // the difference matters: `denied` is the status that files pending-admin
308
+ // work and rewrites .redline.json, so reading an invisible setting as a
309
+ // refusal let a write-but-not-admin re-run overwrite a correct record
310
+ // with a false one. This is an INDETERMINATE read, not a definite one —
311
+ // GitHub does not say the feature is absent, only that this token
312
+ // cannot see it — so it degrades to `unknown`, not `unsupported`.
313
+ // `unsupported` is reserved for a definite "this does not exist here"
314
+ // answer (Azure's Advanced Security-unlicensed 404 is the one adapter
315
+ // that has one); conflating the two would let a plain `redline verify`
316
+ // pass a repository nobody has actually confirmed, since `unsupported`
317
+ // no longer fails it on its own (see cli/commands/verify.ts). Absent
318
+ // block -> unknown, which isPending excludes exactly like unsupported;
319
+ // present but not enabled -> denied, as before.
320
+ const statuses = parseSecurityAnalysisStatuses(repo.body);
321
+ const invisible = statuses === null;
322
+ const stateFor = (key) => invisible ? 'unknown' : statuses[key] === 'enabled' ? 'applied' : 'denied';
323
+ const note = invisible ? ' (not visible to this token)' : '';
324
+ // Dependabot alerts live on their own endpoint, and it answers with a
325
+ // status rather than a body: 204 enabled, 404 disabled (the repository
326
+ // read above already proved the repository itself resolves, so a 404
327
+ // here is about the feature, not the path). 403 is the one case where
328
+ // nothing was learned — an indeterminate read is not an answer, and
329
+ // `denied` is the status that files work against an administrator. Same
330
+ // reasoning as the block above: this is indeterminate, not a definite
331
+ // "not available here", so it degrades to `unknown`. Any other non-2xx
332
+ // is a host failure, same as everywhere else in this file.
333
+ const alertsPath = `${path}/vulnerability-alerts`;
334
+ const alerts = await client.rest('GET', alertsPath);
335
+ if (alerts.status !== 404 && alerts.status !== 403)
336
+ assertOk(alerts.status, alertsPath);
337
+ const alertsStatus = alerts.status === 403 ? 'unknown' : alerts.status === 404 ? 'denied' : 'applied';
338
+ return {
339
+ outcomes: [
340
+ {
341
+ capability: 'secret-scanning',
342
+ status: stateFor('secret_scanning'),
343
+ detail: `secret scanning${note}`,
344
+ },
345
+ {
346
+ capability: 'push-protection',
347
+ status: stateFor('secret_scanning_push_protection'),
348
+ detail: `secret scanning push protection${note}`,
349
+ },
350
+ {
351
+ capability: 'dependency-alerts',
352
+ status: alertsStatus,
353
+ detail: `dependabot alerts (vulnerability alerts)${alertsStatus === 'unknown' ? ' (not visible to this token)' : ''}`,
354
+ },
355
+ ],
356
+ };
357
+ },
358
+ async latestPullRequestNumber(ref) {
359
+ const path = `${repoPath(ref)}/pulls?state=all&per_page=1`;
360
+ const list = await client.rest('GET', path);
361
+ assertOk(list.status, path);
362
+ return parsePullRequestNumbers(list.body)[0] ?? null;
363
+ },
364
+ };
365
+ }
366
+ //# sourceMappingURL=verify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify.js","sourceRoot":"","sources":["../../../cli/platforms/github/verify.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ import { RedlineError } from "../core/errors.js";
2
+ const IDEMPOTENT = new Set(['GET', 'PUT', 'PATCH', 'DELETE', 'HEAD']);
3
+ // 429 is pre-execution (rate limiting), so any method may retry; a 5xx may have
4
+ // committed the request server-side, so only idempotent methods retry — a retried
5
+ // POST would duplicate the PR/policy it created.
6
+ const RETRYABLE = (method, status) => status === 429 || (status >= 500 && IDEMPOTENT.has(method.toUpperCase()));
7
+ const defaultSleep = (ms) => new Promise((r) => setTimeout(r, ms));
8
+ export function createHttp(baseUrl, headers, opts = {}) {
9
+ const doFetch = opts.fetch ?? globalThis.fetch;
10
+ const retries = opts.retries ?? 3;
11
+ const sleep = opts.sleep ?? defaultSleep;
12
+ return {
13
+ async request(method, path, body) {
14
+ const url = path.startsWith('http') ? path : `${baseUrl}${path}`;
15
+ const init = { method, headers: { ...headers } };
16
+ if (body !== undefined) {
17
+ init.body = JSON.stringify(body);
18
+ init.headers = { ...headers, 'content-type': 'application/json' };
19
+ }
20
+ let lastStatus = 0;
21
+ for (let attempt = 0; attempt < retries; attempt += 1) {
22
+ let response;
23
+ try {
24
+ response = await doFetch(url, init);
25
+ }
26
+ catch (cause) {
27
+ if (attempt === retries - 1) {
28
+ throw new RedlineError('host', `request to ${url} failed: ${String(cause)}`);
29
+ }
30
+ await sleep(2 ** attempt * 200);
31
+ continue;
32
+ }
33
+ if (RETRYABLE(method, response.status)) {
34
+ lastStatus = response.status;
35
+ if (attempt === retries - 1)
36
+ break;
37
+ await sleep(2 ** attempt * 200);
38
+ continue;
39
+ }
40
+ if (response.status === 204 || response.headers.get('content-length') === '0') {
41
+ return { status: response.status, body: null };
42
+ }
43
+ const text = await response.text();
44
+ if (text === '') {
45
+ return { status: response.status, body: null };
46
+ }
47
+ try {
48
+ return { status: response.status, body: JSON.parse(text) };
49
+ }
50
+ catch {
51
+ throw new RedlineError('host', `request to ${url} returned ${response.status} with a body that was not JSON`);
52
+ }
53
+ }
54
+ throw new RedlineError('host', `request to ${url} kept returning ${lastStatus}`);
55
+ },
56
+ };
57
+ }
58
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../../cli/platforms/http.ts"],"names":[],"mappings":"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"}