lula2 0.0.5 → 0.0.6

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 (108) hide show
  1. package/README.md +291 -8
  2. package/dist/_app/env.js +1 -0
  3. package/dist/_app/immutable/assets/0.DtiRW3lO.css +1 -0
  4. package/dist/_app/immutable/assets/DynamicControlEditor.BkVTzFZ-.css +1 -0
  5. package/dist/_app/immutable/chunks/7x_q-1ab.js +1 -0
  6. package/dist/_app/immutable/chunks/B19gt6-g.js +2 -0
  7. package/dist/_app/immutable/chunks/BR-0Dorr.js +1 -0
  8. package/dist/_app/immutable/chunks/B_3ksxz5.js +2 -0
  9. package/dist/_app/immutable/chunks/Bg_R1qWi.js +3 -0
  10. package/dist/_app/immutable/chunks/D3aNP_lg.js +1 -0
  11. package/dist/_app/immutable/chunks/D4Q_ObIy.js +1 -0
  12. package/dist/_app/immutable/chunks/DsnmJJEf.js +1 -0
  13. package/dist/_app/immutable/chunks/XY2j_owG.js +66 -0
  14. package/dist/_app/immutable/chunks/rzN25oDf.js +1 -0
  15. package/dist/_app/immutable/entry/app.r0uOd9qg.js +2 -0
  16. package/dist/_app/immutable/entry/start.DvoqR0rc.js +1 -0
  17. package/dist/_app/immutable/nodes/0.Ct6FAss_.js +1 -0
  18. package/dist/_app/immutable/nodes/1.DLoKuy8Q.js +1 -0
  19. package/dist/_app/immutable/nodes/2.IRkwSmiB.js +1 -0
  20. package/dist/_app/immutable/nodes/3.BrTg-ZHv.js +1 -0
  21. package/dist/_app/immutable/nodes/4.Blq-4WQS.js +9 -0
  22. package/dist/_app/version.json +1 -0
  23. package/dist/cli/commands/crawl.js +128 -0
  24. package/dist/cli/commands/ui.js +2769 -0
  25. package/dist/cli/commands/version.js +30 -0
  26. package/dist/cli/server/index.js +2713 -0
  27. package/dist/cli/server/server.js +2702 -0
  28. package/dist/cli/server/serverState.js +1199 -0
  29. package/dist/cli/server/spreadsheetRoutes.js +788 -0
  30. package/dist/cli/server/types.js +0 -0
  31. package/dist/cli/server/websocketServer.js +2625 -0
  32. package/dist/cli/utils/debug.js +24 -0
  33. package/dist/favicon.svg +1 -0
  34. package/dist/index.html +38 -0
  35. package/dist/index.js +2924 -37
  36. package/dist/lula.png +0 -0
  37. package/dist/lula2 +2 -0
  38. package/package.json +120 -72
  39. package/src/app.css +192 -0
  40. package/src/app.d.ts +13 -0
  41. package/src/app.html +13 -0
  42. package/src/lib/actions/fadeWhenScrollable.ts +39 -0
  43. package/src/lib/actions/modal.ts +230 -0
  44. package/src/lib/actions/tooltip.ts +82 -0
  45. package/src/lib/components/control-sets/ControlSetInfo.svelte +20 -0
  46. package/src/lib/components/control-sets/ControlSetSelector.svelte +46 -0
  47. package/src/lib/components/control-sets/index.ts +5 -0
  48. package/src/lib/components/controls/ControlDetailsPanel.svelte +235 -0
  49. package/src/lib/components/controls/ControlsList.svelte +608 -0
  50. package/src/lib/components/controls/DynamicControlEditor.svelte +298 -0
  51. package/src/lib/components/controls/MappingCard.svelte +105 -0
  52. package/src/lib/components/controls/MappingForm.svelte +188 -0
  53. package/src/lib/components/controls/index.ts +9 -0
  54. package/src/lib/components/controls/renderers/EditableFieldRenderer.svelte +103 -0
  55. package/src/lib/components/controls/renderers/FieldRenderer.svelte +49 -0
  56. package/src/lib/components/controls/renderers/index.ts +5 -0
  57. package/src/lib/components/controls/tabs/CustomFieldsTab.svelte +130 -0
  58. package/src/lib/components/controls/tabs/ImplementationTab.svelte +127 -0
  59. package/src/lib/components/controls/tabs/MappingsTab.svelte +182 -0
  60. package/src/lib/components/controls/tabs/OverviewTab.svelte +151 -0
  61. package/src/lib/components/controls/tabs/TimelineTab.svelte +41 -0
  62. package/src/lib/components/controls/tabs/index.ts +8 -0
  63. package/src/lib/components/controls/utils/ProcessedTextRenderer.svelte +63 -0
  64. package/src/lib/components/controls/utils/textProcessor.ts +164 -0
  65. package/src/lib/components/forms/DynamicControlForm.svelte +340 -0
  66. package/src/lib/components/forms/DynamicField.svelte +494 -0
  67. package/src/lib/components/forms/FormField.svelte +107 -0
  68. package/src/lib/components/forms/index.ts +6 -0
  69. package/src/lib/components/setup/ExistingControlSets.svelte +284 -0
  70. package/src/lib/components/setup/SpreadsheetImport.svelte +968 -0
  71. package/src/lib/components/setup/index.ts +5 -0
  72. package/src/lib/components/ui/Dropdown.svelte +107 -0
  73. package/src/lib/components/ui/EmptyState.svelte +80 -0
  74. package/src/lib/components/ui/FeatureToggle.svelte +50 -0
  75. package/src/lib/components/ui/SearchBar.svelte +73 -0
  76. package/src/lib/components/ui/StatusBadge.svelte +79 -0
  77. package/src/lib/components/ui/TabNavigation.svelte +48 -0
  78. package/src/lib/components/ui/Tooltip.svelte +120 -0
  79. package/src/lib/components/ui/index.ts +10 -0
  80. package/src/lib/components/version-control/DiffViewer.svelte +292 -0
  81. package/src/lib/components/version-control/TimelineItem.svelte +107 -0
  82. package/src/lib/components/version-control/YamlDiffViewer.svelte +428 -0
  83. package/src/lib/components/version-control/index.ts +6 -0
  84. package/src/lib/form-types.ts +57 -0
  85. package/src/lib/formatUtils.ts +17 -0
  86. package/src/lib/index.ts +5 -0
  87. package/src/lib/types.ts +180 -0
  88. package/src/lib/websocket.ts +359 -0
  89. package/src/routes/+layout.svelte +236 -0
  90. package/src/routes/+page.svelte +38 -0
  91. package/src/routes/control/[id]/+page.svelte +112 -0
  92. package/src/routes/setup/+page.svelte +241 -0
  93. package/src/stores/compliance.ts +95 -0
  94. package/src/styles/highlightjs.css +20 -0
  95. package/src/styles/modal.css +58 -0
  96. package/src/styles/tables.css +111 -0
  97. package/src/styles/tooltip.css +65 -0
  98. package/dist/controls/index.d.ts +0 -18
  99. package/dist/controls/index.d.ts.map +0 -1
  100. package/dist/controls/index.js +0 -18
  101. package/dist/crawl.d.ts +0 -62
  102. package/dist/crawl.d.ts.map +0 -1
  103. package/dist/crawl.js +0 -172
  104. package/dist/index.d.ts +0 -8
  105. package/dist/index.d.ts.map +0 -1
  106. package/src/controls/index.ts +0 -19
  107. package/src/crawl.ts +0 -227
  108. package/src/index.ts +0 -46
@@ -1,18 +0,0 @@
1
- export declare const controls: {
2
- "123e4567-e89b-12d3-a456-426614174001": {
3
- id: string;
4
- description: string;
5
- remarks: string;
6
- };
7
- "123e4567-e89b-12d3-a456-426614174002": {
8
- id: string;
9
- description: string;
10
- remarks: string;
11
- };
12
- "123e4567-e89b-12d3-a456-426614174003": {
13
- id: string;
14
- description: string;
15
- remarks: string;
16
- };
17
- };
18
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/controls/index.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;CAgBnB,CAAC"}
@@ -1,18 +0,0 @@
1
- import { registerControls } from "compliance-reporter";
2
- export const controls = registerControls({
3
- "123e4567-e89b-12d3-a456-426614174001": {
4
- id: "AC-1",
5
- description: "Restrict Pods running as root",
6
- remarks: "This control ensures that no Pods are allowed to run as the root user.",
7
- },
8
- "123e4567-e89b-12d3-a456-426614174002": {
9
- id: "AC-2",
10
- description: "Restrict Pods running privileged containers",
11
- remarks: "This control ensures that no Pods are allowed to run as privileged containers.",
12
- },
13
- "123e4567-e89b-12d3-a456-426614174003": {
14
- id: "AC-3",
15
- description: "Restrict Pods running as root",
16
- remarks: "This control ensures that no Pods are allowed to run as the root user.",
17
- },
18
- });
package/dist/crawl.d.ts DELETED
@@ -1,62 +0,0 @@
1
- import { Octokit } from "@octokit/rest";
2
- import { Command } from "commander";
3
- /**
4
- * Get the pull request context from the environment or GitHub event payload.
5
- *
6
- * @returns The pull request context containing the owner, repo, and pull number.
7
- */
8
- export declare function getPRContext(): {
9
- owner: string;
10
- repo: string;
11
- pull_number: number;
12
- };
13
- /**
14
- * Fetch a raw file from a GitHub repository via the GitHub API.
15
- *
16
- * @param params - The parameters.
17
- * @param params.octokit - An authenticated Octokit instance.
18
- * @param params.owner - The owner of the repository.
19
- * @param params.repo - The name of the repository.
20
- * @param params.path - The path to the file in the repository.
21
- * @param params.ref - The git reference (branch, tag, or commit SHA).
22
- * @returns The content of the file as a string.
23
- */
24
- export declare function fetchRawFileViaAPI({ octokit, owner, repo, path, ref, }: {
25
- octokit: Octokit;
26
- owner: string;
27
- repo: string;
28
- path: string;
29
- ref: string;
30
- }): Promise<string>;
31
- /**
32
- * Extracts all @lulaStart and @lulaEnd blocks from the given content.
33
- *
34
- * @param content - The content to extract blocks from.
35
- *
36
- * @returns An array of objects containing the UUID, start line, and end line of each block.
37
- */
38
- export declare function extractMapBlocks(content: string): {
39
- uuid: string;
40
- startLine: number;
41
- endLine: number;
42
- }[];
43
- /**
44
- * Get the changed blocks between two versions of text.
45
- *
46
- * @param oldText The original text.
47
- * @param newText The modified text.
48
- *
49
- * @returns An array of objects representing the changed blocks.
50
- */
51
- export declare function getChangedBlocks(oldText: string, newText: string): {
52
- uuid: string;
53
- startLine: number;
54
- endLine: number;
55
- }[];
56
- /**
57
- * Defines the "crawl" command for the CLI.
58
- *
59
- * @returns The configured Command instance.
60
- */
61
- export default function (): Command;
62
- //# sourceMappingURL=crawl.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"crawl.d.ts","sourceRoot":"","sources":["../src/crawl.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC;;;;GAIG;AACH,wBAAgB,YAAY,IAAI;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAsBnF;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,kBAAkB,CAAC,EACvC,OAAO,EACP,KAAK,EACL,IAAI,EACJ,IAAI,EACJ,GAAG,GACJ,EAAE;IACD,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb,GAAG,OAAO,CAAC,MAAM,CAAC,CA0BlB;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG;IACjD,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB,EAAE,CA0BF;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACd;IACD,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB,EAAE,CAkBF;AACD;;;;GAIG;AACH,MAAM,CAAC,OAAO,cAAc,OAAO,CAuDlC"}
package/dist/crawl.js DELETED
@@ -1,172 +0,0 @@
1
- // SPDX-License-Identifier: Apache-2.0
2
- // SPDX-FileCopyrightText: 2025-Present The Lula2 Authors
3
- import fs from "fs";
4
- import { Octokit } from "@octokit/rest";
5
- import { Command } from "commander";
6
- import { createHash } from "crypto";
7
- /**
8
- * Get the pull request context from the environment or GitHub event payload.
9
- *
10
- * @returns The pull request context containing the owner, repo, and pull number.
11
- */
12
- export function getPRContext() {
13
- const fallbackOwner = process.env.OWNER;
14
- const fallbackRepo = process.env.REPO;
15
- const fallbackNumber = process.env.PULL_NUMBER;
16
- if (process.env.GITHUB_EVENT_PATH && process.env.GITHUB_REPOSITORY) {
17
- const event = JSON.parse(fs.readFileSync(process.env.GITHUB_EVENT_PATH, "utf8"));
18
- const [owner, repo] = process.env.GITHUB_REPOSITORY.split("/");
19
- const pull_number = event.pull_request?.number;
20
- if (!pull_number)
21
- throw new Error("PR number not found in GitHub event payload.");
22
- return { owner, repo, pull_number };
23
- }
24
- if (!fallbackOwner || !fallbackRepo || !fallbackNumber) {
25
- throw new Error("Set OWNER, REPO, and PULL_NUMBER in the environment for local use.");
26
- }
27
- return {
28
- owner: fallbackOwner,
29
- repo: fallbackRepo,
30
- pull_number: parseInt(fallbackNumber, 10),
31
- };
32
- }
33
- /**
34
- * Fetch a raw file from a GitHub repository via the GitHub API.
35
- *
36
- * @param params - The parameters.
37
- * @param params.octokit - An authenticated Octokit instance.
38
- * @param params.owner - The owner of the repository.
39
- * @param params.repo - The name of the repository.
40
- * @param params.path - The path to the file in the repository.
41
- * @param params.ref - The git reference (branch, tag, or commit SHA).
42
- * @returns The content of the file as a string.
43
- */
44
- export async function fetchRawFileViaAPI({ octokit, owner, repo, path, ref, }) {
45
- const res = await octokit.repos.getContent({
46
- owner,
47
- repo,
48
- path,
49
- ref,
50
- headers: {
51
- accept: "application/vnd.github.v3.raw",
52
- },
53
- });
54
- if (typeof res.data === "string") {
55
- return res.data;
56
- }
57
- if (typeof res.data === "object" &&
58
- res.data !== null &&
59
- "content" in res.data &&
60
- typeof res.data.content === "string") {
61
- const { content } = res.data;
62
- return Buffer.from(content, "base64").toString("utf-8");
63
- }
64
- throw new Error("Unexpected GitHub API response shape");
65
- }
66
- /**
67
- * Extracts all @lulaStart and @lulaEnd blocks from the given content.
68
- *
69
- * @param content - The content to extract blocks from.
70
- *
71
- * @returns An array of objects containing the UUID, start line, and end line of each block.
72
- */
73
- export function extractMapBlocks(content) {
74
- const lines = content.split("\n");
75
- const blocks = [];
76
- const stack = [];
77
- lines.forEach((line, idx) => {
78
- const start = line.match(/@lulaStart\s+([a-f0-9-]+)/);
79
- const end = line.match(/@lulaEnd\s+([a-f0-9-]+)/);
80
- if (start) {
81
- stack.push({ uuid: start[1], line: idx });
82
- }
83
- else if (end) {
84
- const last = stack.find(s => s.uuid === end[1]);
85
- if (last) {
86
- blocks.push({ uuid: last.uuid, startLine: last.line, endLine: idx + 1 });
87
- stack.splice(stack.indexOf(last), 1);
88
- }
89
- }
90
- });
91
- return blocks;
92
- }
93
- /**
94
- * Get the changed blocks between two versions of text.
95
- *
96
- * @param oldText The original text.
97
- * @param newText The modified text.
98
- *
99
- * @returns An array of objects representing the changed blocks.
100
- */
101
- export function getChangedBlocks(oldText, newText) {
102
- const oldBlocks = extractMapBlocks(oldText);
103
- const newBlocks = extractMapBlocks(newText);
104
- const changed = [];
105
- for (const newBlock of newBlocks) {
106
- const oldMatch = oldBlocks.find(b => b.uuid === newBlock.uuid);
107
- if (!oldMatch)
108
- continue;
109
- const oldSegment = oldText.split("\n").slice(oldMatch.startLine, oldMatch.endLine).join("\n");
110
- const newSegment = newText.split("\n").slice(newBlock.startLine, newBlock.endLine).join("\n");
111
- if (oldSegment !== newSegment) {
112
- changed.push(newBlock);
113
- }
114
- }
115
- return changed;
116
- }
117
- /**
118
- * Defines the "crawl" command for the CLI.
119
- *
120
- * @returns The configured Command instance.
121
- */
122
- export default function () {
123
- return new Command()
124
- .command("crawl")
125
- .description("Detect compliance-related changes between @lulaStart and @lulaEnd in PR files")
126
- .action(async () => {
127
- const { owner, repo, pull_number } = getPRContext();
128
- const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
129
- const pr = await octokit.pulls.get({ owner, repo, pull_number });
130
- const prBranch = pr.data.head.ref;
131
- const { data: files } = await octokit.pulls.listFiles({ owner, repo, pull_number });
132
- for (const file of files) {
133
- if (file.status === "added")
134
- continue;
135
- try {
136
- const [oldText, newText] = await Promise.all([
137
- fetchRawFileViaAPI({ octokit, owner, repo, path: file.filename, ref: "main" }),
138
- fetchRawFileViaAPI({ octokit, owner, repo, path: file.filename, ref: prBranch }),
139
- ]);
140
- const changedBlocks = getChangedBlocks(oldText, newText);
141
- for (const block of changedBlocks) {
142
- const newBlockText = newText
143
- .split("\n")
144
- .slice(block.startLine, block.endLine)
145
- .join("\n");
146
- const blockSha256 = createHash("sha256").update(newBlockText).digest("hex");
147
- const commentBody = `**Compliance Alert**:\`${file.filename}\` changed between lines ${block.startLine + 1}–${block.endLine}.` +
148
- `\nUUID \`${block.uuid}\` may be out of compliance.` +
149
- `\nSHA-256 of block contents: \`${blockSha256}\`.` +
150
- `\n\n Please review the changes to ensure they meet compliance standards.`;
151
- console.log(`Commenting on ${file.filename}: ${commentBody}`);
152
- await octokit.issues.createComment({
153
- owner,
154
- repo,
155
- issue_number: pull_number,
156
- body: commentBody,
157
- });
158
- // await octokit.pulls.createReview({
159
- // owner,
160
- // repo,
161
- // pull_number,
162
- // body: commentBody,
163
- // event: "REQUEST_CHANGES",
164
- // })
165
- }
166
- }
167
- catch (err) {
168
- console.error(`Error processing ${file.filename}: ${err}`);
169
- }
170
- }
171
- });
172
- }
package/dist/index.d.ts DELETED
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Get the current version from package.json
4
- *
5
- * @returns The current version
6
- */
7
- export declare function getVersion(): any;
8
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAYA;;;;GAIG;AACH,wBAAgB,UAAU,QAKzB"}
@@ -1,19 +0,0 @@
1
- import { registerControls } from "compliance-reporter";
2
-
3
- export const controls = registerControls({
4
- "123e4567-e89b-12d3-a456-426614174001": {
5
- id: "AC-1",
6
- description: "Restrict Pods running as root",
7
- remarks: "This control ensures that no Pods are allowed to run as the root user.",
8
- },
9
- "123e4567-e89b-12d3-a456-426614174002": {
10
- id: "AC-2",
11
- description: "Restrict Pods running privileged containers",
12
- remarks: "This control ensures that no Pods are allowed to run as privileged containers.",
13
- },
14
- "123e4567-e89b-12d3-a456-426614174003": {
15
- id: "AC-3",
16
- description: "Restrict Pods running as root",
17
- remarks: "This control ensures that no Pods are allowed to run as the root user.",
18
- },
19
- });
package/src/crawl.ts DELETED
@@ -1,227 +0,0 @@
1
- // SPDX-License-Identifier: Apache-2.0
2
- // SPDX-FileCopyrightText: 2025-Present The Lula2 Authors
3
-
4
- import fs from "fs";
5
- import { Octokit } from "@octokit/rest";
6
- import { Command } from "commander";
7
- import { createHash } from "crypto";
8
-
9
- type FileContentResponse = {
10
- content: string;
11
- encoding: "base64" | string;
12
- };
13
-
14
- /**
15
- * Get the pull request context from the environment or GitHub event payload.
16
- *
17
- * @returns The pull request context containing the owner, repo, and pull number.
18
- */
19
- export function getPRContext(): { owner: string; repo: string; pull_number: number } {
20
- const fallbackOwner = process.env.OWNER;
21
- const fallbackRepo = process.env.REPO;
22
- const fallbackNumber = process.env.PULL_NUMBER;
23
-
24
- if (process.env.GITHUB_EVENT_PATH && process.env.GITHUB_REPOSITORY) {
25
- const event = JSON.parse(fs.readFileSync(process.env.GITHUB_EVENT_PATH, "utf8"));
26
- const [owner, repo] = process.env.GITHUB_REPOSITORY.split("/");
27
- const pull_number = event.pull_request?.number;
28
- if (!pull_number) throw new Error("PR number not found in GitHub event payload.");
29
- return { owner, repo, pull_number };
30
- }
31
-
32
- if (!fallbackOwner || !fallbackRepo || !fallbackNumber) {
33
- throw new Error("Set OWNER, REPO, and PULL_NUMBER in the environment for local use.");
34
- }
35
-
36
- return {
37
- owner: fallbackOwner,
38
- repo: fallbackRepo,
39
- pull_number: parseInt(fallbackNumber, 10),
40
- };
41
- }
42
-
43
- /**
44
- * Fetch a raw file from a GitHub repository via the GitHub API.
45
- *
46
- * @param params - The parameters.
47
- * @param params.octokit - An authenticated Octokit instance.
48
- * @param params.owner - The owner of the repository.
49
- * @param params.repo - The name of the repository.
50
- * @param params.path - The path to the file in the repository.
51
- * @param params.ref - The git reference (branch, tag, or commit SHA).
52
- * @returns The content of the file as a string.
53
- */
54
- export async function fetchRawFileViaAPI({
55
- octokit,
56
- owner,
57
- repo,
58
- path,
59
- ref,
60
- }: {
61
- octokit: Octokit;
62
- owner: string;
63
- repo: string;
64
- path: string;
65
- ref: string;
66
- }): Promise<string> {
67
- const res = await octokit.repos.getContent({
68
- owner,
69
- repo,
70
- path,
71
- ref,
72
- headers: {
73
- accept: "application/vnd.github.v3.raw",
74
- },
75
- });
76
-
77
- if (typeof res.data === "string") {
78
- return res.data;
79
- }
80
-
81
- if (
82
- typeof res.data === "object" &&
83
- res.data !== null &&
84
- "content" in res.data &&
85
- typeof (res.data as { content: unknown }).content === "string"
86
- ) {
87
- const { content } = res.data as FileContentResponse;
88
- return Buffer.from(content, "base64").toString("utf-8");
89
- }
90
-
91
- throw new Error("Unexpected GitHub API response shape");
92
- }
93
-
94
- /**
95
- * Extracts all @lulaStart and @lulaEnd blocks from the given content.
96
- *
97
- * @param content - The content to extract blocks from.
98
- *
99
- * @returns An array of objects containing the UUID, start line, and end line of each block.
100
- */
101
- export function extractMapBlocks(content: string): {
102
- uuid: string;
103
- startLine: number;
104
- endLine: number;
105
- }[] {
106
- const lines = content.split("\n");
107
- interface MapBlock {
108
- uuid: string;
109
- startLine: number;
110
- endLine: number;
111
- }
112
- const blocks: MapBlock[] = [];
113
- const stack: { uuid: string; line: number }[] = [];
114
-
115
- lines.forEach((line, idx) => {
116
- const start = line.match(/@lulaStart\s+([a-f0-9-]+)/);
117
- const end = line.match(/@lulaEnd\s+([a-f0-9-]+)/);
118
-
119
- if (start) {
120
- stack.push({ uuid: start[1], line: idx });
121
- } else if (end) {
122
- const last = stack.find(s => s.uuid === end[1]);
123
- if (last) {
124
- blocks.push({ uuid: last.uuid, startLine: last.line, endLine: idx + 1 });
125
- stack.splice(stack.indexOf(last), 1);
126
- }
127
- }
128
- });
129
-
130
- return blocks;
131
- }
132
-
133
- /**
134
- * Get the changed blocks between two versions of text.
135
- *
136
- * @param oldText The original text.
137
- * @param newText The modified text.
138
- *
139
- * @returns An array of objects representing the changed blocks.
140
- */
141
- export function getChangedBlocks(
142
- oldText: string,
143
- newText: string,
144
- ): {
145
- uuid: string;
146
- startLine: number;
147
- endLine: number;
148
- }[] {
149
- const oldBlocks = extractMapBlocks(oldText);
150
- const newBlocks = extractMapBlocks(newText);
151
- const changed = [];
152
-
153
- for (const newBlock of newBlocks) {
154
- const oldMatch = oldBlocks.find(b => b.uuid === newBlock.uuid);
155
- if (!oldMatch) continue;
156
-
157
- const oldSegment = oldText.split("\n").slice(oldMatch.startLine, oldMatch.endLine).join("\n");
158
- const newSegment = newText.split("\n").slice(newBlock.startLine, newBlock.endLine).join("\n");
159
-
160
- if (oldSegment !== newSegment) {
161
- changed.push(newBlock);
162
- }
163
- }
164
-
165
- return changed;
166
- }
167
- /**
168
- * Defines the "crawl" command for the CLI.
169
- *
170
- * @returns The configured Command instance.
171
- */
172
- export default function (): Command {
173
- return new Command()
174
- .command("crawl")
175
- .description("Detect compliance-related changes between @lulaStart and @lulaEnd in PR files")
176
- .action(async () => {
177
- const { owner, repo, pull_number } = getPRContext();
178
- const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
179
-
180
- const pr = await octokit.pulls.get({ owner, repo, pull_number });
181
- const prBranch = pr.data.head.ref;
182
-
183
- const { data: files } = await octokit.pulls.listFiles({ owner, repo, pull_number });
184
-
185
- for (const file of files) {
186
- if (file.status === "added") continue;
187
- try {
188
- const [oldText, newText] = await Promise.all([
189
- fetchRawFileViaAPI({ octokit, owner, repo, path: file.filename, ref: "main" }),
190
- fetchRawFileViaAPI({ octokit, owner, repo, path: file.filename, ref: prBranch }),
191
- ]);
192
-
193
- const changedBlocks = getChangedBlocks(oldText, newText);
194
-
195
- for (const block of changedBlocks) {
196
- const newBlockText = newText
197
- .split("\n")
198
- .slice(block.startLine, block.endLine)
199
- .join("\n");
200
-
201
- const blockSha256 = createHash("sha256").update(newBlockText).digest("hex");
202
- const commentBody =
203
- `**Compliance Alert**:\`${file.filename}\` changed between lines ${block.startLine + 1}–${block.endLine}.` +
204
- `\nUUID \`${block.uuid}\` may be out of compliance.` +
205
- `\nSHA-256 of block contents: \`${blockSha256}\`.` +
206
- `\n\n Please review the changes to ensure they meet compliance standards.`;
207
- console.log(`Commenting on ${file.filename}: ${commentBody}`);
208
- await octokit.issues.createComment({
209
- owner,
210
- repo,
211
- issue_number: pull_number,
212
- body: commentBody,
213
- });
214
- // await octokit.pulls.createReview({
215
- // owner,
216
- // repo,
217
- // pull_number,
218
- // body: commentBody,
219
- // event: "REQUEST_CHANGES",
220
- // })
221
- }
222
- } catch (err) {
223
- console.error(`Error processing ${file.filename}: ${err}`);
224
- }
225
- }
226
- });
227
- }
package/src/index.ts DELETED
@@ -1,46 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { Command } from "commander";
4
- import crawl from "./crawl.js";
5
- import fs from "fs";
6
- import path from "path";
7
-
8
- const program = new Command();
9
- import { fileURLToPath } from "url";
10
-
11
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
12
-
13
- /**
14
- * Get the current version from package.json
15
- *
16
- * @returns The current version
17
- */
18
- export function getVersion() {
19
- const pkgPath = path.resolve(__dirname, "../package.json"); // adjust path if index.ts is in src/
20
- const packageJson = fs.readFileSync(pkgPath, "utf8");
21
- const { version } = JSON.parse(packageJson);
22
- return version;
23
- }
24
-
25
- program
26
- .name("lula2")
27
- .description("Reports and exports compliance status for defined controls")
28
- .version(getVersion(), "-v, --version", "output the current version")
29
- .option("-c, --config <path>", "path to config file", "compliance.json")
30
- .addCommand(crawl())
31
- .action(options => {
32
- console.log("Checking compliance status...");
33
- if (options.config) {
34
- console.log(`Using config file: ${options.config}`);
35
- } else {
36
- console.log("Using default configuration");
37
- }
38
- console.log("Compliance check completed!");
39
- });
40
-
41
- program.parse(process.argv);
42
-
43
- // If no command is provided, show help
44
- if (!process.argv.slice(1).length) {
45
- program.help();
46
- }