lula2 0.7.3 → 0.7.5
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/README.md +0 -1
- package/dist/_app/immutable/chunks/{DqNseCFX.js → BI-GirXZ.js} +1 -1
- package/dist/_app/immutable/chunks/{DHBD-O6i.js → BIdjJ0zz.js} +1 -1
- package/dist/_app/immutable/chunks/BSyVkqhj.js +2 -0
- package/dist/_app/immutable/chunks/{DzukiT8m.js → Cng7c2CG.js} +1 -1
- package/dist/_app/immutable/chunks/CxBMFlfX.js +1 -0
- package/dist/_app/immutable/chunks/{D5ermNRH.js → DArZRX9-.js} +36 -36
- package/dist/_app/immutable/chunks/{CUt21Xol.js → DH2IP9c7.js} +1 -1
- package/dist/_app/immutable/chunks/{BBITAbWI.js → DXSHWIjJ.js} +1 -1
- package/dist/_app/immutable/chunks/{Cih4fRH-.js → DsuS1uUo.js} +1 -1
- package/dist/_app/immutable/chunks/urFjAlpd.js +1 -0
- package/dist/_app/immutable/entry/{app.BvGur0mr.js → app.CjycYot0.js} +2 -2
- package/dist/_app/immutable/entry/start.Bgy9x4Qb.js +1 -0
- package/dist/_app/immutable/nodes/{0.Dx6r-xPK.js → 0.CGKh5y4X.js} +1 -1
- package/dist/_app/immutable/nodes/{1.COpX6TiD.js → 1.D5L7DxSG.js} +1 -1
- package/dist/_app/immutable/nodes/{2.zlTZskRg.js → 2.Hrl6uq-b.js} +1 -1
- package/dist/_app/immutable/nodes/{3.BafDPdLR.js → 3.BoHxdRm3.js} +1 -1
- package/dist/_app/immutable/nodes/{4.CoEqSeTv.js → 4.DAVWsDkK.js} +1 -1
- package/dist/_app/version.json +1 -1
- package/dist/cli/commands/ui.js +46 -41
- package/dist/cli/server/index.js +46 -41
- package/dist/cli/server/server.js +46 -41
- package/dist/cli/server/serverState.js +23 -11
- package/dist/cli/server/websocketServer.js +40 -41
- package/dist/index.html +11 -11
- package/dist/index.js +49 -44
- package/package.json +2 -2
- package/src/lib/components/controls/MappingCard.svelte +3 -3
- package/src/lib/components/controls/tabs/MappingsTab.svelte +27 -17
- package/src/lib/types.ts +2 -0
- package/src/lib/websocket.ts +2 -6
- package/dist/_app/immutable/chunks/B3ME9fYB.js +0 -1
- package/dist/_app/immutable/chunks/DtiGB2s7.js +0 -2
- package/dist/_app/immutable/chunks/R-CI0WwD.js +0 -1
- package/dist/_app/immutable/entry/start.yE51hRYN.js +0 -1
package/dist/cli/commands/ui.js
CHANGED
|
@@ -1680,6 +1680,7 @@ import {
|
|
|
1680
1680
|
} from "fs";
|
|
1681
1681
|
import * as yaml2 from "js-yaml";
|
|
1682
1682
|
import { join as join2 } from "path";
|
|
1683
|
+
import { createHash as createHash2 } from "crypto";
|
|
1683
1684
|
var FileStore;
|
|
1684
1685
|
var init_fileStore = __esm({
|
|
1685
1686
|
"cli/server/infrastructure/fileStore.ts"() {
|
|
@@ -1977,6 +1978,10 @@ var init_fileStore = __esm({
|
|
|
1977
1978
|
const content = readFileSync2(mappingFile, "utf8");
|
|
1978
1979
|
const parsed = yaml2.load(content);
|
|
1979
1980
|
if (Array.isArray(parsed)) {
|
|
1981
|
+
parsed.forEach((mapping) => {
|
|
1982
|
+
mapping.hash = createHash2("sha256").update(JSON.stringify(mapping)).digest("hex");
|
|
1983
|
+
return mapping;
|
|
1984
|
+
});
|
|
1980
1985
|
mappings.push(...parsed);
|
|
1981
1986
|
}
|
|
1982
1987
|
} catch (error) {
|
|
@@ -2011,12 +2016,9 @@ var init_fileStore = __esm({
|
|
|
2011
2016
|
existingMappings = [];
|
|
2012
2017
|
}
|
|
2013
2018
|
}
|
|
2014
|
-
const
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
} else {
|
|
2018
|
-
existingMappings.push(mapping);
|
|
2019
|
-
}
|
|
2019
|
+
const cleanMapping = { ...mapping };
|
|
2020
|
+
delete cleanMapping.hash;
|
|
2021
|
+
existingMappings.push(cleanMapping);
|
|
2020
2022
|
try {
|
|
2021
2023
|
const yamlContent = yaml2.dump(existingMappings, {
|
|
2022
2024
|
indent: 2,
|
|
@@ -2041,7 +2043,8 @@ var init_fileStore = __esm({
|
|
|
2041
2043
|
let mappings = yaml2.load(content) || [];
|
|
2042
2044
|
const originalLength = mappings.length;
|
|
2043
2045
|
mappings = mappings.filter((m) => {
|
|
2044
|
-
|
|
2046
|
+
const hash = createHash2("sha256").update(JSON.stringify(m)).digest("hex");
|
|
2047
|
+
return `${m.control_id}:${hash}` !== compositeKey;
|
|
2045
2048
|
});
|
|
2046
2049
|
if (mappings.length < originalLength) {
|
|
2047
2050
|
if (mappings.length === 0) {
|
|
@@ -2104,8 +2107,13 @@ var init_fileStore = __esm({
|
|
|
2104
2107
|
if (!existsSync2(familyDir)) {
|
|
2105
2108
|
mkdirSync(familyDir, { recursive: true });
|
|
2106
2109
|
}
|
|
2110
|
+
const cleanMappings = controlMappings.map((m) => {
|
|
2111
|
+
const clean = { ...m };
|
|
2112
|
+
delete clean.hash;
|
|
2113
|
+
return clean;
|
|
2114
|
+
});
|
|
2107
2115
|
try {
|
|
2108
|
-
const yamlContent = yaml2.dump(
|
|
2116
|
+
const yamlContent = yaml2.dump(cleanMappings, {
|
|
2109
2117
|
indent: 2,
|
|
2110
2118
|
lineWidth: -1,
|
|
2111
2119
|
noRefs: true
|
|
@@ -3024,6 +3032,7 @@ __export(serverState_exports, {
|
|
|
3024
3032
|
saveMappingsToFile: () => saveMappingsToFile
|
|
3025
3033
|
});
|
|
3026
3034
|
import { join as join3 } from "path";
|
|
3035
|
+
import { createHash as createHash3 } from "crypto";
|
|
3027
3036
|
function initializeServerState(controlSetDir, subdir = ".") {
|
|
3028
3037
|
const fullPath = subdir === "." ? controlSetDir : join3(controlSetDir, subdir);
|
|
3029
3038
|
serverState = {
|
|
@@ -3066,11 +3075,11 @@ function addMappingToIndexes(mapping) {
|
|
|
3066
3075
|
if (!state.mappingsByFamily.has(family)) {
|
|
3067
3076
|
state.mappingsByFamily.set(family, /* @__PURE__ */ new Set());
|
|
3068
3077
|
}
|
|
3069
|
-
state.mappingsByFamily.get(family).add(mapping.
|
|
3078
|
+
state.mappingsByFamily.get(family).add(mapping.hash);
|
|
3070
3079
|
if (!state.mappingsByControl.has(mapping.control_id)) {
|
|
3071
3080
|
state.mappingsByControl.set(mapping.control_id, /* @__PURE__ */ new Set());
|
|
3072
3081
|
}
|
|
3073
|
-
state.mappingsByControl.get(mapping.control_id).add(mapping.
|
|
3082
|
+
state.mappingsByControl.get(mapping.control_id).add(mapping.hash);
|
|
3074
3083
|
}
|
|
3075
3084
|
async function loadAllData() {
|
|
3076
3085
|
const state = getServerState();
|
|
@@ -3084,7 +3093,10 @@ async function loadAllData() {
|
|
|
3084
3093
|
debug(`Loaded ${controls.length} controls from individual files`);
|
|
3085
3094
|
const mappings = await state.fileStore.loadMappings();
|
|
3086
3095
|
for (const mapping of mappings) {
|
|
3087
|
-
|
|
3096
|
+
if (!mapping.hash) {
|
|
3097
|
+
mapping.hash = createHash3("sha256").update(JSON.stringify(mapping)).digest("hex");
|
|
3098
|
+
}
|
|
3099
|
+
const compositeKey = `${mapping.control_id}:${mapping.hash}`;
|
|
3088
3100
|
state.mappingsCache.set(compositeKey, mapping);
|
|
3089
3101
|
addMappingToIndexes(mapping);
|
|
3090
3102
|
}
|
|
@@ -5132,6 +5144,7 @@ init_gitHistory();
|
|
|
5132
5144
|
import * as yaml5 from "js-yaml";
|
|
5133
5145
|
import { join as join5 } from "path";
|
|
5134
5146
|
import { WebSocket, WebSocketServer } from "ws";
|
|
5147
|
+
import crypto2 from "node:crypto";
|
|
5135
5148
|
var WebSocketManager = class {
|
|
5136
5149
|
wss = null;
|
|
5137
5150
|
clients = /* @__PURE__ */ new Set();
|
|
@@ -5196,21 +5209,24 @@ var WebSocketManager = class {
|
|
|
5196
5209
|
if (payload && payload.control_id) {
|
|
5197
5210
|
const mapping = payload;
|
|
5198
5211
|
if (!mapping.uuid) {
|
|
5199
|
-
const
|
|
5200
|
-
mapping.uuid =
|
|
5212
|
+
const crypto3 = await import("crypto");
|
|
5213
|
+
mapping.uuid = crypto3.randomUUID();
|
|
5201
5214
|
}
|
|
5215
|
+
if (!mapping.hash || mapping.hash === "") {
|
|
5216
|
+
mapping.hash = crypto2.createHash("sha256").update(JSON.stringify(mapping)).digest("hex");
|
|
5217
|
+
}
|
|
5218
|
+
const compositeKey = `${mapping.control_id}:${mapping.hash}`;
|
|
5202
5219
|
await state.fileStore.saveMapping(mapping);
|
|
5203
|
-
const compositeKey = `${mapping.control_id}:${mapping.uuid}`;
|
|
5204
5220
|
state.mappingsCache.set(compositeKey, mapping);
|
|
5205
5221
|
const family = mapping.control_id.split("-")[0];
|
|
5206
5222
|
if (!state.mappingsByFamily.has(family)) {
|
|
5207
5223
|
state.mappingsByFamily.set(family, /* @__PURE__ */ new Set());
|
|
5208
5224
|
}
|
|
5209
|
-
state.mappingsByFamily.get(family)?.add(mapping.
|
|
5225
|
+
state.mappingsByFamily.get(family)?.add(mapping.hash);
|
|
5210
5226
|
if (!state.mappingsByControl.has(mapping.control_id)) {
|
|
5211
5227
|
state.mappingsByControl.set(mapping.control_id, /* @__PURE__ */ new Set());
|
|
5212
5228
|
}
|
|
5213
|
-
state.mappingsByControl.get(mapping.control_id)?.add(mapping.
|
|
5229
|
+
state.mappingsByControl.get(mapping.control_id)?.add(mapping.hash);
|
|
5214
5230
|
ws.send(
|
|
5215
5231
|
JSON.stringify({
|
|
5216
5232
|
type: "mapping-created",
|
|
@@ -5221,38 +5237,21 @@ var WebSocketManager = class {
|
|
|
5221
5237
|
}
|
|
5222
5238
|
break;
|
|
5223
5239
|
}
|
|
5224
|
-
case "update-mapping": {
|
|
5225
|
-
const state = getServerState();
|
|
5226
|
-
if (payload && payload.uuid) {
|
|
5227
|
-
const mapping = payload;
|
|
5228
|
-
await state.fileStore.saveMapping(mapping);
|
|
5229
|
-
const compositeKey = `${mapping.control_id}:${mapping.uuid}`;
|
|
5230
|
-
state.mappingsCache.set(compositeKey, mapping);
|
|
5231
|
-
ws.send(
|
|
5232
|
-
JSON.stringify({
|
|
5233
|
-
type: "mapping-updated",
|
|
5234
|
-
payload: { uuid: mapping.uuid, success: true }
|
|
5235
|
-
})
|
|
5236
|
-
);
|
|
5237
|
-
this.broadcastState();
|
|
5238
|
-
}
|
|
5239
|
-
break;
|
|
5240
|
-
}
|
|
5241
5240
|
case "delete-mapping": {
|
|
5242
5241
|
const state = getServerState();
|
|
5243
|
-
if (payload && payload.
|
|
5244
|
-
const
|
|
5245
|
-
const mapping = state.mappingsCache.get(
|
|
5242
|
+
if (payload && payload.composite_key) {
|
|
5243
|
+
const composite_key = payload.composite_key;
|
|
5244
|
+
const mapping = state.mappingsCache.get(composite_key);
|
|
5246
5245
|
if (mapping) {
|
|
5247
|
-
await state.fileStore.deleteMapping(
|
|
5248
|
-
state.mappingsCache.delete(
|
|
5246
|
+
await state.fileStore.deleteMapping(composite_key);
|
|
5247
|
+
state.mappingsCache.delete(composite_key);
|
|
5249
5248
|
const family = mapping.control_id.split("-")[0];
|
|
5250
|
-
state.mappingsByFamily.get(family)?.delete(
|
|
5251
|
-
state.mappingsByControl.get(mapping.control_id)?.delete(
|
|
5249
|
+
state.mappingsByFamily.get(family)?.delete(mapping.hash);
|
|
5250
|
+
state.mappingsByControl.get(mapping.control_id)?.delete(mapping.hash);
|
|
5252
5251
|
ws.send(
|
|
5253
5252
|
JSON.stringify({
|
|
5254
5253
|
type: "mapping-deleted",
|
|
5255
|
-
payload: {
|
|
5254
|
+
payload: { hash: mapping.hash, success: true }
|
|
5256
5255
|
})
|
|
5257
5256
|
);
|
|
5258
5257
|
this.broadcastState();
|
|
@@ -5729,6 +5728,7 @@ var WebSocketManager = class {
|
|
|
5729
5728
|
var wsManager = new WebSocketManager();
|
|
5730
5729
|
|
|
5731
5730
|
// cli/server/server.ts
|
|
5731
|
+
import { createHash as createHash4 } from "crypto";
|
|
5732
5732
|
var __filename = fileURLToPath(import.meta.url);
|
|
5733
5733
|
var __dirname = dirname2(__filename);
|
|
5734
5734
|
async function createServer(options) {
|
|
@@ -5754,6 +5754,11 @@ async function createServer(options) {
|
|
|
5754
5754
|
app.get("/*splat", (req, res) => {
|
|
5755
5755
|
res.sendFile("index.html", { root: distPath });
|
|
5756
5756
|
});
|
|
5757
|
+
app.post("/hash", (req, res) => {
|
|
5758
|
+
delete req.body.hash;
|
|
5759
|
+
const hash = createHash4("sha256").update(JSON.stringify(req.body)).digest("hex");
|
|
5760
|
+
res.status(200).json({ hash });
|
|
5761
|
+
});
|
|
5757
5762
|
const httpServer = createHttpServer(app);
|
|
5758
5763
|
wsManager.initialize(httpServer);
|
|
5759
5764
|
return {
|
package/dist/cli/server/index.js
CHANGED
|
@@ -1662,6 +1662,7 @@ import {
|
|
|
1662
1662
|
} from "fs";
|
|
1663
1663
|
import * as yaml2 from "js-yaml";
|
|
1664
1664
|
import { join as join2 } from "path";
|
|
1665
|
+
import { createHash as createHash2 } from "crypto";
|
|
1665
1666
|
var FileStore;
|
|
1666
1667
|
var init_fileStore = __esm({
|
|
1667
1668
|
"cli/server/infrastructure/fileStore.ts"() {
|
|
@@ -1959,6 +1960,10 @@ var init_fileStore = __esm({
|
|
|
1959
1960
|
const content = readFileSync2(mappingFile, "utf8");
|
|
1960
1961
|
const parsed = yaml2.load(content);
|
|
1961
1962
|
if (Array.isArray(parsed)) {
|
|
1963
|
+
parsed.forEach((mapping) => {
|
|
1964
|
+
mapping.hash = createHash2("sha256").update(JSON.stringify(mapping)).digest("hex");
|
|
1965
|
+
return mapping;
|
|
1966
|
+
});
|
|
1962
1967
|
mappings.push(...parsed);
|
|
1963
1968
|
}
|
|
1964
1969
|
} catch (error) {
|
|
@@ -1993,12 +1998,9 @@ var init_fileStore = __esm({
|
|
|
1993
1998
|
existingMappings = [];
|
|
1994
1999
|
}
|
|
1995
2000
|
}
|
|
1996
|
-
const
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
} else {
|
|
2000
|
-
existingMappings.push(mapping);
|
|
2001
|
-
}
|
|
2001
|
+
const cleanMapping = { ...mapping };
|
|
2002
|
+
delete cleanMapping.hash;
|
|
2003
|
+
existingMappings.push(cleanMapping);
|
|
2002
2004
|
try {
|
|
2003
2005
|
const yamlContent = yaml2.dump(existingMappings, {
|
|
2004
2006
|
indent: 2,
|
|
@@ -2023,7 +2025,8 @@ var init_fileStore = __esm({
|
|
|
2023
2025
|
let mappings = yaml2.load(content) || [];
|
|
2024
2026
|
const originalLength = mappings.length;
|
|
2025
2027
|
mappings = mappings.filter((m) => {
|
|
2026
|
-
|
|
2028
|
+
const hash = createHash2("sha256").update(JSON.stringify(m)).digest("hex");
|
|
2029
|
+
return `${m.control_id}:${hash}` !== compositeKey;
|
|
2027
2030
|
});
|
|
2028
2031
|
if (mappings.length < originalLength) {
|
|
2029
2032
|
if (mappings.length === 0) {
|
|
@@ -2086,8 +2089,13 @@ var init_fileStore = __esm({
|
|
|
2086
2089
|
if (!existsSync2(familyDir)) {
|
|
2087
2090
|
mkdirSync(familyDir, { recursive: true });
|
|
2088
2091
|
}
|
|
2092
|
+
const cleanMappings = controlMappings.map((m) => {
|
|
2093
|
+
const clean = { ...m };
|
|
2094
|
+
delete clean.hash;
|
|
2095
|
+
return clean;
|
|
2096
|
+
});
|
|
2089
2097
|
try {
|
|
2090
|
-
const yamlContent = yaml2.dump(
|
|
2098
|
+
const yamlContent = yaml2.dump(cleanMappings, {
|
|
2091
2099
|
indent: 2,
|
|
2092
2100
|
lineWidth: -1,
|
|
2093
2101
|
noRefs: true
|
|
@@ -3006,6 +3014,7 @@ __export(serverState_exports, {
|
|
|
3006
3014
|
saveMappingsToFile: () => saveMappingsToFile
|
|
3007
3015
|
});
|
|
3008
3016
|
import { join as join3 } from "path";
|
|
3017
|
+
import { createHash as createHash3 } from "crypto";
|
|
3009
3018
|
function initializeServerState(controlSetDir, subdir = ".") {
|
|
3010
3019
|
const fullPath = subdir === "." ? controlSetDir : join3(controlSetDir, subdir);
|
|
3011
3020
|
serverState = {
|
|
@@ -3048,11 +3057,11 @@ function addMappingToIndexes(mapping) {
|
|
|
3048
3057
|
if (!state.mappingsByFamily.has(family)) {
|
|
3049
3058
|
state.mappingsByFamily.set(family, /* @__PURE__ */ new Set());
|
|
3050
3059
|
}
|
|
3051
|
-
state.mappingsByFamily.get(family).add(mapping.
|
|
3060
|
+
state.mappingsByFamily.get(family).add(mapping.hash);
|
|
3052
3061
|
if (!state.mappingsByControl.has(mapping.control_id)) {
|
|
3053
3062
|
state.mappingsByControl.set(mapping.control_id, /* @__PURE__ */ new Set());
|
|
3054
3063
|
}
|
|
3055
|
-
state.mappingsByControl.get(mapping.control_id).add(mapping.
|
|
3064
|
+
state.mappingsByControl.get(mapping.control_id).add(mapping.hash);
|
|
3056
3065
|
}
|
|
3057
3066
|
async function loadAllData() {
|
|
3058
3067
|
const state = getServerState();
|
|
@@ -3066,7 +3075,10 @@ async function loadAllData() {
|
|
|
3066
3075
|
debug(`Loaded ${controls.length} controls from individual files`);
|
|
3067
3076
|
const mappings = await state.fileStore.loadMappings();
|
|
3068
3077
|
for (const mapping of mappings) {
|
|
3069
|
-
|
|
3078
|
+
if (!mapping.hash) {
|
|
3079
|
+
mapping.hash = createHash3("sha256").update(JSON.stringify(mapping)).digest("hex");
|
|
3080
|
+
}
|
|
3081
|
+
const compositeKey = `${mapping.control_id}:${mapping.hash}`;
|
|
3070
3082
|
state.mappingsCache.set(compositeKey, mapping);
|
|
3071
3083
|
addMappingToIndexes(mapping);
|
|
3072
3084
|
}
|
|
@@ -5109,6 +5121,7 @@ init_gitHistory();
|
|
|
5109
5121
|
import * as yaml5 from "js-yaml";
|
|
5110
5122
|
import { join as join5 } from "path";
|
|
5111
5123
|
import { WebSocket, WebSocketServer } from "ws";
|
|
5124
|
+
import crypto2 from "node:crypto";
|
|
5112
5125
|
var WebSocketManager = class {
|
|
5113
5126
|
wss = null;
|
|
5114
5127
|
clients = /* @__PURE__ */ new Set();
|
|
@@ -5173,21 +5186,24 @@ var WebSocketManager = class {
|
|
|
5173
5186
|
if (payload && payload.control_id) {
|
|
5174
5187
|
const mapping = payload;
|
|
5175
5188
|
if (!mapping.uuid) {
|
|
5176
|
-
const
|
|
5177
|
-
mapping.uuid =
|
|
5189
|
+
const crypto3 = await import("crypto");
|
|
5190
|
+
mapping.uuid = crypto3.randomUUID();
|
|
5178
5191
|
}
|
|
5192
|
+
if (!mapping.hash || mapping.hash === "") {
|
|
5193
|
+
mapping.hash = crypto2.createHash("sha256").update(JSON.stringify(mapping)).digest("hex");
|
|
5194
|
+
}
|
|
5195
|
+
const compositeKey = `${mapping.control_id}:${mapping.hash}`;
|
|
5179
5196
|
await state.fileStore.saveMapping(mapping);
|
|
5180
|
-
const compositeKey = `${mapping.control_id}:${mapping.uuid}`;
|
|
5181
5197
|
state.mappingsCache.set(compositeKey, mapping);
|
|
5182
5198
|
const family = mapping.control_id.split("-")[0];
|
|
5183
5199
|
if (!state.mappingsByFamily.has(family)) {
|
|
5184
5200
|
state.mappingsByFamily.set(family, /* @__PURE__ */ new Set());
|
|
5185
5201
|
}
|
|
5186
|
-
state.mappingsByFamily.get(family)?.add(mapping.
|
|
5202
|
+
state.mappingsByFamily.get(family)?.add(mapping.hash);
|
|
5187
5203
|
if (!state.mappingsByControl.has(mapping.control_id)) {
|
|
5188
5204
|
state.mappingsByControl.set(mapping.control_id, /* @__PURE__ */ new Set());
|
|
5189
5205
|
}
|
|
5190
|
-
state.mappingsByControl.get(mapping.control_id)?.add(mapping.
|
|
5206
|
+
state.mappingsByControl.get(mapping.control_id)?.add(mapping.hash);
|
|
5191
5207
|
ws.send(
|
|
5192
5208
|
JSON.stringify({
|
|
5193
5209
|
type: "mapping-created",
|
|
@@ -5198,38 +5214,21 @@ var WebSocketManager = class {
|
|
|
5198
5214
|
}
|
|
5199
5215
|
break;
|
|
5200
5216
|
}
|
|
5201
|
-
case "update-mapping": {
|
|
5202
|
-
const state = getServerState();
|
|
5203
|
-
if (payload && payload.uuid) {
|
|
5204
|
-
const mapping = payload;
|
|
5205
|
-
await state.fileStore.saveMapping(mapping);
|
|
5206
|
-
const compositeKey = `${mapping.control_id}:${mapping.uuid}`;
|
|
5207
|
-
state.mappingsCache.set(compositeKey, mapping);
|
|
5208
|
-
ws.send(
|
|
5209
|
-
JSON.stringify({
|
|
5210
|
-
type: "mapping-updated",
|
|
5211
|
-
payload: { uuid: mapping.uuid, success: true }
|
|
5212
|
-
})
|
|
5213
|
-
);
|
|
5214
|
-
this.broadcastState();
|
|
5215
|
-
}
|
|
5216
|
-
break;
|
|
5217
|
-
}
|
|
5218
5217
|
case "delete-mapping": {
|
|
5219
5218
|
const state = getServerState();
|
|
5220
|
-
if (payload && payload.
|
|
5221
|
-
const
|
|
5222
|
-
const mapping = state.mappingsCache.get(
|
|
5219
|
+
if (payload && payload.composite_key) {
|
|
5220
|
+
const composite_key = payload.composite_key;
|
|
5221
|
+
const mapping = state.mappingsCache.get(composite_key);
|
|
5223
5222
|
if (mapping) {
|
|
5224
|
-
await state.fileStore.deleteMapping(
|
|
5225
|
-
state.mappingsCache.delete(
|
|
5223
|
+
await state.fileStore.deleteMapping(composite_key);
|
|
5224
|
+
state.mappingsCache.delete(composite_key);
|
|
5226
5225
|
const family = mapping.control_id.split("-")[0];
|
|
5227
|
-
state.mappingsByFamily.get(family)?.delete(
|
|
5228
|
-
state.mappingsByControl.get(mapping.control_id)?.delete(
|
|
5226
|
+
state.mappingsByFamily.get(family)?.delete(mapping.hash);
|
|
5227
|
+
state.mappingsByControl.get(mapping.control_id)?.delete(mapping.hash);
|
|
5229
5228
|
ws.send(
|
|
5230
5229
|
JSON.stringify({
|
|
5231
5230
|
type: "mapping-deleted",
|
|
5232
|
-
payload: {
|
|
5231
|
+
payload: { hash: mapping.hash, success: true }
|
|
5233
5232
|
})
|
|
5234
5233
|
);
|
|
5235
5234
|
this.broadcastState();
|
|
@@ -5706,6 +5705,7 @@ var WebSocketManager = class {
|
|
|
5706
5705
|
var wsManager = new WebSocketManager();
|
|
5707
5706
|
|
|
5708
5707
|
// cli/server/server.ts
|
|
5708
|
+
import { createHash as createHash4 } from "crypto";
|
|
5709
5709
|
var __filename = fileURLToPath(import.meta.url);
|
|
5710
5710
|
var __dirname = dirname2(__filename);
|
|
5711
5711
|
async function createServer(options) {
|
|
@@ -5731,6 +5731,11 @@ async function createServer(options) {
|
|
|
5731
5731
|
app.get("/*splat", (req, res) => {
|
|
5732
5732
|
res.sendFile("index.html", { root: distPath });
|
|
5733
5733
|
});
|
|
5734
|
+
app.post("/hash", (req, res) => {
|
|
5735
|
+
delete req.body.hash;
|
|
5736
|
+
const hash = createHash4("sha256").update(JSON.stringify(req.body)).digest("hex");
|
|
5737
|
+
res.status(200).json({ hash });
|
|
5738
|
+
});
|
|
5734
5739
|
const httpServer = createHttpServer(app);
|
|
5735
5740
|
wsManager.initialize(httpServer);
|
|
5736
5741
|
return {
|
|
@@ -1662,6 +1662,7 @@ import {
|
|
|
1662
1662
|
} from "fs";
|
|
1663
1663
|
import * as yaml2 from "js-yaml";
|
|
1664
1664
|
import { join as join2 } from "path";
|
|
1665
|
+
import { createHash as createHash2 } from "crypto";
|
|
1665
1666
|
var FileStore;
|
|
1666
1667
|
var init_fileStore = __esm({
|
|
1667
1668
|
"cli/server/infrastructure/fileStore.ts"() {
|
|
@@ -1959,6 +1960,10 @@ var init_fileStore = __esm({
|
|
|
1959
1960
|
const content = readFileSync2(mappingFile, "utf8");
|
|
1960
1961
|
const parsed = yaml2.load(content);
|
|
1961
1962
|
if (Array.isArray(parsed)) {
|
|
1963
|
+
parsed.forEach((mapping) => {
|
|
1964
|
+
mapping.hash = createHash2("sha256").update(JSON.stringify(mapping)).digest("hex");
|
|
1965
|
+
return mapping;
|
|
1966
|
+
});
|
|
1962
1967
|
mappings.push(...parsed);
|
|
1963
1968
|
}
|
|
1964
1969
|
} catch (error) {
|
|
@@ -1993,12 +1998,9 @@ var init_fileStore = __esm({
|
|
|
1993
1998
|
existingMappings = [];
|
|
1994
1999
|
}
|
|
1995
2000
|
}
|
|
1996
|
-
const
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
} else {
|
|
2000
|
-
existingMappings.push(mapping);
|
|
2001
|
-
}
|
|
2001
|
+
const cleanMapping = { ...mapping };
|
|
2002
|
+
delete cleanMapping.hash;
|
|
2003
|
+
existingMappings.push(cleanMapping);
|
|
2002
2004
|
try {
|
|
2003
2005
|
const yamlContent = yaml2.dump(existingMappings, {
|
|
2004
2006
|
indent: 2,
|
|
@@ -2023,7 +2025,8 @@ var init_fileStore = __esm({
|
|
|
2023
2025
|
let mappings = yaml2.load(content) || [];
|
|
2024
2026
|
const originalLength = mappings.length;
|
|
2025
2027
|
mappings = mappings.filter((m) => {
|
|
2026
|
-
|
|
2028
|
+
const hash = createHash2("sha256").update(JSON.stringify(m)).digest("hex");
|
|
2029
|
+
return `${m.control_id}:${hash}` !== compositeKey;
|
|
2027
2030
|
});
|
|
2028
2031
|
if (mappings.length < originalLength) {
|
|
2029
2032
|
if (mappings.length === 0) {
|
|
@@ -2086,8 +2089,13 @@ var init_fileStore = __esm({
|
|
|
2086
2089
|
if (!existsSync2(familyDir)) {
|
|
2087
2090
|
mkdirSync(familyDir, { recursive: true });
|
|
2088
2091
|
}
|
|
2092
|
+
const cleanMappings = controlMappings.map((m) => {
|
|
2093
|
+
const clean = { ...m };
|
|
2094
|
+
delete clean.hash;
|
|
2095
|
+
return clean;
|
|
2096
|
+
});
|
|
2089
2097
|
try {
|
|
2090
|
-
const yamlContent = yaml2.dump(
|
|
2098
|
+
const yamlContent = yaml2.dump(cleanMappings, {
|
|
2091
2099
|
indent: 2,
|
|
2092
2100
|
lineWidth: -1,
|
|
2093
2101
|
noRefs: true
|
|
@@ -3006,6 +3014,7 @@ __export(serverState_exports, {
|
|
|
3006
3014
|
saveMappingsToFile: () => saveMappingsToFile
|
|
3007
3015
|
});
|
|
3008
3016
|
import { join as join3 } from "path";
|
|
3017
|
+
import { createHash as createHash3 } from "crypto";
|
|
3009
3018
|
function initializeServerState(controlSetDir, subdir = ".") {
|
|
3010
3019
|
const fullPath = subdir === "." ? controlSetDir : join3(controlSetDir, subdir);
|
|
3011
3020
|
serverState = {
|
|
@@ -3048,11 +3057,11 @@ function addMappingToIndexes(mapping) {
|
|
|
3048
3057
|
if (!state.mappingsByFamily.has(family)) {
|
|
3049
3058
|
state.mappingsByFamily.set(family, /* @__PURE__ */ new Set());
|
|
3050
3059
|
}
|
|
3051
|
-
state.mappingsByFamily.get(family).add(mapping.
|
|
3060
|
+
state.mappingsByFamily.get(family).add(mapping.hash);
|
|
3052
3061
|
if (!state.mappingsByControl.has(mapping.control_id)) {
|
|
3053
3062
|
state.mappingsByControl.set(mapping.control_id, /* @__PURE__ */ new Set());
|
|
3054
3063
|
}
|
|
3055
|
-
state.mappingsByControl.get(mapping.control_id).add(mapping.
|
|
3064
|
+
state.mappingsByControl.get(mapping.control_id).add(mapping.hash);
|
|
3056
3065
|
}
|
|
3057
3066
|
async function loadAllData() {
|
|
3058
3067
|
const state = getServerState();
|
|
@@ -3066,7 +3075,10 @@ async function loadAllData() {
|
|
|
3066
3075
|
debug(`Loaded ${controls.length} controls from individual files`);
|
|
3067
3076
|
const mappings = await state.fileStore.loadMappings();
|
|
3068
3077
|
for (const mapping of mappings) {
|
|
3069
|
-
|
|
3078
|
+
if (!mapping.hash) {
|
|
3079
|
+
mapping.hash = createHash3("sha256").update(JSON.stringify(mapping)).digest("hex");
|
|
3080
|
+
}
|
|
3081
|
+
const compositeKey = `${mapping.control_id}:${mapping.hash}`;
|
|
3070
3082
|
state.mappingsCache.set(compositeKey, mapping);
|
|
3071
3083
|
addMappingToIndexes(mapping);
|
|
3072
3084
|
}
|
|
@@ -5109,6 +5121,7 @@ init_gitHistory();
|
|
|
5109
5121
|
import * as yaml5 from "js-yaml";
|
|
5110
5122
|
import { join as join5 } from "path";
|
|
5111
5123
|
import { WebSocket, WebSocketServer } from "ws";
|
|
5124
|
+
import crypto2 from "node:crypto";
|
|
5112
5125
|
var WebSocketManager = class {
|
|
5113
5126
|
wss = null;
|
|
5114
5127
|
clients = /* @__PURE__ */ new Set();
|
|
@@ -5173,21 +5186,24 @@ var WebSocketManager = class {
|
|
|
5173
5186
|
if (payload && payload.control_id) {
|
|
5174
5187
|
const mapping = payload;
|
|
5175
5188
|
if (!mapping.uuid) {
|
|
5176
|
-
const
|
|
5177
|
-
mapping.uuid =
|
|
5189
|
+
const crypto3 = await import("crypto");
|
|
5190
|
+
mapping.uuid = crypto3.randomUUID();
|
|
5178
5191
|
}
|
|
5192
|
+
if (!mapping.hash || mapping.hash === "") {
|
|
5193
|
+
mapping.hash = crypto2.createHash("sha256").update(JSON.stringify(mapping)).digest("hex");
|
|
5194
|
+
}
|
|
5195
|
+
const compositeKey = `${mapping.control_id}:${mapping.hash}`;
|
|
5179
5196
|
await state.fileStore.saveMapping(mapping);
|
|
5180
|
-
const compositeKey = `${mapping.control_id}:${mapping.uuid}`;
|
|
5181
5197
|
state.mappingsCache.set(compositeKey, mapping);
|
|
5182
5198
|
const family = mapping.control_id.split("-")[0];
|
|
5183
5199
|
if (!state.mappingsByFamily.has(family)) {
|
|
5184
5200
|
state.mappingsByFamily.set(family, /* @__PURE__ */ new Set());
|
|
5185
5201
|
}
|
|
5186
|
-
state.mappingsByFamily.get(family)?.add(mapping.
|
|
5202
|
+
state.mappingsByFamily.get(family)?.add(mapping.hash);
|
|
5187
5203
|
if (!state.mappingsByControl.has(mapping.control_id)) {
|
|
5188
5204
|
state.mappingsByControl.set(mapping.control_id, /* @__PURE__ */ new Set());
|
|
5189
5205
|
}
|
|
5190
|
-
state.mappingsByControl.get(mapping.control_id)?.add(mapping.
|
|
5206
|
+
state.mappingsByControl.get(mapping.control_id)?.add(mapping.hash);
|
|
5191
5207
|
ws.send(
|
|
5192
5208
|
JSON.stringify({
|
|
5193
5209
|
type: "mapping-created",
|
|
@@ -5198,38 +5214,21 @@ var WebSocketManager = class {
|
|
|
5198
5214
|
}
|
|
5199
5215
|
break;
|
|
5200
5216
|
}
|
|
5201
|
-
case "update-mapping": {
|
|
5202
|
-
const state = getServerState();
|
|
5203
|
-
if (payload && payload.uuid) {
|
|
5204
|
-
const mapping = payload;
|
|
5205
|
-
await state.fileStore.saveMapping(mapping);
|
|
5206
|
-
const compositeKey = `${mapping.control_id}:${mapping.uuid}`;
|
|
5207
|
-
state.mappingsCache.set(compositeKey, mapping);
|
|
5208
|
-
ws.send(
|
|
5209
|
-
JSON.stringify({
|
|
5210
|
-
type: "mapping-updated",
|
|
5211
|
-
payload: { uuid: mapping.uuid, success: true }
|
|
5212
|
-
})
|
|
5213
|
-
);
|
|
5214
|
-
this.broadcastState();
|
|
5215
|
-
}
|
|
5216
|
-
break;
|
|
5217
|
-
}
|
|
5218
5217
|
case "delete-mapping": {
|
|
5219
5218
|
const state = getServerState();
|
|
5220
|
-
if (payload && payload.
|
|
5221
|
-
const
|
|
5222
|
-
const mapping = state.mappingsCache.get(
|
|
5219
|
+
if (payload && payload.composite_key) {
|
|
5220
|
+
const composite_key = payload.composite_key;
|
|
5221
|
+
const mapping = state.mappingsCache.get(composite_key);
|
|
5223
5222
|
if (mapping) {
|
|
5224
|
-
await state.fileStore.deleteMapping(
|
|
5225
|
-
state.mappingsCache.delete(
|
|
5223
|
+
await state.fileStore.deleteMapping(composite_key);
|
|
5224
|
+
state.mappingsCache.delete(composite_key);
|
|
5226
5225
|
const family = mapping.control_id.split("-")[0];
|
|
5227
|
-
state.mappingsByFamily.get(family)?.delete(
|
|
5228
|
-
state.mappingsByControl.get(mapping.control_id)?.delete(
|
|
5226
|
+
state.mappingsByFamily.get(family)?.delete(mapping.hash);
|
|
5227
|
+
state.mappingsByControl.get(mapping.control_id)?.delete(mapping.hash);
|
|
5229
5228
|
ws.send(
|
|
5230
5229
|
JSON.stringify({
|
|
5231
5230
|
type: "mapping-deleted",
|
|
5232
|
-
payload: {
|
|
5231
|
+
payload: { hash: mapping.hash, success: true }
|
|
5233
5232
|
})
|
|
5234
5233
|
);
|
|
5235
5234
|
this.broadcastState();
|
|
@@ -5706,6 +5705,7 @@ var WebSocketManager = class {
|
|
|
5706
5705
|
var wsManager = new WebSocketManager();
|
|
5707
5706
|
|
|
5708
5707
|
// cli/server/server.ts
|
|
5708
|
+
import { createHash as createHash4 } from "crypto";
|
|
5709
5709
|
var __filename = fileURLToPath(import.meta.url);
|
|
5710
5710
|
var __dirname = dirname2(__filename);
|
|
5711
5711
|
async function createServer(options) {
|
|
@@ -5731,6 +5731,11 @@ async function createServer(options) {
|
|
|
5731
5731
|
app.get("/*splat", (req, res) => {
|
|
5732
5732
|
res.sendFile("index.html", { root: distPath });
|
|
5733
5733
|
});
|
|
5734
|
+
app.post("/hash", (req, res) => {
|
|
5735
|
+
delete req.body.hash;
|
|
5736
|
+
const hash = createHash4("sha256").update(JSON.stringify(req.body)).digest("hex");
|
|
5737
|
+
res.status(200).json({ hash });
|
|
5738
|
+
});
|
|
5734
5739
|
const httpServer = createHttpServer(app);
|
|
5735
5740
|
wsManager.initialize(httpServer);
|
|
5736
5741
|
return {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// cli/server/serverState.ts
|
|
2
2
|
import { join as join3 } from "path";
|
|
3
|
+
import { createHash as createHash2 } from "crypto";
|
|
3
4
|
|
|
4
5
|
// cli/utils/debug.ts
|
|
5
6
|
var debugEnabled = false;
|
|
@@ -88,6 +89,7 @@ function getControlId(control, baseDir) {
|
|
|
88
89
|
}
|
|
89
90
|
|
|
90
91
|
// cli/server/infrastructure/fileStore.ts
|
|
92
|
+
import { createHash } from "crypto";
|
|
91
93
|
var FileStore = class {
|
|
92
94
|
baseDir;
|
|
93
95
|
controlsDir;
|
|
@@ -380,6 +382,10 @@ var FileStore = class {
|
|
|
380
382
|
const content = readFileSync2(mappingFile, "utf8");
|
|
381
383
|
const parsed = yaml2.load(content);
|
|
382
384
|
if (Array.isArray(parsed)) {
|
|
385
|
+
parsed.forEach((mapping) => {
|
|
386
|
+
mapping.hash = createHash("sha256").update(JSON.stringify(mapping)).digest("hex");
|
|
387
|
+
return mapping;
|
|
388
|
+
});
|
|
383
389
|
mappings.push(...parsed);
|
|
384
390
|
}
|
|
385
391
|
} catch (error) {
|
|
@@ -414,12 +420,9 @@ var FileStore = class {
|
|
|
414
420
|
existingMappings = [];
|
|
415
421
|
}
|
|
416
422
|
}
|
|
417
|
-
const
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
} else {
|
|
421
|
-
existingMappings.push(mapping);
|
|
422
|
-
}
|
|
423
|
+
const cleanMapping = { ...mapping };
|
|
424
|
+
delete cleanMapping.hash;
|
|
425
|
+
existingMappings.push(cleanMapping);
|
|
423
426
|
try {
|
|
424
427
|
const yamlContent = yaml2.dump(existingMappings, {
|
|
425
428
|
indent: 2,
|
|
@@ -444,7 +447,8 @@ var FileStore = class {
|
|
|
444
447
|
let mappings = yaml2.load(content) || [];
|
|
445
448
|
const originalLength = mappings.length;
|
|
446
449
|
mappings = mappings.filter((m) => {
|
|
447
|
-
|
|
450
|
+
const hash = createHash("sha256").update(JSON.stringify(m)).digest("hex");
|
|
451
|
+
return `${m.control_id}:${hash}` !== compositeKey;
|
|
448
452
|
});
|
|
449
453
|
if (mappings.length < originalLength) {
|
|
450
454
|
if (mappings.length === 0) {
|
|
@@ -507,8 +511,13 @@ var FileStore = class {
|
|
|
507
511
|
if (!existsSync2(familyDir)) {
|
|
508
512
|
mkdirSync(familyDir, { recursive: true });
|
|
509
513
|
}
|
|
514
|
+
const cleanMappings = controlMappings.map((m) => {
|
|
515
|
+
const clean = { ...m };
|
|
516
|
+
delete clean.hash;
|
|
517
|
+
return clean;
|
|
518
|
+
});
|
|
510
519
|
try {
|
|
511
|
-
const yamlContent = yaml2.dump(
|
|
520
|
+
const yamlContent = yaml2.dump(cleanMappings, {
|
|
512
521
|
indent: 2,
|
|
513
522
|
lineWidth: -1,
|
|
514
523
|
noRefs: true
|
|
@@ -1447,11 +1456,11 @@ function addMappingToIndexes(mapping) {
|
|
|
1447
1456
|
if (!state.mappingsByFamily.has(family)) {
|
|
1448
1457
|
state.mappingsByFamily.set(family, /* @__PURE__ */ new Set());
|
|
1449
1458
|
}
|
|
1450
|
-
state.mappingsByFamily.get(family).add(mapping.
|
|
1459
|
+
state.mappingsByFamily.get(family).add(mapping.hash);
|
|
1451
1460
|
if (!state.mappingsByControl.has(mapping.control_id)) {
|
|
1452
1461
|
state.mappingsByControl.set(mapping.control_id, /* @__PURE__ */ new Set());
|
|
1453
1462
|
}
|
|
1454
|
-
state.mappingsByControl.get(mapping.control_id).add(mapping.
|
|
1463
|
+
state.mappingsByControl.get(mapping.control_id).add(mapping.hash);
|
|
1455
1464
|
}
|
|
1456
1465
|
async function loadAllData() {
|
|
1457
1466
|
const state = getServerState();
|
|
@@ -1465,7 +1474,10 @@ async function loadAllData() {
|
|
|
1465
1474
|
debug(`Loaded ${controls.length} controls from individual files`);
|
|
1466
1475
|
const mappings = await state.fileStore.loadMappings();
|
|
1467
1476
|
for (const mapping of mappings) {
|
|
1468
|
-
|
|
1477
|
+
if (!mapping.hash) {
|
|
1478
|
+
mapping.hash = createHash2("sha256").update(JSON.stringify(mapping)).digest("hex");
|
|
1479
|
+
}
|
|
1480
|
+
const compositeKey = `${mapping.control_id}:${mapping.hash}`;
|
|
1469
1481
|
state.mappingsCache.set(compositeKey, mapping);
|
|
1470
1482
|
addMappingToIndexes(mapping);
|
|
1471
1483
|
}
|