lula2 0.1.1-nightly.0 → 0.2.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.
- package/dist/_app/immutable/assets/0.Dv98laBw.css +1 -0
- package/dist/_app/immutable/chunks/{DSi9HfzB.js → CKNf7CSU.js} +1 -1
- package/dist/_app/immutable/chunks/{D4wsprzy.js → Ca3ZVyCu.js} +1 -1
- package/dist/_app/immutable/chunks/Dacgmabg.js +3 -0
- package/dist/_app/immutable/entry/{app.pY_jLQAk.js → app.lS9kJbUY.js} +2 -2
- package/dist/_app/immutable/entry/start.z83Vr3Ay.js +1 -0
- package/dist/_app/immutable/nodes/{0.BLkz8enN.js → 0.DNlUjA6t.js} +1 -1
- package/dist/_app/immutable/nodes/{1.CfOyMeub.js → 1.C8f84F9P.js} +1 -1
- package/dist/_app/immutable/nodes/{2.DOcHSA9J.js → 2.NGPmbiD7.js} +1 -1
- package/dist/_app/immutable/nodes/{3.D2EgveDf.js → 3.BJr8tGL-.js} +1 -1
- package/dist/_app/immutable/nodes/4.37t6YhN2.js +12 -0
- package/dist/_app/version.json +1 -1
- package/dist/cli/commands/ui.js +37 -2
- package/dist/cli/server/index.js +37 -2
- package/dist/cli/server/server.js +37 -2
- package/dist/cli/server/spreadsheetRoutes.js +35 -0
- package/dist/cli/server/websocketServer.js +37 -2
- package/dist/index.html +6 -6
- package/dist/index.js +37 -2
- package/package.json +21 -20
- package/src/lib/components/setup/SpreadsheetImport.svelte +105 -2
- package/dist/_app/immutable/assets/0.BjwH76RW.css +0 -1
- package/dist/_app/immutable/chunks/D3AAXov_.js +0 -3
- package/dist/_app/immutable/entry/start.Cdw5p6kF.js +0 -1
- package/dist/_app/immutable/nodes/4.Dg1K2qE0.js +0 -9
|
@@ -2810,6 +2810,7 @@ __export(spreadsheetRoutes_exports, {
|
|
|
2810
2810
|
default: () => spreadsheetRoutes_default,
|
|
2811
2811
|
scanControlSets: () => scanControlSets
|
|
2812
2812
|
});
|
|
2813
|
+
import crypto from "crypto";
|
|
2813
2814
|
import { parse as parseCSVSync } from "csv-parse/sync";
|
|
2814
2815
|
import ExcelJS from "exceljs";
|
|
2815
2816
|
import express from "express";
|
|
@@ -3128,6 +3129,15 @@ var init_spreadsheetRoutes = __esm({
|
|
|
3128
3129
|
controlSetName = "Imported Control Set",
|
|
3129
3130
|
controlSetDescription = "Imported from spreadsheet"
|
|
3130
3131
|
} = req.body;
|
|
3132
|
+
let justificationFields = [];
|
|
3133
|
+
if (req.body.justificationFields) {
|
|
3134
|
+
try {
|
|
3135
|
+
justificationFields = JSON.parse(req.body.justificationFields);
|
|
3136
|
+
debug("Justification fields received:", justificationFields);
|
|
3137
|
+
} catch (e) {
|
|
3138
|
+
console.error("Failed to parse justification fields:", e);
|
|
3139
|
+
}
|
|
3140
|
+
}
|
|
3131
3141
|
debug("Import parameters received:", {
|
|
3132
3142
|
controlIdField,
|
|
3133
3143
|
startRow,
|
|
@@ -3375,11 +3385,17 @@ var init_spreadsheetRoutes = __esm({
|
|
|
3375
3385
|
};
|
|
3376
3386
|
writeFileSync2(join4(baseDir, "lula.yaml"), yaml4.dump(controlSetData));
|
|
3377
3387
|
const controlsDir = join4(baseDir, "controls");
|
|
3388
|
+
const mappingsDir = join4(baseDir, "mappings");
|
|
3389
|
+
const justificationFieldNames = justificationFields;
|
|
3378
3390
|
families.forEach((familyControls, family) => {
|
|
3379
3391
|
const familyDir = join4(controlsDir, family);
|
|
3392
|
+
const familyMappingsDir = join4(mappingsDir, family);
|
|
3380
3393
|
if (!existsSync3(familyDir)) {
|
|
3381
3394
|
mkdirSync2(familyDir, { recursive: true });
|
|
3382
3395
|
}
|
|
3396
|
+
if (!existsSync3(familyMappingsDir)) {
|
|
3397
|
+
mkdirSync2(familyMappingsDir, { recursive: true });
|
|
3398
|
+
}
|
|
3383
3399
|
familyControls.forEach((control) => {
|
|
3384
3400
|
const controlId = control[controlIdFieldNameClean];
|
|
3385
3401
|
if (!controlId) {
|
|
@@ -3389,12 +3405,24 @@ var init_spreadsheetRoutes = __esm({
|
|
|
3389
3405
|
const controlIdStr = String(controlId).slice(0, 50);
|
|
3390
3406
|
const fileName2 = `${controlIdStr.replace(/[^a-zA-Z0-9-]/g, "_")}.yaml`;
|
|
3391
3407
|
const filePath = join4(familyDir, fileName2);
|
|
3408
|
+
const mappingFileName = `${controlIdStr.replace(/[^a-zA-Z0-9-]/g, "_")}-mappings.yaml`;
|
|
3409
|
+
const mappingFilePath = join4(familyMappingsDir, mappingFileName);
|
|
3392
3410
|
const filteredControl = {};
|
|
3411
|
+
const mappingData = {
|
|
3412
|
+
control_id: controlIdStr,
|
|
3413
|
+
justification: "",
|
|
3414
|
+
uuid: crypto.randomUUID()
|
|
3415
|
+
};
|
|
3416
|
+
const justificationContents = [];
|
|
3393
3417
|
if (control.family !== void 0) {
|
|
3394
3418
|
filteredControl.family = control.family;
|
|
3395
3419
|
}
|
|
3396
3420
|
Object.keys(control).forEach((fieldName) => {
|
|
3397
3421
|
if (fieldName === "family") return;
|
|
3422
|
+
if (justificationFields.includes(fieldName) && control[fieldName] !== void 0 && control[fieldName] !== null) {
|
|
3423
|
+
justificationContents.push(control[fieldName]);
|
|
3424
|
+
return;
|
|
3425
|
+
}
|
|
3398
3426
|
const isInFrontendSchema = frontendFieldSchema?.some((f) => f.fieldName === fieldName);
|
|
3399
3427
|
const isInFieldsMetadata = fields.hasOwnProperty(fieldName);
|
|
3400
3428
|
if (isInFrontendSchema || isInFieldsMetadata) {
|
|
@@ -3402,6 +3430,13 @@ var init_spreadsheetRoutes = __esm({
|
|
|
3402
3430
|
}
|
|
3403
3431
|
});
|
|
3404
3432
|
writeFileSync2(filePath, yaml4.dump(filteredControl));
|
|
3433
|
+
if (justificationContents.length > 0) {
|
|
3434
|
+
mappingData.justification = justificationContents.join("\n\n");
|
|
3435
|
+
}
|
|
3436
|
+
if (mappingData.justification && mappingData.justification.trim() !== "") {
|
|
3437
|
+
const mappingArray = [mappingData];
|
|
3438
|
+
writeFileSync2(mappingFilePath, yaml4.dump(mappingArray));
|
|
3439
|
+
}
|
|
3405
3440
|
});
|
|
3406
3441
|
});
|
|
3407
3442
|
res.json({
|
|
@@ -4510,8 +4545,8 @@ var WebSocketManager = class {
|
|
|
4510
4545
|
if (payload && payload.control_id) {
|
|
4511
4546
|
const mapping = payload;
|
|
4512
4547
|
if (!mapping.uuid) {
|
|
4513
|
-
const
|
|
4514
|
-
mapping.uuid =
|
|
4548
|
+
const crypto2 = await import("crypto");
|
|
4549
|
+
mapping.uuid = crypto2.randomUUID();
|
|
4515
4550
|
}
|
|
4516
4551
|
await state.fileStore.saveMapping(mapping);
|
|
4517
4552
|
state.mappingsCache.set(mapping.uuid, mapping);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// cli/server/spreadsheetRoutes.ts
|
|
2
|
+
import crypto from "crypto";
|
|
2
3
|
import { parse as parseCSVSync } from "csv-parse/sync";
|
|
3
4
|
import ExcelJS from "exceljs";
|
|
4
5
|
import express from "express";
|
|
@@ -93,6 +94,15 @@ router.post("/import-spreadsheet", upload.single("file"), async (req, res) => {
|
|
|
93
94
|
controlSetName = "Imported Control Set",
|
|
94
95
|
controlSetDescription = "Imported from spreadsheet"
|
|
95
96
|
} = req.body;
|
|
97
|
+
let justificationFields = [];
|
|
98
|
+
if (req.body.justificationFields) {
|
|
99
|
+
try {
|
|
100
|
+
justificationFields = JSON.parse(req.body.justificationFields);
|
|
101
|
+
debug("Justification fields received:", justificationFields);
|
|
102
|
+
} catch (e) {
|
|
103
|
+
console.error("Failed to parse justification fields:", e);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
96
106
|
debug("Import parameters received:", {
|
|
97
107
|
controlIdField,
|
|
98
108
|
startRow,
|
|
@@ -340,11 +350,17 @@ router.post("/import-spreadsheet", upload.single("file"), async (req, res) => {
|
|
|
340
350
|
};
|
|
341
351
|
writeFileSync(join(baseDir, "lula.yaml"), yaml4.dump(controlSetData));
|
|
342
352
|
const controlsDir = join(baseDir, "controls");
|
|
353
|
+
const mappingsDir = join(baseDir, "mappings");
|
|
354
|
+
const justificationFieldNames = justificationFields;
|
|
343
355
|
families.forEach((familyControls, family) => {
|
|
344
356
|
const familyDir = join(controlsDir, family);
|
|
357
|
+
const familyMappingsDir = join(mappingsDir, family);
|
|
345
358
|
if (!existsSync(familyDir)) {
|
|
346
359
|
mkdirSync(familyDir, { recursive: true });
|
|
347
360
|
}
|
|
361
|
+
if (!existsSync(familyMappingsDir)) {
|
|
362
|
+
mkdirSync(familyMappingsDir, { recursive: true });
|
|
363
|
+
}
|
|
348
364
|
familyControls.forEach((control) => {
|
|
349
365
|
const controlId = control[controlIdFieldNameClean];
|
|
350
366
|
if (!controlId) {
|
|
@@ -354,12 +370,24 @@ router.post("/import-spreadsheet", upload.single("file"), async (req, res) => {
|
|
|
354
370
|
const controlIdStr = String(controlId).slice(0, 50);
|
|
355
371
|
const fileName2 = `${controlIdStr.replace(/[^a-zA-Z0-9-]/g, "_")}.yaml`;
|
|
356
372
|
const filePath = join(familyDir, fileName2);
|
|
373
|
+
const mappingFileName = `${controlIdStr.replace(/[^a-zA-Z0-9-]/g, "_")}-mappings.yaml`;
|
|
374
|
+
const mappingFilePath = join(familyMappingsDir, mappingFileName);
|
|
357
375
|
const filteredControl = {};
|
|
376
|
+
const mappingData = {
|
|
377
|
+
control_id: controlIdStr,
|
|
378
|
+
justification: "",
|
|
379
|
+
uuid: crypto.randomUUID()
|
|
380
|
+
};
|
|
381
|
+
const justificationContents = [];
|
|
358
382
|
if (control.family !== void 0) {
|
|
359
383
|
filteredControl.family = control.family;
|
|
360
384
|
}
|
|
361
385
|
Object.keys(control).forEach((fieldName) => {
|
|
362
386
|
if (fieldName === "family") return;
|
|
387
|
+
if (justificationFields.includes(fieldName) && control[fieldName] !== void 0 && control[fieldName] !== null) {
|
|
388
|
+
justificationContents.push(control[fieldName]);
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
363
391
|
const isInFrontendSchema = frontendFieldSchema?.some((f) => f.fieldName === fieldName);
|
|
364
392
|
const isInFieldsMetadata = fields.hasOwnProperty(fieldName);
|
|
365
393
|
if (isInFrontendSchema || isInFieldsMetadata) {
|
|
@@ -367,6 +395,13 @@ router.post("/import-spreadsheet", upload.single("file"), async (req, res) => {
|
|
|
367
395
|
}
|
|
368
396
|
});
|
|
369
397
|
writeFileSync(filePath, yaml4.dump(filteredControl));
|
|
398
|
+
if (justificationContents.length > 0) {
|
|
399
|
+
mappingData.justification = justificationContents.join("\n\n");
|
|
400
|
+
}
|
|
401
|
+
if (mappingData.justification && mappingData.justification.trim() !== "") {
|
|
402
|
+
const mappingArray = [mappingData];
|
|
403
|
+
writeFileSync(mappingFilePath, yaml4.dump(mappingArray));
|
|
404
|
+
}
|
|
370
405
|
});
|
|
371
406
|
});
|
|
372
407
|
res.json({
|
|
@@ -1257,6 +1257,7 @@ __export(spreadsheetRoutes_exports, {
|
|
|
1257
1257
|
default: () => spreadsheetRoutes_default,
|
|
1258
1258
|
scanControlSets: () => scanControlSets
|
|
1259
1259
|
});
|
|
1260
|
+
import crypto from "crypto";
|
|
1260
1261
|
import { parse as parseCSVSync } from "csv-parse/sync";
|
|
1261
1262
|
import ExcelJS from "exceljs";
|
|
1262
1263
|
import express from "express";
|
|
@@ -1575,6 +1576,15 @@ var init_spreadsheetRoutes = __esm({
|
|
|
1575
1576
|
controlSetName = "Imported Control Set",
|
|
1576
1577
|
controlSetDescription = "Imported from spreadsheet"
|
|
1577
1578
|
} = req.body;
|
|
1579
|
+
let justificationFields = [];
|
|
1580
|
+
if (req.body.justificationFields) {
|
|
1581
|
+
try {
|
|
1582
|
+
justificationFields = JSON.parse(req.body.justificationFields);
|
|
1583
|
+
debug("Justification fields received:", justificationFields);
|
|
1584
|
+
} catch (e) {
|
|
1585
|
+
console.error("Failed to parse justification fields:", e);
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1578
1588
|
debug("Import parameters received:", {
|
|
1579
1589
|
controlIdField,
|
|
1580
1590
|
startRow,
|
|
@@ -1822,11 +1832,17 @@ var init_spreadsheetRoutes = __esm({
|
|
|
1822
1832
|
};
|
|
1823
1833
|
writeFileSync2(join4(baseDir, "lula.yaml"), yaml4.dump(controlSetData));
|
|
1824
1834
|
const controlsDir = join4(baseDir, "controls");
|
|
1835
|
+
const mappingsDir = join4(baseDir, "mappings");
|
|
1836
|
+
const justificationFieldNames = justificationFields;
|
|
1825
1837
|
families.forEach((familyControls, family) => {
|
|
1826
1838
|
const familyDir = join4(controlsDir, family);
|
|
1839
|
+
const familyMappingsDir = join4(mappingsDir, family);
|
|
1827
1840
|
if (!existsSync3(familyDir)) {
|
|
1828
1841
|
mkdirSync2(familyDir, { recursive: true });
|
|
1829
1842
|
}
|
|
1843
|
+
if (!existsSync3(familyMappingsDir)) {
|
|
1844
|
+
mkdirSync2(familyMappingsDir, { recursive: true });
|
|
1845
|
+
}
|
|
1830
1846
|
familyControls.forEach((control) => {
|
|
1831
1847
|
const controlId = control[controlIdFieldNameClean];
|
|
1832
1848
|
if (!controlId) {
|
|
@@ -1836,12 +1852,24 @@ var init_spreadsheetRoutes = __esm({
|
|
|
1836
1852
|
const controlIdStr = String(controlId).slice(0, 50);
|
|
1837
1853
|
const fileName2 = `${controlIdStr.replace(/[^a-zA-Z0-9-]/g, "_")}.yaml`;
|
|
1838
1854
|
const filePath = join4(familyDir, fileName2);
|
|
1855
|
+
const mappingFileName = `${controlIdStr.replace(/[^a-zA-Z0-9-]/g, "_")}-mappings.yaml`;
|
|
1856
|
+
const mappingFilePath = join4(familyMappingsDir, mappingFileName);
|
|
1839
1857
|
const filteredControl = {};
|
|
1858
|
+
const mappingData = {
|
|
1859
|
+
control_id: controlIdStr,
|
|
1860
|
+
justification: "",
|
|
1861
|
+
uuid: crypto.randomUUID()
|
|
1862
|
+
};
|
|
1863
|
+
const justificationContents = [];
|
|
1840
1864
|
if (control.family !== void 0) {
|
|
1841
1865
|
filteredControl.family = control.family;
|
|
1842
1866
|
}
|
|
1843
1867
|
Object.keys(control).forEach((fieldName) => {
|
|
1844
1868
|
if (fieldName === "family") return;
|
|
1869
|
+
if (justificationFields.includes(fieldName) && control[fieldName] !== void 0 && control[fieldName] !== null) {
|
|
1870
|
+
justificationContents.push(control[fieldName]);
|
|
1871
|
+
return;
|
|
1872
|
+
}
|
|
1845
1873
|
const isInFrontendSchema = frontendFieldSchema?.some((f) => f.fieldName === fieldName);
|
|
1846
1874
|
const isInFieldsMetadata = fields.hasOwnProperty(fieldName);
|
|
1847
1875
|
if (isInFrontendSchema || isInFieldsMetadata) {
|
|
@@ -1849,6 +1877,13 @@ var init_spreadsheetRoutes = __esm({
|
|
|
1849
1877
|
}
|
|
1850
1878
|
});
|
|
1851
1879
|
writeFileSync2(filePath, yaml4.dump(filteredControl));
|
|
1880
|
+
if (justificationContents.length > 0) {
|
|
1881
|
+
mappingData.justification = justificationContents.join("\n\n");
|
|
1882
|
+
}
|
|
1883
|
+
if (mappingData.justification && mappingData.justification.trim() !== "") {
|
|
1884
|
+
const mappingArray = [mappingData];
|
|
1885
|
+
writeFileSync2(mappingFilePath, yaml4.dump(mappingArray));
|
|
1886
|
+
}
|
|
1852
1887
|
});
|
|
1853
1888
|
});
|
|
1854
1889
|
res.json({
|
|
@@ -2090,8 +2125,8 @@ var WebSocketManager = class {
|
|
|
2090
2125
|
if (payload && payload.control_id) {
|
|
2091
2126
|
const mapping = payload;
|
|
2092
2127
|
if (!mapping.uuid) {
|
|
2093
|
-
const
|
|
2094
|
-
mapping.uuid =
|
|
2128
|
+
const crypto2 = await import("crypto");
|
|
2129
|
+
mapping.uuid = crypto2.randomUUID();
|
|
2095
2130
|
}
|
|
2096
2131
|
await state.fileStore.saveMapping(mapping);
|
|
2097
2132
|
state.mappingsCache.set(mapping.uuid, mapping);
|
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.
|
|
10
|
-
<link rel="modulepreload" href="/_app/immutable/chunks/
|
|
9
|
+
<link rel="modulepreload" href="/_app/immutable/entry/start.z83Vr3Ay.js">
|
|
10
|
+
<link rel="modulepreload" href="/_app/immutable/chunks/Dacgmabg.js">
|
|
11
11
|
<link rel="modulepreload" href="/_app/immutable/chunks/Cby0Z7eP.js">
|
|
12
|
-
<link rel="modulepreload" href="/_app/immutable/entry/app.
|
|
12
|
+
<link rel="modulepreload" href="/_app/immutable/entry/app.lS9kJbUY.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
|
-
|
|
22
|
+
__sveltekit_1lw4tcu = {
|
|
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.
|
|
30
|
-
import("/_app/immutable/entry/app.
|
|
29
|
+
import("/_app/immutable/entry/start.z83Vr3Ay.js"),
|
|
30
|
+
import("/_app/immutable/entry/app.lS9kJbUY.js")
|
|
31
31
|
]).then(([kit, app]) => {
|
|
32
32
|
kit.start(app, element);
|
|
33
33
|
});
|
package/dist/index.js
CHANGED
|
@@ -2829,6 +2829,7 @@ __export(spreadsheetRoutes_exports, {
|
|
|
2829
2829
|
default: () => spreadsheetRoutes_default,
|
|
2830
2830
|
scanControlSets: () => scanControlSets
|
|
2831
2831
|
});
|
|
2832
|
+
import crypto from "crypto";
|
|
2832
2833
|
import { parse as parseCSVSync } from "csv-parse/sync";
|
|
2833
2834
|
import ExcelJS from "exceljs";
|
|
2834
2835
|
import express from "express";
|
|
@@ -3147,6 +3148,15 @@ var init_spreadsheetRoutes = __esm({
|
|
|
3147
3148
|
controlSetName = "Imported Control Set",
|
|
3148
3149
|
controlSetDescription = "Imported from spreadsheet"
|
|
3149
3150
|
} = req.body;
|
|
3151
|
+
let justificationFields = [];
|
|
3152
|
+
if (req.body.justificationFields) {
|
|
3153
|
+
try {
|
|
3154
|
+
justificationFields = JSON.parse(req.body.justificationFields);
|
|
3155
|
+
debug("Justification fields received:", justificationFields);
|
|
3156
|
+
} catch (e) {
|
|
3157
|
+
console.error("Failed to parse justification fields:", e);
|
|
3158
|
+
}
|
|
3159
|
+
}
|
|
3150
3160
|
debug("Import parameters received:", {
|
|
3151
3161
|
controlIdField,
|
|
3152
3162
|
startRow,
|
|
@@ -3394,11 +3404,17 @@ var init_spreadsheetRoutes = __esm({
|
|
|
3394
3404
|
};
|
|
3395
3405
|
writeFileSync2(join4(baseDir, "lula.yaml"), yaml4.dump(controlSetData));
|
|
3396
3406
|
const controlsDir = join4(baseDir, "controls");
|
|
3407
|
+
const mappingsDir = join4(baseDir, "mappings");
|
|
3408
|
+
const justificationFieldNames = justificationFields;
|
|
3397
3409
|
families.forEach((familyControls, family) => {
|
|
3398
3410
|
const familyDir = join4(controlsDir, family);
|
|
3411
|
+
const familyMappingsDir = join4(mappingsDir, family);
|
|
3399
3412
|
if (!existsSync3(familyDir)) {
|
|
3400
3413
|
mkdirSync2(familyDir, { recursive: true });
|
|
3401
3414
|
}
|
|
3415
|
+
if (!existsSync3(familyMappingsDir)) {
|
|
3416
|
+
mkdirSync2(familyMappingsDir, { recursive: true });
|
|
3417
|
+
}
|
|
3402
3418
|
familyControls.forEach((control) => {
|
|
3403
3419
|
const controlId = control[controlIdFieldNameClean];
|
|
3404
3420
|
if (!controlId) {
|
|
@@ -3408,12 +3424,24 @@ var init_spreadsheetRoutes = __esm({
|
|
|
3408
3424
|
const controlIdStr = String(controlId).slice(0, 50);
|
|
3409
3425
|
const fileName2 = `${controlIdStr.replace(/[^a-zA-Z0-9-]/g, "_")}.yaml`;
|
|
3410
3426
|
const filePath = join4(familyDir, fileName2);
|
|
3427
|
+
const mappingFileName = `${controlIdStr.replace(/[^a-zA-Z0-9-]/g, "_")}-mappings.yaml`;
|
|
3428
|
+
const mappingFilePath = join4(familyMappingsDir, mappingFileName);
|
|
3411
3429
|
const filteredControl = {};
|
|
3430
|
+
const mappingData = {
|
|
3431
|
+
control_id: controlIdStr,
|
|
3432
|
+
justification: "",
|
|
3433
|
+
uuid: crypto.randomUUID()
|
|
3434
|
+
};
|
|
3435
|
+
const justificationContents = [];
|
|
3412
3436
|
if (control.family !== void 0) {
|
|
3413
3437
|
filteredControl.family = control.family;
|
|
3414
3438
|
}
|
|
3415
3439
|
Object.keys(control).forEach((fieldName) => {
|
|
3416
3440
|
if (fieldName === "family") return;
|
|
3441
|
+
if (justificationFields.includes(fieldName) && control[fieldName] !== void 0 && control[fieldName] !== null) {
|
|
3442
|
+
justificationContents.push(control[fieldName]);
|
|
3443
|
+
return;
|
|
3444
|
+
}
|
|
3417
3445
|
const isInFrontendSchema = frontendFieldSchema?.some((f) => f.fieldName === fieldName);
|
|
3418
3446
|
const isInFieldsMetadata = fields.hasOwnProperty(fieldName);
|
|
3419
3447
|
if (isInFrontendSchema || isInFieldsMetadata) {
|
|
@@ -3421,6 +3449,13 @@ var init_spreadsheetRoutes = __esm({
|
|
|
3421
3449
|
}
|
|
3422
3450
|
});
|
|
3423
3451
|
writeFileSync2(filePath, yaml4.dump(filteredControl));
|
|
3452
|
+
if (justificationContents.length > 0) {
|
|
3453
|
+
mappingData.justification = justificationContents.join("\n\n");
|
|
3454
|
+
}
|
|
3455
|
+
if (mappingData.justification && mappingData.justification.trim() !== "") {
|
|
3456
|
+
const mappingArray = [mappingData];
|
|
3457
|
+
writeFileSync2(mappingFilePath, yaml4.dump(mappingArray));
|
|
3458
|
+
}
|
|
3424
3459
|
});
|
|
3425
3460
|
});
|
|
3426
3461
|
res.json({
|
|
@@ -4537,8 +4572,8 @@ var WebSocketManager = class {
|
|
|
4537
4572
|
if (payload && payload.control_id) {
|
|
4538
4573
|
const mapping = payload;
|
|
4539
4574
|
if (!mapping.uuid) {
|
|
4540
|
-
const
|
|
4541
|
-
mapping.uuid =
|
|
4575
|
+
const crypto2 = await import("crypto");
|
|
4576
|
+
mapping.uuid = crypto2.randomUUID();
|
|
4542
4577
|
}
|
|
4543
4578
|
await state.fileStore.saveMapping(mapping);
|
|
4544
4579
|
state.mappingsCache.set(mapping.uuid, mapping);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lula2",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "A tool for managing compliance as code in your GitHub repositories.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"lula2": "./dist/lula2"
|
|
@@ -33,6 +33,25 @@
|
|
|
33
33
|
"!dist/**/*.test.js*",
|
|
34
34
|
"!dist/**/*.test.d.ts*"
|
|
35
35
|
],
|
|
36
|
+
"scripts": {
|
|
37
|
+
"dev": "vite dev --port 5173",
|
|
38
|
+
"dev:api": "tsx --watch index.ts --debug ui --port 3000 --no-open-browser",
|
|
39
|
+
"dev:full": "concurrently \"npm run dev:api\" \"npm run dev\"",
|
|
40
|
+
"build": "npm run build:svelte && npm run build:cli && npm run postbuild:cli",
|
|
41
|
+
"build:svelte": "vite build",
|
|
42
|
+
"build:cli": "esbuild index.ts cli/**/*.ts --bundle --platform=node --target=node22 --format=esm --outdir=dist --external:express --external:commander --external:js-yaml --external:yaml --external:isomorphic-git --external:glob --external:open --external:ws --external:cors --external:multer --external:@octokit/rest --external:undici --external:exceljs --external:csv-parse",
|
|
43
|
+
"postbuild:cli": "cp cli-wrapper.mjs dist/lula2 && chmod +x dist/lula2",
|
|
44
|
+
"preview": "vite preview",
|
|
45
|
+
"prepare": "svelte-kit sync || echo ''",
|
|
46
|
+
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json && tsc --noEmit",
|
|
47
|
+
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
|
48
|
+
"format": "prettier --write 'src/**/*.{ts,js,svelte}' 'cli/**/*.ts' 'index.ts' 'tests/**/*.ts'",
|
|
49
|
+
"format:check": "prettier --check 'src/**/*.{ts,js,svelte}' 'cli/**/*.ts' 'index.ts' 'tests/**/*.ts'",
|
|
50
|
+
"lint": "prettier --check 'src/**/*.{ts,js,svelte}' 'cli/**/*.ts' 'index.ts' 'tests/**/*.ts' && eslint src cli",
|
|
51
|
+
"test": "npm run test:unit -- --run --coverage",
|
|
52
|
+
"test:integration": "vitest --config integration/vitest.config.integration.ts",
|
|
53
|
+
"test:unit": "vitest"
|
|
54
|
+
},
|
|
36
55
|
"dependencies": {
|
|
37
56
|
"@octokit/rest": "^22.0.0",
|
|
38
57
|
"@types/ws": "^8.18.1",
|
|
@@ -104,23 +123,5 @@
|
|
|
104
123
|
"main",
|
|
105
124
|
"next"
|
|
106
125
|
]
|
|
107
|
-
},
|
|
108
|
-
"scripts": {
|
|
109
|
-
"dev": "vite dev --port 5173",
|
|
110
|
-
"dev:api": "tsx --watch index.ts --debug ui --port 3000 --no-open-browser",
|
|
111
|
-
"dev:full": "concurrently \"npm run dev:api\" \"npm run dev\"",
|
|
112
|
-
"build": "npm run build:svelte && npm run build:cli && npm run postbuild:cli",
|
|
113
|
-
"build:svelte": "vite build",
|
|
114
|
-
"build:cli": "esbuild index.ts cli/**/*.ts --bundle --platform=node --target=node22 --format=esm --outdir=dist --external:express --external:commander --external:js-yaml --external:yaml --external:isomorphic-git --external:glob --external:open --external:ws --external:cors --external:multer --external:@octokit/rest --external:undici --external:exceljs --external:csv-parse",
|
|
115
|
-
"postbuild:cli": "cp cli-wrapper.mjs dist/lula2 && chmod +x dist/lula2",
|
|
116
|
-
"preview": "vite preview",
|
|
117
|
-
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json && tsc --noEmit",
|
|
118
|
-
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
|
119
|
-
"format": "prettier --write 'src/**/*.{ts,js,svelte}' 'cli/**/*.ts' 'index.ts' 'tests/**/*.ts'",
|
|
120
|
-
"format:check": "prettier --check 'src/**/*.{ts,js,svelte}' 'cli/**/*.ts' 'index.ts' 'tests/**/*.ts'",
|
|
121
|
-
"lint": "prettier --check 'src/**/*.{ts,js,svelte}' 'cli/**/*.ts' 'index.ts' 'tests/**/*.ts' && eslint src cli",
|
|
122
|
-
"test": "npm run test:unit -- --run --coverage",
|
|
123
|
-
"test:integration": "vitest --config integration/vitest.config.integration.ts",
|
|
124
|
-
"test:unit": "vitest"
|
|
125
126
|
}
|
|
126
|
-
}
|
|
127
|
+
}
|
|
@@ -17,7 +17,10 @@
|
|
|
17
17
|
$: availableFields = fields.filter((f) => fields.includes(f));
|
|
18
18
|
|
|
19
19
|
// Field configuration for tabs
|
|
20
|
-
type TabAssignment = 'overview' | 'implementation' | 'custom' | null;
|
|
20
|
+
type TabAssignment = 'overview' | 'implementation' | 'mappings' | 'custom' | null;
|
|
21
|
+
|
|
22
|
+
// Store fields for justification
|
|
23
|
+
let justificationFields: string[] = [];
|
|
21
24
|
let fieldConfigs = new Map<
|
|
22
25
|
string,
|
|
23
26
|
{
|
|
@@ -55,6 +58,7 @@
|
|
|
55
58
|
controlCount = 0;
|
|
56
59
|
fieldConfigs.clear();
|
|
57
60
|
fieldConfigs = new Map(); // Force reactivity
|
|
61
|
+
justificationFields = [];
|
|
58
62
|
|
|
59
63
|
// Reset selections
|
|
60
64
|
controlIdField = '';
|
|
@@ -307,6 +311,17 @@
|
|
|
307
311
|
e.preventDefault();
|
|
308
312
|
if (draggedField && fieldConfigs.has(draggedField)) {
|
|
309
313
|
const config = fieldConfigs.get(draggedField)!;
|
|
314
|
+
|
|
315
|
+
// If dragging from mappings tab to another tab, remove from justificationFields
|
|
316
|
+
if (config.tab === 'mappings' && tab !== 'mappings') {
|
|
317
|
+
justificationFields = justificationFields.filter((f) => f !== draggedField);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// If dragging to mappings tab, add to justificationFields
|
|
321
|
+
if (tab === 'mappings' && !justificationFields.includes(draggedField)) {
|
|
322
|
+
justificationFields = [...justificationFields, draggedField];
|
|
323
|
+
}
|
|
324
|
+
|
|
310
325
|
config.tab = tab;
|
|
311
326
|
|
|
312
327
|
// If dropping at a specific position, update display orders
|
|
@@ -420,6 +435,12 @@
|
|
|
420
435
|
}));
|
|
421
436
|
formData.append('fieldSchema', JSON.stringify(fieldSchema));
|
|
422
437
|
|
|
438
|
+
// Add justification fields
|
|
439
|
+
formData.append(
|
|
440
|
+
'justificationFields',
|
|
441
|
+
JSON.stringify(justificationFields.map((field) => cleanFieldName(field)))
|
|
442
|
+
);
|
|
443
|
+
|
|
423
444
|
const response = await fetch('/api/import-spreadsheet', {
|
|
424
445
|
method: 'POST',
|
|
425
446
|
body: formData
|
|
@@ -692,7 +713,7 @@
|
|
|
692
713
|
</p>
|
|
693
714
|
|
|
694
715
|
<!-- Column Layout -->
|
|
695
|
-
<div class="grid grid-cols-1 lg:grid-cols-
|
|
716
|
+
<div class="grid grid-cols-1 lg:grid-cols-5 gap-4">
|
|
696
717
|
<!-- Excluded Fields Column -->
|
|
697
718
|
<div
|
|
698
719
|
class="border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800"
|
|
@@ -843,6 +864,88 @@
|
|
|
843
864
|
</div>
|
|
844
865
|
</div>
|
|
845
866
|
|
|
867
|
+
<!-- Mappings Tab Column -->
|
|
868
|
+
<div
|
|
869
|
+
class="border border-orange-300 dark:border-orange-700 rounded-lg bg-white dark:bg-gray-800"
|
|
870
|
+
>
|
|
871
|
+
<div
|
|
872
|
+
class="p-3 border-b border-orange-200 dark:border-orange-800 bg-orange-50 dark:bg-orange-900/20 rounded-t-lg"
|
|
873
|
+
>
|
|
874
|
+
<h4 class="text-sm font-semibold text-orange-700 dark:text-orange-300">Mappings Tab</h4>
|
|
875
|
+
<p class="text-xs text-orange-600 dark:text-orange-400 mt-1">
|
|
876
|
+
Pre-populate justification for a control mapping
|
|
877
|
+
</p>
|
|
878
|
+
</div>
|
|
879
|
+
<div
|
|
880
|
+
class="p-3 min-h-[400px] max-h-[600px] overflow-y-auto transition-colors
|
|
881
|
+
{dragOverTab === 'mappings' ? 'bg-orange-50 dark:bg-orange-900/10' : ''}"
|
|
882
|
+
on:dragover={(e) => handleTabDragOver(e, 'mappings')}
|
|
883
|
+
on:dragleave={handleTabDragLeave}
|
|
884
|
+
on:drop={(e) => handleTabDrop(e, 'mappings')}
|
|
885
|
+
role="region"
|
|
886
|
+
aria-label="Justifications tab drop zone"
|
|
887
|
+
>
|
|
888
|
+
<!-- Justification Fields -->
|
|
889
|
+
<div class="space-y-2">
|
|
890
|
+
{#if justificationFields.length > 0}
|
|
891
|
+
<!-- Display justification fields -->
|
|
892
|
+
{#each justificationFields as field, index}
|
|
893
|
+
<div
|
|
894
|
+
draggable="true"
|
|
895
|
+
on:dragstart={(e) => handleFieldDragStart(e, field)}
|
|
896
|
+
on:dragend={handleFieldDragEnd}
|
|
897
|
+
on:dragover={(e) => handleFieldDragOver(e, field)}
|
|
898
|
+
on:dragleave={handleFieldDragLeave}
|
|
899
|
+
on:drop={(e) => handleFieldDrop(e, field, 'mappings')}
|
|
900
|
+
role="button"
|
|
901
|
+
aria-label="{field} field in Mappings tab"
|
|
902
|
+
tabindex="0"
|
|
903
|
+
class="flex items-center px-3 py-2 bg-orange-100 dark:bg-orange-900/30 text-orange-800 dark:text-orange-300 rounded text-sm cursor-move hover:bg-orange-200 dark:hover:bg-orange-800/30 transition-colors
|
|
904
|
+
{dragOverField === field && draggedField !== field ? 'border-t-2 border-orange-500' : ''}"
|
|
905
|
+
>
|
|
906
|
+
<Draggable class="w-3 h-3 mr-2 flex-shrink-0" />
|
|
907
|
+
<span class="truncate">{field}</span>
|
|
908
|
+
</div>
|
|
909
|
+
{/each}
|
|
910
|
+
{:else}
|
|
911
|
+
<!-- Drop zone only shown when no fields are present -->
|
|
912
|
+
<div
|
|
913
|
+
role="region"
|
|
914
|
+
aria-label="Justification field drop zone"
|
|
915
|
+
class="p-4 transition-colors
|
|
916
|
+
{dragOverTab === 'mappings' ? 'bg-orange-50 dark:bg-orange-900/10' : ''}"
|
|
917
|
+
on:dragover={(e) => {
|
|
918
|
+
e.preventDefault();
|
|
919
|
+
handleTabDragOver(e, 'mappings');
|
|
920
|
+
}}
|
|
921
|
+
on:dragleave={handleTabDragLeave}
|
|
922
|
+
on:drop={(e) => {
|
|
923
|
+
e.preventDefault();
|
|
924
|
+
if (draggedField && fieldConfigs.has(draggedField)) {
|
|
925
|
+
// Add to justification fields if not already present
|
|
926
|
+
if (!justificationFields.includes(draggedField)) {
|
|
927
|
+
justificationFields = [...justificationFields, draggedField];
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
// Set tab assignment
|
|
931
|
+
const config = fieldConfigs.get(draggedField)!;
|
|
932
|
+
config.tab = 'mappings';
|
|
933
|
+
fieldConfigs.set(draggedField, config);
|
|
934
|
+
fieldConfigs = new Map(fieldConfigs); // Force reactivity
|
|
935
|
+
|
|
936
|
+
dragOverTab = null;
|
|
937
|
+
}
|
|
938
|
+
}}
|
|
939
|
+
>
|
|
940
|
+
<p class="text-xs text-gray-400 dark:text-gray-500 text-center py-4">
|
|
941
|
+
Drop fields here
|
|
942
|
+
</p>
|
|
943
|
+
</div>
|
|
944
|
+
{/if}
|
|
945
|
+
</div>
|
|
946
|
+
</div>
|
|
947
|
+
</div>
|
|
948
|
+
|
|
846
949
|
<!-- Custom Tab Column -->
|
|
847
950
|
<div
|
|
848
951
|
class="border border-purple-300 dark:border-purple-700 rounded-lg bg-white dark:bg-gray-800"
|