lula2 0.2.1 → 0.3.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.
@@ -380,7 +380,10 @@ var init_fileStore = __esm({
380
380
  const controlId = mapping.control_id;
381
381
  const family = this.getControlFamily(controlId);
382
382
  const familyDir = join2(this.mappingsDir, family);
383
- const mappingFile = join2(familyDir, `${controlId}-mappings.yaml`);
383
+ const mappingFile = join2(
384
+ familyDir,
385
+ `${controlId.replace(/[^a-zA-Z0-9-]/g, "_")}-mappings.yaml`
386
+ );
384
387
  if (!existsSync2(familyDir)) {
385
388
  mkdirSync(familyDir, { recursive: true });
386
389
  }
@@ -478,7 +481,10 @@ var init_fileStore = __esm({
478
481
  for (const [controlId, controlMappings] of mappingsByControl) {
479
482
  const family = this.getControlFamily(controlId);
480
483
  const familyDir = join2(this.mappingsDir, family);
481
- const mappingFile = join2(familyDir, `${controlId}-mappings.yaml`);
484
+ const mappingFile = join2(
485
+ familyDir,
486
+ `${controlId.replace(/[^a-zA-Z0-9-]/g, "_")}-mappings.yaml`
487
+ );
482
488
  if (!existsSync2(familyDir)) {
483
489
  mkdirSync(familyDir, { recursive: true });
484
490
  }
@@ -1776,7 +1782,9 @@ var init_spreadsheetRoutes = __esm({
1776
1782
  uiType = "long_text";
1777
1783
  }
1778
1784
  let category = frontendConfig?.category || "custom";
1779
- if (!frontendConfig) {
1785
+ if (justificationFields.includes(fieldName)) {
1786
+ category = "mappings";
1787
+ } else if (!frontendConfig) {
1780
1788
  if (fieldName.includes("status") || fieldName.includes("state")) {
1781
1789
  category = "compliance";
1782
1790
  } else if (fieldName.includes("title") || fieldName.includes("name") || fieldName.includes("description")) {
@@ -1804,8 +1812,8 @@ var init_spreadsheetRoutes = __esm({
1804
1812
  // Control ID is always first
1805
1813
  category: isControlIdField ? "core" : category,
1806
1814
  // Control ID is always core
1807
- tab: isControlIdField ? "overview" : frontendConfig?.tab || void 0
1808
- // Control ID is always in overview
1815
+ tab: isControlIdField ? "overview" : justificationFields.includes(fieldName) ? "mappings" : frontendConfig?.tab || void 0
1816
+ // Use frontend config or default
1809
1817
  };
1810
1818
  if (uiType === "select") {
1811
1819
  fieldDef.options = Array.from(metadata.uniqueValues).sort();
@@ -1868,7 +1876,7 @@ var init_spreadsheetRoutes = __esm({
1868
1876
  if (fieldName === "family") return;
1869
1877
  if (justificationFields.includes(fieldName) && control[fieldName] !== void 0 && control[fieldName] !== null) {
1870
1878
  justificationContents.push(control[fieldName]);
1871
- return;
1879
+ filteredControl[fieldName] = control[fieldName];
1872
1880
  }
1873
1881
  const isInFrontendSchema = frontendFieldSchema?.some((f) => f.fieldName === fieldName);
1874
1882
  const isInFieldsMetadata = fields.hasOwnProperty(fieldName);
@@ -2266,7 +2274,7 @@ var WebSocketManager = class {
2266
2274
  totalCommits: controlHistory.totalCommits,
2267
2275
  commits: controlHistory.commits?.length || 0
2268
2276
  });
2269
- const mappingFilename = `${control.id}-mappings.yaml`;
2277
+ const mappingFilename = `${control.id.replace(/[^a-zA-Z0-9-]/g, "_")}-mappings.yaml`;
2270
2278
  const mappingPath = join5(currentPath2, "mappings", family, mappingFilename);
2271
2279
  let mappingHistory = { commits: [], totalCommits: 0 };
2272
2280
  if (existsSync4(mappingPath)) {
package/dist/index.html CHANGED
@@ -6,10 +6,10 @@
6
6
  <link rel="icon" href="/lula.png" />
7
7
  <meta name="viewport" content="width=device-width, initial-scale=1" />
8
8
 
9
- <link rel="modulepreload" href="/_app/immutable/entry/start.8aoHS084.js">
10
- <link rel="modulepreload" href="/_app/immutable/chunks/ByYV7ZA-.js">
9
+ <link rel="modulepreload" href="/_app/immutable/entry/start.68S9ad6U.js">
10
+ <link rel="modulepreload" href="/_app/immutable/chunks/Ds14DLx0.js">
11
11
  <link rel="modulepreload" href="/_app/immutable/chunks/Cby0Z7eP.js">
12
- <link rel="modulepreload" href="/_app/immutable/entry/app.fwEu_ZMM.js">
12
+ <link rel="modulepreload" href="/_app/immutable/entry/app.XLGRlmCF.js">
13
13
  <link rel="modulepreload" href="/_app/immutable/chunks/DsnmJJEf.js">
14
14
  <link rel="modulepreload" href="/_app/immutable/chunks/DqsOU3kV.js">
15
15
  <link rel="modulepreload" href="/_app/immutable/chunks/CoF2vljD.js">
@@ -19,15 +19,15 @@
19
19
  <div style="display: contents">
20
20
  <script>
21
21
  {
22
- __sveltekit_fjsoc9 = {
22
+ __sveltekit_6f9em3 = {
23
23
  base: ""
24
24
  };
25
25
 
26
26
  const element = document.currentScript.parentElement;
27
27
 
28
28
  Promise.all([
29
- import("/_app/immutable/entry/start.8aoHS084.js"),
30
- import("/_app/immutable/entry/app.fwEu_ZMM.js")
29
+ import("/_app/immutable/entry/start.68S9ad6U.js"),
30
+ import("/_app/immutable/entry/app.XLGRlmCF.js")
31
31
  ]).then(([kit, app]) => {
32
32
  kit.start(app, element);
33
33
  });
package/dist/index.js CHANGED
@@ -1952,7 +1952,10 @@ var init_fileStore = __esm({
1952
1952
  const controlId = mapping.control_id;
1953
1953
  const family = this.getControlFamily(controlId);
1954
1954
  const familyDir = join2(this.mappingsDir, family);
1955
- const mappingFile = join2(familyDir, `${controlId}-mappings.yaml`);
1955
+ const mappingFile = join2(
1956
+ familyDir,
1957
+ `${controlId.replace(/[^a-zA-Z0-9-]/g, "_")}-mappings.yaml`
1958
+ );
1956
1959
  if (!existsSync2(familyDir)) {
1957
1960
  mkdirSync(familyDir, { recursive: true });
1958
1961
  }
@@ -2050,7 +2053,10 @@ var init_fileStore = __esm({
2050
2053
  for (const [controlId, controlMappings] of mappingsByControl) {
2051
2054
  const family = this.getControlFamily(controlId);
2052
2055
  const familyDir = join2(this.mappingsDir, family);
2053
- const mappingFile = join2(familyDir, `${controlId}-mappings.yaml`);
2056
+ const mappingFile = join2(
2057
+ familyDir,
2058
+ `${controlId.replace(/[^a-zA-Z0-9-]/g, "_")}-mappings.yaml`
2059
+ );
2054
2060
  if (!existsSync2(familyDir)) {
2055
2061
  mkdirSync(familyDir, { recursive: true });
2056
2062
  }
@@ -3348,7 +3354,9 @@ var init_spreadsheetRoutes = __esm({
3348
3354
  uiType = "long_text";
3349
3355
  }
3350
3356
  let category = frontendConfig?.category || "custom";
3351
- if (!frontendConfig) {
3357
+ if (justificationFields.includes(fieldName)) {
3358
+ category = "mappings";
3359
+ } else if (!frontendConfig) {
3352
3360
  if (fieldName.includes("status") || fieldName.includes("state")) {
3353
3361
  category = "compliance";
3354
3362
  } else if (fieldName.includes("title") || fieldName.includes("name") || fieldName.includes("description")) {
@@ -3376,8 +3384,8 @@ var init_spreadsheetRoutes = __esm({
3376
3384
  // Control ID is always first
3377
3385
  category: isControlIdField ? "core" : category,
3378
3386
  // Control ID is always core
3379
- tab: isControlIdField ? "overview" : frontendConfig?.tab || void 0
3380
- // Control ID is always in overview
3387
+ tab: isControlIdField ? "overview" : justificationFields.includes(fieldName) ? "mappings" : frontendConfig?.tab || void 0
3388
+ // Use frontend config or default
3381
3389
  };
3382
3390
  if (uiType === "select") {
3383
3391
  fieldDef.options = Array.from(metadata.uniqueValues).sort();
@@ -3440,7 +3448,7 @@ var init_spreadsheetRoutes = __esm({
3440
3448
  if (fieldName === "family") return;
3441
3449
  if (justificationFields.includes(fieldName) && control[fieldName] !== void 0 && control[fieldName] !== null) {
3442
3450
  justificationContents.push(control[fieldName]);
3443
- return;
3451
+ filteredControl[fieldName] = control[fieldName];
3444
3452
  }
3445
3453
  const isInFrontendSchema = frontendFieldSchema?.some((f) => f.fieldName === fieldName);
3446
3454
  const isInFieldsMetadata = fields.hasOwnProperty(fieldName);
@@ -4713,7 +4721,7 @@ var WebSocketManager = class {
4713
4721
  totalCommits: controlHistory.totalCommits,
4714
4722
  commits: controlHistory.commits?.length || 0
4715
4723
  });
4716
- const mappingFilename = `${control.id}-mappings.yaml`;
4724
+ const mappingFilename = `${control.id.replace(/[^a-zA-Z0-9-]/g, "_")}-mappings.yaml`;
4717
4725
  const mappingPath = join5(currentPath2, "mappings", family, mappingFilename);
4718
4726
  let mappingHistory = { commits: [], totalCommits: 0 };
4719
4727
  if (existsSync6(mappingPath)) {
@@ -5246,6 +5254,7 @@ var closingBody = `
5246
5254
  ---
5247
5255
 
5248
5256
  <sub>**Tip:** Customize your compliance reviews with <a href="https://github.com/defenseunicorns/lula.git" class="Link--inTextBlock" target="_blank" rel="noopener noreferrer">Lula</a>.</sub>`;
5257
+ var LULA_SIGNATURE = "<!-- LULA_SIGNATURE:v1 -->";
5249
5258
  async function postFinding(params) {
5250
5259
  const { octokit, postMode, owner, repo, pull_number, body } = params;
5251
5260
  if (postMode === "comment") {
@@ -5355,7 +5364,8 @@ function crawlCommand() {
5355
5364
  const pr = await octokit.pulls.get({ owner, repo, pull_number });
5356
5365
  const prBranch = pr.data.head.ref;
5357
5366
  const { data: files } = await octokit.pulls.listFiles({ owner, repo, pull_number });
5358
- let commentBody = `## Lula Compliance Overview
5367
+ let commentBody = `${LULA_SIGNATURE}
5368
+ ## Lula Compliance Overview
5359
5369
 
5360
5370
  Please review the changes to ensure they meet compliance standards.
5361
5371
 
@@ -5366,7 +5376,6 @@ Lula reviewed ${files.length} files changed that affect compliance.
5366
5376
  `;
5367
5377
  for (const file of files) {
5368
5378
  if (file.status === "added") continue;
5369
- console.log(`Commenting regarding \`${file.filename}\`.`);
5370
5379
  try {
5371
5380
  const [oldText, newText] = await Promise.all([
5372
5381
  fetchRawFileViaAPI({ octokit, owner, repo, path: file.filename, ref: "main" }),
@@ -5374,6 +5383,7 @@ Lula reviewed ${files.length} files changed that affect compliance.
5374
5383
  ]);
5375
5384
  const changedBlocks = getChangedBlocks(oldText, newText);
5376
5385
  for (const block of changedBlocks) {
5386
+ console.log(`Commenting regarding \`${file.filename}\`.`);
5377
5387
  leavePost = true;
5378
5388
  commentBody += `
5379
5389
 
@@ -5393,6 +5403,12 @@ Lula reviewed ${files.length} files changed that affect compliance.
5393
5403
  console.error(`Error processing ${file.filename}: ${err}`);
5394
5404
  }
5395
5405
  }
5406
+ if (opts.postMode === "comment") {
5407
+ await deleteOldIssueComments({ octokit, owner, repo, pull_number });
5408
+ } else {
5409
+ await dismissOldReviews({ octokit, owner, repo, pull_number });
5410
+ await deleteOldReviewComments({ octokit, owner, repo, pull_number });
5411
+ }
5396
5412
  if (leavePost) {
5397
5413
  await postFinding({
5398
5414
  octokit,
@@ -5414,6 +5430,90 @@ ${commentBody + closingBody}
5414
5430
  }
5415
5431
  });
5416
5432
  }
5433
+ async function deleteOldIssueComments({
5434
+ octokit,
5435
+ owner,
5436
+ repo,
5437
+ pull_number
5438
+ }) {
5439
+ let page = 1;
5440
+ while (true) {
5441
+ const { data: comments } = await octokit.issues.listComments({
5442
+ owner,
5443
+ repo,
5444
+ issue_number: pull_number,
5445
+ per_page: 100,
5446
+ page
5447
+ });
5448
+ if (!comments.length) break;
5449
+ for (const c of comments) {
5450
+ const hasSignature = (c.body ?? "").includes(LULA_SIGNATURE);
5451
+ if (hasSignature) {
5452
+ await octokit.issues.deleteComment({ owner, repo, comment_id: c.id });
5453
+ break;
5454
+ }
5455
+ }
5456
+ page++;
5457
+ }
5458
+ }
5459
+ async function deleteOldReviewComments({
5460
+ octokit,
5461
+ owner,
5462
+ repo,
5463
+ pull_number
5464
+ }) {
5465
+ let page = 1;
5466
+ while (true) {
5467
+ const { data: reviewComments } = await octokit.pulls.listReviewComments({
5468
+ owner,
5469
+ repo,
5470
+ pull_number,
5471
+ per_page: 100,
5472
+ page
5473
+ });
5474
+ if (!reviewComments.length) break;
5475
+ for (const rc of reviewComments) {
5476
+ const hasSignature = (rc.body ?? "").includes(LULA_SIGNATURE);
5477
+ if (hasSignature) {
5478
+ await octokit.pulls.deleteReviewComment({ owner, repo, comment_id: rc.id });
5479
+ break;
5480
+ }
5481
+ }
5482
+ page++;
5483
+ }
5484
+ }
5485
+ async function dismissOldReviews({
5486
+ octokit,
5487
+ owner,
5488
+ repo,
5489
+ pull_number
5490
+ }) {
5491
+ let page = 1;
5492
+ while (true) {
5493
+ const { data: reviews } = await octokit.pulls.listReviews({
5494
+ owner,
5495
+ repo,
5496
+ pull_number,
5497
+ per_page: 100,
5498
+ page
5499
+ });
5500
+ if (!reviews.length) break;
5501
+ for (const r of reviews) {
5502
+ const hasSignature = (r.body ?? "").includes(LULA_SIGNATURE);
5503
+ if (hasSignature) {
5504
+ await octokit.pulls.dismissReview({
5505
+ owner,
5506
+ repo,
5507
+ pull_number,
5508
+ review_id: r.id,
5509
+ message: "Superseded by a new Lula compliance review."
5510
+ });
5511
+ break;
5512
+ }
5513
+ }
5514
+ page++;
5515
+ }
5516
+ }
5417
5517
 
5418
5518
  // index.ts
5419
5519
  var program = new Command2();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lula2",
3
- "version": "0.2.1",
3
+ "version": "0.3.1",
4
4
  "description": "A tool for managing compliance as code in your GitHub repositories.",
5
5
  "bin": {
6
6
  "lula2": "./dist/lula2"
@@ -1 +0,0 @@
1
- import{l as o,a as r}from"../chunks/ByYV7ZA-.js";export{o as load_css,r as start};