dsh-pentester 2.2.0 → 2.4.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/lib/index.js CHANGED
@@ -1050,7 +1050,7 @@ function readJsonFile(file) {
1050
1050
  return;
1051
1051
  }
1052
1052
  }
1053
- function readJsonArray(file) {
1053
+ function readJsonArray$1(file) {
1054
1054
  const parsed = readJsonFile(file);
1055
1055
  if (Array.isArray(parsed)) return parsed.filter((entry) => entry !== null && typeof entry === "object");
1056
1056
  return [];
@@ -1066,7 +1066,7 @@ async function promoteAssets(ctx, stage, delegationDirs, delegationIds) {
1066
1066
  const assetMap = /* @__PURE__ */ new Map();
1067
1067
  for (let index = 0; index < delegationDirs.length; index += 1) {
1068
1068
  const delegationId = delegationIds[index] ?? `D-${String(index + 1).padStart(3, "0")}`;
1069
- const entries = readJsonArray(join(delegationDirs[index], "assets.json"));
1069
+ const entries = readJsonArray$1(join(delegationDirs[index], "artifacts", "assets.json"));
1070
1070
  for (const entry of entries) {
1071
1071
  const key = assetKey(entry);
1072
1072
  if (key === "") continue;
@@ -1082,7 +1082,7 @@ async function promoteAssets(ctx, stage, delegationDirs, delegationIds) {
1082
1082
  const serviceMap = /* @__PURE__ */ new Map();
1083
1083
  for (let index = 0; index < delegationDirs.length; index += 1) {
1084
1084
  const delegationId = delegationIds[index] ?? `D-${String(index + 1).padStart(3, "0")}`;
1085
- const entries = readJsonArray(join(delegationDirs[index], "services.json"));
1085
+ const entries = readJsonArray$1(join(delegationDirs[index], "artifacts", "services.json"));
1086
1086
  for (const entry of entries) {
1087
1087
  const key = serviceKey(entry);
1088
1088
  if (key === "") continue;
@@ -1115,46 +1115,92 @@ async function promoteAssets(ctx, stage, delegationDirs, delegationIds) {
1115
1115
  */
1116
1116
  /**
1117
1117
  * Aggregate findings from every delegation directory in the completed stage,
1118
- * index them in `findings/findings.json`, and write each delegation's detail
1119
- * as `findings/<delegationId>-findings.md`.
1118
+ * index them in `findings/findings.json`, and write each finding's detail
1119
+ * as `findings/<findingId>-findings.md` with a provenance header.
1120
1120
  *
1121
- * If a delegation produces `artifacts/findings.md`, its content is copied
1122
- * into the canonical detail file with a provenance header.
1121
+ * Worker contract:
1122
+ * artifacts/findings.json — structured array of finding entries
1123
+ * artifacts/findings.md — human-readable detail (used as fallback if
1124
+ * no structured entries exist)
1123
1125
  */
1124
1126
  async function promoteFindings(ctx, stage, delegationDirs, delegationIds) {
1125
1127
  const root = ctx.targetRoot;
1126
1128
  const findingsDir = join(root, "findings");
1127
1129
  await mkdir(findingsDir, { recursive: true });
1128
1130
  const updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1129
- const entries = [];
1130
1131
  const existingIndex = loadExistingIndex(findingsDir);
1132
+ const entries = [];
1133
+ const touchedDelegationIds = /* @__PURE__ */ new Set();
1131
1134
  for (let index = 0; index < delegationDirs.length; index += 1) {
1132
1135
  const delegationId = delegationIds[index] ?? `D-${String(index + 1).padStart(3, "0")}`;
1133
1136
  const delegationDir = delegationDirs[index];
1134
- const sourceFile = join(delegationDir, "findings.md");
1135
- if (!existsSync(sourceFile)) continue;
1136
- const content = readFileSync(sourceFile, "utf8").trim();
1137
- if (content === "") continue;
1138
- const detailFile = `${delegationId}-findings.md`;
1139
- const detailPath = join(findingsDir, detailFile);
1140
- await writeFile(detailPath, `# ${delegationId} Findings\n\n**Source Stage**: ${stage}\n**Updated**: ${updatedAt}\n\n---\n\n${content}\n`, "utf8");
1141
- const existing = existingIndex.get(delegationId);
1142
- if (existing !== void 0) existingIndex.set(delegationId, {
1143
- ...existing,
1144
- updatedAt,
1145
- sourceStage: stage
1146
- });
1147
- else entries.push({
1148
- sourceDelegationId: delegationId,
1149
- sourceStage: stage,
1150
- detailFile,
1151
- updatedAt
1152
- });
1137
+ touchedDelegationIds.add(delegationId);
1138
+ const structuredFile = join(delegationDir, "artifacts", "findings.json");
1139
+ const detailFile = join(delegationDir, "artifacts", "findings.md");
1140
+ const structured = readJsonArray(structuredFile);
1141
+ if (structured.length > 0) for (const entry of structured) {
1142
+ const findingId = typeof entry.id === "string" && entry.id.length > 0 ? entry.id : `${delegationId}-${crypto.randomUUID().slice(0, 8)}`;
1143
+ const title = typeof entry.title === "string" && entry.title.length > 0 ? entry.title : findingId;
1144
+ const severity = typeof entry.severity === "string" ? entry.severity : "info";
1145
+ const affectedAsset = typeof entry.affectedAsset === "string" && entry.affectedAsset.length > 0 ? entry.affectedAsset : void 0;
1146
+ const confidence = typeof entry.confidence === "string" && entry.confidence.length > 0 ? entry.confidence : void 0;
1147
+ const detailFileName = `${findingId}-findings.md`;
1148
+ const detailPath = join(findingsDir, detailFileName);
1149
+ let body = "";
1150
+ if (existsSync(detailFile)) body = readFileSync(detailFile, "utf8").trim();
1151
+ if (body === "") {
1152
+ body = `# ${title}\n\n**Severity**: ${severity}\n\n${affectedAsset !== void 0 ? `**Affected Asset**: ${affectedAsset}\n\n` : ""}`;
1153
+ if (typeof entry.description === "string" && entry.description.length > 0) body += `${entry.description}\n`;
1154
+ }
1155
+ await writeFile(detailPath, `# ${title}\n\n**Source Stage**: ${stage}\n**Source Delegation**: ${delegationId}\n**Updated**: ${updatedAt}\n\n---\n\n${body}\n`, "utf8");
1156
+ entries.push({
1157
+ id: findingId,
1158
+ title,
1159
+ severity,
1160
+ ...affectedAsset !== void 0 ? { affectedAsset } : {},
1161
+ ...confidence !== void 0 ? { confidence } : {},
1162
+ sourceDelegationId: delegationId,
1163
+ sourceStage: stage,
1164
+ detailFile: detailFileName,
1165
+ updatedAt
1166
+ });
1167
+ }
1168
+ else if (existsSync(detailFile)) {
1169
+ const content = readFileSync(detailFile, "utf8").trim();
1170
+ if (content !== "") {
1171
+ const detailFileName = `${delegationId}-findings.md`;
1172
+ const detailPath = join(findingsDir, detailFileName);
1173
+ await writeFile(detailPath, `# ${delegationId} Findings\n\n**Source Stage**: ${stage}\n**Updated**: ${updatedAt}\n\n---\n\n${content}\n`, "utf8");
1174
+ entries.push({
1175
+ id: delegationId,
1176
+ title: `${delegationId} Findings`,
1177
+ severity: "info",
1178
+ sourceDelegationId: delegationId,
1179
+ sourceStage: stage,
1180
+ detailFile: detailFileName,
1181
+ updatedAt
1182
+ });
1183
+ }
1184
+ }
1153
1185
  }
1154
- for (const [id, entry] of existingIndex) if (!delegationIds.includes(id)) entries.push(entry);
1155
- entries.sort((a, b) => a.sourceDelegationId.localeCompare(b.sourceDelegationId));
1186
+ for (const [id, entry] of existingIndex) if (!touchedDelegationIds.has(entry.sourceDelegationId)) entries.push(entry);
1187
+ entries.sort((a, b) => {
1188
+ const cmp = a.sourceDelegationId.localeCompare(b.sourceDelegationId);
1189
+ if (cmp !== 0) return cmp;
1190
+ return a.id.localeCompare(b.id);
1191
+ });
1156
1192
  if (entries.length > 0) await writeFile(join(findingsDir, "findings.json"), `${JSON.stringify(entries, null, 2)}\n`, "utf8");
1157
1193
  }
1194
+ function readJsonArray(file) {
1195
+ if (!existsSync(file)) return [];
1196
+ try {
1197
+ const parsed = JSON.parse(readFileSync(file, "utf8"));
1198
+ if (Array.isArray(parsed)) return parsed.filter((entry) => entry !== null && typeof entry === "object");
1199
+ return [];
1200
+ } catch {
1201
+ return [];
1202
+ }
1203
+ }
1158
1204
  function loadExistingIndex(findingsDir) {
1159
1205
  const file = join(findingsDir, "findings.json");
1160
1206
  if (!existsSync(file)) return /* @__PURE__ */ new Map();
@@ -1162,9 +1208,10 @@ function loadExistingIndex(findingsDir) {
1162
1208
  const parsed = JSON.parse(readFileSync(file, "utf8"));
1163
1209
  if (!Array.isArray(parsed)) return /* @__PURE__ */ new Map();
1164
1210
  const map = /* @__PURE__ */ new Map();
1165
- for (const entry of parsed) if (entry !== null && typeof entry === "object" && typeof entry.sourceDelegationId === "string") {
1166
- const id = entry.sourceDelegationId;
1167
- map.set(id, entry);
1211
+ for (const entry of parsed) if (entry !== null && typeof entry === "object") {
1212
+ const e = entry;
1213
+ const id = typeof e.id === "string" ? e.id : "";
1214
+ if (id !== "") map.set(id, e);
1168
1215
  }
1169
1216
  return map;
1170
1217
  } catch {
@@ -3474,17 +3521,29 @@ async function parseFindingsIndex(ctx) {
3474
3521
  const raw = await readFile(indexFile, "utf8");
3475
3522
  const entries = JSON.parse(raw);
3476
3523
  if (!Array.isArray(entries)) return [];
3524
+ const validSeverities = /* @__PURE__ */ new Set([
3525
+ "critical",
3526
+ "high",
3527
+ "medium",
3528
+ "low",
3529
+ "info"
3530
+ ]);
3477
3531
  return entries.filter((e) => e !== null && typeof e === "object").map((e) => {
3478
3532
  const detailFile = typeof e.detailFile === "string" ? e.detailFile : "";
3479
- const id = detailFile.replace(/-findings\.md$/, "");
3533
+ const fallbackId = detailFile.replace(/-findings\.md$/, "");
3534
+ const id = typeof e.id === "string" && e.id.length > 0 ? e.id : fallbackId;
3535
+ const rawSeverity = typeof e.severity === "string" ? e.severity : "";
3536
+ const severity = validSeverities.has(rawSeverity) ? rawSeverity : "info";
3480
3537
  return {
3481
3538
  id,
3482
- title: id,
3483
- severity: typeof e.severity === "string" ? e.severity : "info",
3539
+ title: typeof e.title === "string" && e.title.length > 0 ? e.title : id,
3540
+ severity,
3484
3541
  sourceStage: typeof e.sourceStage === "string" ? e.sourceStage : "",
3485
3542
  sourceDelegationId: typeof e.sourceDelegationId === "string" ? e.sourceDelegationId : "",
3486
3543
  detailFile,
3487
- updatedAt: typeof e.updatedAt === "string" ? e.updatedAt : ""
3544
+ updatedAt: typeof e.updatedAt === "string" ? e.updatedAt : "",
3545
+ ...typeof e.affectedAsset === "string" && e.affectedAsset.length > 0 ? { affectedAsset: e.affectedAsset } : {},
3546
+ ...typeof e.confidence === "string" && e.confidence.length > 0 ? { confidence: e.confidence } : {}
3488
3547
  };
3489
3548
  });
3490
3549
  } catch {
@@ -4074,9 +4133,9 @@ async function resolveBinding(deps, sessionId) {
4074
4133
  * Generated by scripts/build-id.mjs before each build.
4075
4134
  * Both host (tsdown) and client (esbuild) import this module.
4076
4135
  *
4077
- * Format: version+shortSha (e.g. "1.1.0+391bfd6")
4136
+ * Format: version+shortSha
4078
4137
  */
4079
- const BUILD_ID = "2.2.0+478786d";
4138
+ const BUILD_ID = "2.4.0+3a85fc6";
4080
4139
  //#endregion
4081
4140
  //#region src/ui-view/invocations.ts
4082
4141
  /**