dsh-pentester 0.0.1 → 1.0.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.
Files changed (62) hide show
  1. package/README.md +39 -5
  2. package/agents/impact/profile.yml +19 -0
  3. package/agents/recon/profile.yml +23 -0
  4. package/agents/reporting/REPORT_TEMPLATE.md +333 -0
  5. package/agents/reporting/profile.yml +45 -0
  6. package/agents/threat-model/profile.yml +19 -0
  7. package/agents/validation/profile.yml +20 -0
  8. package/agents/vulnerability/profile.yml +19 -0
  9. package/agents/web/profile.yml +19 -0
  10. package/cordis.dev.patch.yml +16 -0
  11. package/cordis.patch.yml +2 -0
  12. package/docker/README.md +60 -0
  13. package/docker/kali/Dockerfile +881 -0
  14. package/docker/kali/README.md +104 -0
  15. package/docker/kali/REPORT_TEMPLATE.md +333 -0
  16. package/docker/kali/TOOL_PROMPT.md +362 -0
  17. package/docker/kali/bin/clone-kb +39 -0
  18. package/docker/kali/bin/entrypoint.sh +9 -0
  19. package/docker/kali/bin/gen-tools-json.sh +246 -0
  20. package/docker/kali/bin/record-traffic.sh +32 -0
  21. package/docker/kali/bin/tool-info +31 -0
  22. package/docker/kali/bin/tool-list +17 -0
  23. package/lib/catalog-BmeOyr6n.js +231 -0
  24. package/lib/catalog-BmeOyr6n.js.map +1 -0
  25. package/lib/catalog-DhE_r18k.js +231 -0
  26. package/lib/catalog-DhE_r18k.js.map +1 -0
  27. package/lib/client.js +15234 -0
  28. package/lib/client.js.map +7 -0
  29. package/lib/container-listing-BI6Xj8l2.js +56 -0
  30. package/lib/container-listing-BI6Xj8l2.js.map +1 -0
  31. package/lib/container-listing-BWZoBnN_.js +56 -0
  32. package/lib/container-listing-BWZoBnN_.js.map +1 -0
  33. package/lib/container-listing-CE5t-Y_H.js +56 -0
  34. package/lib/container-listing-CE5t-Y_H.js.map +1 -0
  35. package/lib/container-listing-DZPBvrLD.js +56 -0
  36. package/lib/container-listing-DZPBvrLD.js.map +1 -0
  37. package/lib/docker-tar-_wf8UrSj.js +66 -0
  38. package/lib/docker-tar-_wf8UrSj.js.map +1 -0
  39. package/lib/engagement-container-store-B9D8g0wq.js +641 -0
  40. package/lib/engagement-container-store-B9D8g0wq.js.map +1 -0
  41. package/lib/engagement-container-store-Cf-h0B4F.js +641 -0
  42. package/lib/engagement-container-store-Cf-h0B4F.js.map +1 -0
  43. package/lib/engagement-container-store-Cu1wuur1.js +639 -0
  44. package/lib/engagement-container-store-Cu1wuur1.js.map +1 -0
  45. package/lib/engagement-container-store-L_Gms-zi.js +632 -0
  46. package/lib/engagement-container-store-L_Gms-zi.js.map +1 -0
  47. package/lib/index.d.ts +41 -0
  48. package/lib/index.js +3200 -0
  49. package/lib/index.js.map +1 -0
  50. package/lib/model-CZoiogVs.js +149 -0
  51. package/lib/model-CZoiogVs.js.map +1 -0
  52. package/lib/model-DKZ5Rjik.js +145 -0
  53. package/lib/model-DKZ5Rjik.js.map +1 -0
  54. package/lib/worker-events-jFvaBp9x.js +351 -0
  55. package/lib/worker-events-jFvaBp9x.js.map +1 -0
  56. package/lib/worker-events-juSf0EDW.js +347 -0
  57. package/lib/worker-events-juSf0EDW.js.map +1 -0
  58. package/package.json +89 -8
  59. package/presets/pentester/agent.cordis.yml +194 -0
  60. package/presets/pentester/preset.yml +2 -0
  61. package/presets/pentester/run-state.mjs +210 -0
  62. package/index.js +0 -5
@@ -0,0 +1,56 @@
1
+ import { a as PENTESTER_RUNTIME_PROFILE_LABEL, n as PENTESTER_ENGAGEMENT_LABEL } from "./engagement-container-store-Cf-h0B4F.js";
2
+ //#region src/tools/container-listing.ts
3
+ /**
4
+ * Container listing helpers used by the P0-B GC. The engine's
5
+ * `listContainers` returns id + labels + name + image; filtering by the
6
+ * dsh.pentester.* labels is done here so the GC never parses container names
7
+ * for ownership (labels are the single source of truth).
8
+ */
9
+ async function listContainersByLabel(engine, label, value) {
10
+ const containers = await engine.listContainers({
11
+ label: `${label}=${value}`,
12
+ all: true
13
+ });
14
+ const out = [];
15
+ for (const container of containers) {
16
+ const engagementId = container.labels?.[PENTESTER_ENGAGEMENT_LABEL];
17
+ const profile = container.labels?.[PENTESTER_RUNTIME_PROFILE_LABEL];
18
+ if (engagementId === void 0 || profile === void 0) continue;
19
+ out.push({
20
+ containerId: container.id,
21
+ engagementId,
22
+ profile,
23
+ name: container.name
24
+ });
25
+ }
26
+ return out;
27
+ }
28
+ /**
29
+ * Legacy candidates from the OLD 1-AgentRun=1-container scheme:
30
+ * - image == dsh-pentester/kali:latest
31
+ * - name matches `dsh-<engagement>--<run>--<toolbox>` (no managed label)
32
+ * Ownership is confirmed only by the caller (engagement dir exists); anything
33
+ * ambiguous is reported, never deleted.
34
+ */
35
+ const LEGACY_NAME = /^dsh-([a-zA-Z0-9_.-]{8,40})--[a-zA-Z0-9_.-]{8,40}--[a-zA-Z0-9_.-]{1,24}$/;
36
+ async function listLegacyPentesterContainers(engine) {
37
+ const containers = await engine.listContainers({ all: true });
38
+ const out = [];
39
+ for (const container of containers) {
40
+ if (container.labels?.["dsh.pentester.managed"] === "true") continue;
41
+ if (!container.image.includes("dsh-pentester/kali")) continue;
42
+ const match = LEGACY_NAME.exec(container.name);
43
+ if (match === null) continue;
44
+ out.push({
45
+ containerId: container.id,
46
+ name: container.name,
47
+ image: container.image,
48
+ engagementId: match[1]
49
+ });
50
+ }
51
+ return out;
52
+ }
53
+ //#endregion
54
+ export { listContainersByLabel, listLegacyPentesterContainers };
55
+
56
+ //# sourceMappingURL=container-listing-BI6Xj8l2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"container-listing-BI6Xj8l2.js","names":[],"sources":["../src/tools/container-listing.ts"],"sourcesContent":["import type { DockerEngineClient } from './docker-engine.js'\nimport {\n PENTESTER_ENGAGEMENT_LABEL,\n PENTESTER_MANAGED_LABEL,\n PENTESTER_MANAGED_TRUE,\n PENTESTER_RUNTIME_PROFILE_LABEL,\n} from './engagement-container-store.js'\nimport type { LegacyContainerCandidate, ManagedContainerEntry } from './container-gc.js'\n\n/**\n * Container listing helpers used by the P0-B GC. The engine's\n * `listContainers` returns id + labels + name + image; filtering by the\n * dsh.pentester.* labels is done here so the GC never parses container names\n * for ownership (labels are the single source of truth).\n */\n\nexport async function listContainersByLabel(\n engine: DockerEngineClient,\n label: string,\n value: string,\n): Promise<readonly ManagedContainerEntry[]> {\n const containers = await engine.listContainers({ label: `${label}=${value}`, all: true })\n const out: ManagedContainerEntry[] = []\n for (const container of containers) {\n const engagementId = container.labels?.[PENTESTER_ENGAGEMENT_LABEL]\n const profile = container.labels?.[PENTESTER_RUNTIME_PROFILE_LABEL]\n if (engagementId === undefined || profile === undefined) {\n continue\n }\n out.push({ containerId: container.id, engagementId, profile, name: container.name })\n }\n return out\n}\n\n/**\n * Legacy candidates from the OLD 1-AgentRun=1-container scheme:\n * - image == dsh-pentester/kali:latest\n * - name matches `dsh-<engagement>--<run>--<toolbox>` (no managed label)\n * Ownership is confirmed only by the caller (engagement dir exists); anything\n * ambiguous is reported, never deleted.\n */\nconst LEGACY_NAME = /^dsh-([a-zA-Z0-9_.-]{8,40})--[a-zA-Z0-9_.-]{8,40}--[a-zA-Z0-9_.-]{1,24}$/\n\nexport async function listLegacyPentesterContainers(\n engine: DockerEngineClient,\n): Promise<readonly LegacyContainerCandidate[]> {\n const containers = await engine.listContainers({ all: true })\n const out: LegacyContainerCandidate[] = []\n for (const container of containers) {\n if (container.labels?.[PENTESTER_MANAGED_LABEL] === PENTESTER_MANAGED_TRUE) {\n // Managed containers are handled by the new-scheme scan, never legacy.\n continue\n }\n if (!container.image.includes('dsh-pentester/kali')) {\n continue\n }\n const match = LEGACY_NAME.exec(container.name)\n if (match === null) {\n continue\n }\n out.push({\n containerId: container.id,\n name: container.name,\n image: container.image,\n engagementId: match[1],\n })\n }\n return out\n}\n"],"mappings":";;;;;;;;AAgBA,eAAsB,sBACpB,QACA,OACA,OAC2C;CAC3C,MAAM,aAAa,MAAM,OAAO,eAAe;EAAE,OAAO,GAAG,MAAM,GAAG;EAAS,KAAK;CAAK,CAAC;CACxF,MAAM,MAA+B,CAAC;CACtC,KAAK,MAAM,aAAa,YAAY;EAClC,MAAM,eAAe,UAAU,SAAS;EACxC,MAAM,UAAU,UAAU,SAAS;EACnC,IAAI,iBAAiB,KAAA,KAAa,YAAY,KAAA,GAC5C;EAEF,IAAI,KAAK;GAAE,aAAa,UAAU;GAAI;GAAc;GAAS,MAAM,UAAU;EAAK,CAAC;CACrF;CACA,OAAO;AACT;;;;;;;;AASA,MAAM,cAAc;AAEpB,eAAsB,8BACpB,QAC8C;CAC9C,MAAM,aAAa,MAAM,OAAO,eAAe,EAAE,KAAK,KAAK,CAAC;CAC5D,MAAM,MAAkC,CAAC;CACzC,KAAK,MAAM,aAAa,YAAY;EAClC,IAAI,UAAU,SAAA,6BAAA,QAEZ;EAEF,IAAI,CAAC,UAAU,MAAM,SAAS,oBAAoB,GAChD;EAEF,MAAM,QAAQ,YAAY,KAAK,UAAU,IAAI;EAC7C,IAAI,UAAU,MACZ;EAEF,IAAI,KAAK;GACP,aAAa,UAAU;GACvB,MAAM,UAAU;GAChB,OAAO,UAAU;GACjB,cAAc,MAAM;EACtB,CAAC;CACH;CACA,OAAO;AACT"}
@@ -0,0 +1,56 @@
1
+ import { a as PENTESTER_RUNTIME_PROFILE_LABEL, n as PENTESTER_ENGAGEMENT_LABEL } from "./engagement-container-store-L_Gms-zi.js";
2
+ //#region src/tools/container-listing.ts
3
+ /**
4
+ * Container listing helpers used by the P0-B GC. The engine's
5
+ * `listContainers` returns id + labels + name + image; filtering by the
6
+ * dsh.pentester.* labels is done here so the GC never parses container names
7
+ * for ownership (labels are the single source of truth).
8
+ */
9
+ async function listContainersByLabel(engine, label, value) {
10
+ const containers = await engine.listContainers({
11
+ label: `${label}=${value}`,
12
+ all: true
13
+ });
14
+ const out = [];
15
+ for (const container of containers) {
16
+ const engagementId = container.labels?.[PENTESTER_ENGAGEMENT_LABEL];
17
+ const profile = container.labels?.[PENTESTER_RUNTIME_PROFILE_LABEL];
18
+ if (engagementId === void 0 || profile === void 0) continue;
19
+ out.push({
20
+ containerId: container.id,
21
+ engagementId,
22
+ profile,
23
+ name: container.name
24
+ });
25
+ }
26
+ return out;
27
+ }
28
+ /**
29
+ * Legacy candidates from the OLD 1-AgentRun=1-container scheme:
30
+ * - image == dsh-pentester/kali:latest
31
+ * - name matches `dsh-<engagement>--<run>--<toolbox>` (no managed label)
32
+ * Ownership is confirmed only by the caller (engagement dir exists); anything
33
+ * ambiguous is reported, never deleted.
34
+ */
35
+ const LEGACY_NAME = /^dsh-([a-zA-Z0-9_.-]{8,40})--[a-zA-Z0-9_.-]{8,40}--[a-zA-Z0-9_.-]{1,24}$/;
36
+ async function listLegacyPentesterContainers(engine) {
37
+ const containers = await engine.listContainers({ all: true });
38
+ const out = [];
39
+ for (const container of containers) {
40
+ if (container.labels?.["dsh.pentester.managed"] === "true") continue;
41
+ if (!container.image.includes("dsh-pentester/kali")) continue;
42
+ const match = LEGACY_NAME.exec(container.name);
43
+ if (match === null) continue;
44
+ out.push({
45
+ containerId: container.id,
46
+ name: container.name,
47
+ image: container.image,
48
+ engagementId: match[1]
49
+ });
50
+ }
51
+ return out;
52
+ }
53
+ //#endregion
54
+ export { listContainersByLabel, listLegacyPentesterContainers };
55
+
56
+ //# sourceMappingURL=container-listing-BWZoBnN_.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"container-listing-BWZoBnN_.js","names":[],"sources":["../src/tools/container-listing.ts"],"sourcesContent":["import type { DockerEngineClient } from './docker-engine.js'\nimport {\n PENTESTER_ENGAGEMENT_LABEL,\n PENTESTER_MANAGED_LABEL,\n PENTESTER_MANAGED_TRUE,\n PENTESTER_RUNTIME_PROFILE_LABEL,\n} from './engagement-container-store.js'\nimport type { LegacyContainerCandidate, ManagedContainerEntry } from './container-gc.js'\n\n/**\n * Container listing helpers used by the P0-B GC. The engine's\n * `listContainers` returns id + labels + name + image; filtering by the\n * dsh.pentester.* labels is done here so the GC never parses container names\n * for ownership (labels are the single source of truth).\n */\n\nexport async function listContainersByLabel(\n engine: DockerEngineClient,\n label: string,\n value: string,\n): Promise<readonly ManagedContainerEntry[]> {\n const containers = await engine.listContainers({ label: `${label}=${value}`, all: true })\n const out: ManagedContainerEntry[] = []\n for (const container of containers) {\n const engagementId = container.labels?.[PENTESTER_ENGAGEMENT_LABEL]\n const profile = container.labels?.[PENTESTER_RUNTIME_PROFILE_LABEL]\n if (engagementId === undefined || profile === undefined) {\n continue\n }\n out.push({ containerId: container.id, engagementId, profile, name: container.name })\n }\n return out\n}\n\n/**\n * Legacy candidates from the OLD 1-AgentRun=1-container scheme:\n * - image == dsh-pentester/kali:latest\n * - name matches `dsh-<engagement>--<run>--<toolbox>` (no managed label)\n * Ownership is confirmed only by the caller (engagement dir exists); anything\n * ambiguous is reported, never deleted.\n */\nconst LEGACY_NAME = /^dsh-([a-zA-Z0-9_.-]{8,40})--[a-zA-Z0-9_.-]{8,40}--[a-zA-Z0-9_.-]{1,24}$/\n\nexport async function listLegacyPentesterContainers(\n engine: DockerEngineClient,\n): Promise<readonly LegacyContainerCandidate[]> {\n const containers = await engine.listContainers({ all: true })\n const out: LegacyContainerCandidate[] = []\n for (const container of containers) {\n if (container.labels?.[PENTESTER_MANAGED_LABEL] === PENTESTER_MANAGED_TRUE) {\n // Managed containers are handled by the new-scheme scan, never legacy.\n continue\n }\n if (!container.image.includes('dsh-pentester/kali')) {\n continue\n }\n const match = LEGACY_NAME.exec(container.name)\n if (match === null) {\n continue\n }\n out.push({\n containerId: container.id,\n name: container.name,\n image: container.image,\n engagementId: match[1],\n })\n }\n return out\n}\n"],"mappings":";;;;;;;;AAgBA,eAAsB,sBACpB,QACA,OACA,OAC2C;CAC3C,MAAM,aAAa,MAAM,OAAO,eAAe;EAAE,OAAO,GAAG,MAAM,GAAG;EAAS,KAAK;CAAK,CAAC;CACxF,MAAM,MAA+B,CAAC;CACtC,KAAK,MAAM,aAAa,YAAY;EAClC,MAAM,eAAe,UAAU,SAAS;EACxC,MAAM,UAAU,UAAU,SAAS;EACnC,IAAI,iBAAiB,KAAA,KAAa,YAAY,KAAA,GAC5C;EAEF,IAAI,KAAK;GAAE,aAAa,UAAU;GAAI;GAAc;GAAS,MAAM,UAAU;EAAK,CAAC;CACrF;CACA,OAAO;AACT;;;;;;;;AASA,MAAM,cAAc;AAEpB,eAAsB,8BACpB,QAC8C;CAC9C,MAAM,aAAa,MAAM,OAAO,eAAe,EAAE,KAAK,KAAK,CAAC;CAC5D,MAAM,MAAkC,CAAC;CACzC,KAAK,MAAM,aAAa,YAAY;EAClC,IAAI,UAAU,SAAA,6BAAA,QAEZ;EAEF,IAAI,CAAC,UAAU,MAAM,SAAS,oBAAoB,GAChD;EAEF,MAAM,QAAQ,YAAY,KAAK,UAAU,IAAI;EAC7C,IAAI,UAAU,MACZ;EAEF,IAAI,KAAK;GACP,aAAa,UAAU;GACvB,MAAM,UAAU;GAChB,OAAO,UAAU;GACjB,cAAc,MAAM;EACtB,CAAC;CACH;CACA,OAAO;AACT"}
@@ -0,0 +1,56 @@
1
+ import { a as PENTESTER_RUNTIME_PROFILE_LABEL, n as PENTESTER_ENGAGEMENT_LABEL } from "./engagement-container-store-Cu1wuur1.js";
2
+ //#region src/tools/container-listing.ts
3
+ /**
4
+ * Container listing helpers used by the P0-B GC. The engine's
5
+ * `listContainers` returns id + labels + name + image; filtering by the
6
+ * dsh.pentester.* labels is done here so the GC never parses container names
7
+ * for ownership (labels are the single source of truth).
8
+ */
9
+ async function listContainersByLabel(engine, label, value) {
10
+ const containers = await engine.listContainers({
11
+ label: `${label}=${value}`,
12
+ all: true
13
+ });
14
+ const out = [];
15
+ for (const container of containers) {
16
+ const engagementId = container.labels?.[PENTESTER_ENGAGEMENT_LABEL];
17
+ const profile = container.labels?.[PENTESTER_RUNTIME_PROFILE_LABEL];
18
+ if (engagementId === void 0 || profile === void 0) continue;
19
+ out.push({
20
+ containerId: container.id,
21
+ engagementId,
22
+ profile,
23
+ name: container.name
24
+ });
25
+ }
26
+ return out;
27
+ }
28
+ /**
29
+ * Legacy candidates from the OLD 1-AgentRun=1-container scheme:
30
+ * - image == dsh-pentester/kali:latest
31
+ * - name matches `dsh-<engagement>--<run>--<toolbox>` (no managed label)
32
+ * Ownership is confirmed only by the caller (engagement dir exists); anything
33
+ * ambiguous is reported, never deleted.
34
+ */
35
+ const LEGACY_NAME = /^dsh-([a-zA-Z0-9_.-]{8,40})--[a-zA-Z0-9_.-]{8,40}--[a-zA-Z0-9_.-]{1,24}$/;
36
+ async function listLegacyPentesterContainers(engine) {
37
+ const containers = await engine.listContainers({ all: true });
38
+ const out = [];
39
+ for (const container of containers) {
40
+ if (container.labels?.["dsh.pentester.managed"] === "true") continue;
41
+ if (!container.image.includes("dsh-pentester/kali")) continue;
42
+ const match = LEGACY_NAME.exec(container.name);
43
+ if (match === null) continue;
44
+ out.push({
45
+ containerId: container.id,
46
+ name: container.name,
47
+ image: container.image,
48
+ engagementId: match[1]
49
+ });
50
+ }
51
+ return out;
52
+ }
53
+ //#endregion
54
+ export { listContainersByLabel, listLegacyPentesterContainers };
55
+
56
+ //# sourceMappingURL=container-listing-CE5t-Y_H.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"container-listing-CE5t-Y_H.js","names":[],"sources":["../src/tools/container-listing.ts"],"sourcesContent":["import type { DockerEngineClient } from './docker-engine.js'\nimport {\n PENTESTER_ENGAGEMENT_LABEL,\n PENTESTER_MANAGED_LABEL,\n PENTESTER_MANAGED_TRUE,\n PENTESTER_RUNTIME_PROFILE_LABEL,\n} from './engagement-container-store.js'\nimport type { LegacyContainerCandidate, ManagedContainerEntry } from './container-gc.js'\n\n/**\n * Container listing helpers used by the P0-B GC. The engine's\n * `listContainers` returns id + labels + name + image; filtering by the\n * dsh.pentester.* labels is done here so the GC never parses container names\n * for ownership (labels are the single source of truth).\n */\n\nexport async function listContainersByLabel(\n engine: DockerEngineClient,\n label: string,\n value: string,\n): Promise<readonly ManagedContainerEntry[]> {\n const containers = await engine.listContainers({ label: `${label}=${value}`, all: true })\n const out: ManagedContainerEntry[] = []\n for (const container of containers) {\n const engagementId = container.labels?.[PENTESTER_ENGAGEMENT_LABEL]\n const profile = container.labels?.[PENTESTER_RUNTIME_PROFILE_LABEL]\n if (engagementId === undefined || profile === undefined) {\n continue\n }\n out.push({ containerId: container.id, engagementId, profile, name: container.name })\n }\n return out\n}\n\n/**\n * Legacy candidates from the OLD 1-AgentRun=1-container scheme:\n * - image == dsh-pentester/kali:latest\n * - name matches `dsh-<engagement>--<run>--<toolbox>` (no managed label)\n * Ownership is confirmed only by the caller (engagement dir exists); anything\n * ambiguous is reported, never deleted.\n */\nconst LEGACY_NAME = /^dsh-([a-zA-Z0-9_.-]{8,40})--[a-zA-Z0-9_.-]{8,40}--[a-zA-Z0-9_.-]{1,24}$/\n\nexport async function listLegacyPentesterContainers(\n engine: DockerEngineClient,\n): Promise<readonly LegacyContainerCandidate[]> {\n const containers = await engine.listContainers({ all: true })\n const out: LegacyContainerCandidate[] = []\n for (const container of containers) {\n if (container.labels?.[PENTESTER_MANAGED_LABEL] === PENTESTER_MANAGED_TRUE) {\n // Managed containers are handled by the new-scheme scan, never legacy.\n continue\n }\n if (!container.image.includes('dsh-pentester/kali')) {\n continue\n }\n const match = LEGACY_NAME.exec(container.name)\n if (match === null) {\n continue\n }\n out.push({\n containerId: container.id,\n name: container.name,\n image: container.image,\n engagementId: match[1],\n })\n }\n return out\n}\n"],"mappings":";;;;;;;;AAgBA,eAAsB,sBACpB,QACA,OACA,OAC2C;CAC3C,MAAM,aAAa,MAAM,OAAO,eAAe;EAAE,OAAO,GAAG,MAAM,GAAG;EAAS,KAAK;CAAK,CAAC;CACxF,MAAM,MAA+B,CAAC;CACtC,KAAK,MAAM,aAAa,YAAY;EAClC,MAAM,eAAe,UAAU,SAAS;EACxC,MAAM,UAAU,UAAU,SAAS;EACnC,IAAI,iBAAiB,KAAA,KAAa,YAAY,KAAA,GAC5C;EAEF,IAAI,KAAK;GAAE,aAAa,UAAU;GAAI;GAAc;GAAS,MAAM,UAAU;EAAK,CAAC;CACrF;CACA,OAAO;AACT;;;;;;;;AASA,MAAM,cAAc;AAEpB,eAAsB,8BACpB,QAC8C;CAC9C,MAAM,aAAa,MAAM,OAAO,eAAe,EAAE,KAAK,KAAK,CAAC;CAC5D,MAAM,MAAkC,CAAC;CACzC,KAAK,MAAM,aAAa,YAAY;EAClC,IAAI,UAAU,SAAA,6BAAA,QAEZ;EAEF,IAAI,CAAC,UAAU,MAAM,SAAS,oBAAoB,GAChD;EAEF,MAAM,QAAQ,YAAY,KAAK,UAAU,IAAI;EAC7C,IAAI,UAAU,MACZ;EAEF,IAAI,KAAK;GACP,aAAa,UAAU;GACvB,MAAM,UAAU;GAChB,OAAO,UAAU;GACjB,cAAc,MAAM;EACtB,CAAC;CACH;CACA,OAAO;AACT"}
@@ -0,0 +1,56 @@
1
+ import { a as PENTESTER_RUNTIME_PROFILE_LABEL, n as PENTESTER_ENGAGEMENT_LABEL } from "./engagement-container-store-B9D8g0wq.js";
2
+ //#region src/tools/container-listing.ts
3
+ /**
4
+ * Container listing helpers used by the P0-B GC. The engine's
5
+ * `listContainers` returns id + labels + name + image; filtering by the
6
+ * dsh.pentester.* labels is done here so the GC never parses container names
7
+ * for ownership (labels are the single source of truth).
8
+ */
9
+ async function listContainersByLabel(engine, label, value) {
10
+ const containers = await engine.listContainers({
11
+ label: `${label}=${value}`,
12
+ all: true
13
+ });
14
+ const out = [];
15
+ for (const container of containers) {
16
+ const engagementId = container.labels?.[PENTESTER_ENGAGEMENT_LABEL];
17
+ const profile = container.labels?.[PENTESTER_RUNTIME_PROFILE_LABEL];
18
+ if (engagementId === void 0 || profile === void 0) continue;
19
+ out.push({
20
+ containerId: container.id,
21
+ engagementId,
22
+ profile,
23
+ name: container.name
24
+ });
25
+ }
26
+ return out;
27
+ }
28
+ /**
29
+ * Legacy candidates from the OLD 1-AgentRun=1-container scheme:
30
+ * - image == dsh-pentester/kali:latest
31
+ * - name matches `dsh-<engagement>--<run>--<toolbox>` (no managed label)
32
+ * Ownership is confirmed only by the caller (engagement dir exists); anything
33
+ * ambiguous is reported, never deleted.
34
+ */
35
+ const LEGACY_NAME = /^dsh-([a-zA-Z0-9_.-]{8,40})--[a-zA-Z0-9_.-]{8,40}--[a-zA-Z0-9_.-]{1,24}$/;
36
+ async function listLegacyPentesterContainers(engine) {
37
+ const containers = await engine.listContainers({ all: true });
38
+ const out = [];
39
+ for (const container of containers) {
40
+ if (container.labels?.["dsh.pentester.managed"] === "true") continue;
41
+ if (!container.image.includes("dsh-pentester/kali")) continue;
42
+ const match = LEGACY_NAME.exec(container.name);
43
+ if (match === null) continue;
44
+ out.push({
45
+ containerId: container.id,
46
+ name: container.name,
47
+ image: container.image,
48
+ engagementId: match[1]
49
+ });
50
+ }
51
+ return out;
52
+ }
53
+ //#endregion
54
+ export { listContainersByLabel, listLegacyPentesterContainers };
55
+
56
+ //# sourceMappingURL=container-listing-DZPBvrLD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"container-listing-DZPBvrLD.js","names":[],"sources":["../src/tools/container-listing.ts"],"sourcesContent":["import type { DockerEngineClient } from './docker-engine.js'\nimport {\n PENTESTER_ENGAGEMENT_LABEL,\n PENTESTER_MANAGED_LABEL,\n PENTESTER_MANAGED_TRUE,\n PENTESTER_RUNTIME_PROFILE_LABEL,\n} from './engagement-container-store.js'\nimport type { LegacyContainerCandidate, ManagedContainerEntry } from './container-gc.js'\n\n/**\n * Container listing helpers used by the P0-B GC. The engine's\n * `listContainers` returns id + labels + name + image; filtering by the\n * dsh.pentester.* labels is done here so the GC never parses container names\n * for ownership (labels are the single source of truth).\n */\n\nexport async function listContainersByLabel(\n engine: DockerEngineClient,\n label: string,\n value: string,\n): Promise<readonly ManagedContainerEntry[]> {\n const containers = await engine.listContainers({ label: `${label}=${value}`, all: true })\n const out: ManagedContainerEntry[] = []\n for (const container of containers) {\n const engagementId = container.labels?.[PENTESTER_ENGAGEMENT_LABEL]\n const profile = container.labels?.[PENTESTER_RUNTIME_PROFILE_LABEL]\n if (engagementId === undefined || profile === undefined) {\n continue\n }\n out.push({ containerId: container.id, engagementId, profile, name: container.name })\n }\n return out\n}\n\n/**\n * Legacy candidates from the OLD 1-AgentRun=1-container scheme:\n * - image == dsh-pentester/kali:latest\n * - name matches `dsh-<engagement>--<run>--<toolbox>` (no managed label)\n * Ownership is confirmed only by the caller (engagement dir exists); anything\n * ambiguous is reported, never deleted.\n */\nconst LEGACY_NAME = /^dsh-([a-zA-Z0-9_.-]{8,40})--[a-zA-Z0-9_.-]{8,40}--[a-zA-Z0-9_.-]{1,24}$/\n\nexport async function listLegacyPentesterContainers(\n engine: DockerEngineClient,\n): Promise<readonly LegacyContainerCandidate[]> {\n const containers = await engine.listContainers({ all: true })\n const out: LegacyContainerCandidate[] = []\n for (const container of containers) {\n if (container.labels?.[PENTESTER_MANAGED_LABEL] === PENTESTER_MANAGED_TRUE) {\n // Managed containers are handled by the new-scheme scan, never legacy.\n continue\n }\n if (!container.image.includes('dsh-pentester/kali')) {\n continue\n }\n const match = LEGACY_NAME.exec(container.name)\n if (match === null) {\n continue\n }\n out.push({\n containerId: container.id,\n name: container.name,\n image: container.image,\n engagementId: match[1],\n })\n }\n return out\n}\n"],"mappings":";;;;;;;;AAgBA,eAAsB,sBACpB,QACA,OACA,OAC2C;CAC3C,MAAM,aAAa,MAAM,OAAO,eAAe;EAAE,OAAO,GAAG,MAAM,GAAG;EAAS,KAAK;CAAK,CAAC;CACxF,MAAM,MAA+B,CAAC;CACtC,KAAK,MAAM,aAAa,YAAY;EAClC,MAAM,eAAe,UAAU,SAAS;EACxC,MAAM,UAAU,UAAU,SAAS;EACnC,IAAI,iBAAiB,KAAA,KAAa,YAAY,KAAA,GAC5C;EAEF,IAAI,KAAK;GAAE,aAAa,UAAU;GAAI;GAAc;GAAS,MAAM,UAAU;EAAK,CAAC;CACrF;CACA,OAAO;AACT;;;;;;;;AASA,MAAM,cAAc;AAEpB,eAAsB,8BACpB,QAC8C;CAC9C,MAAM,aAAa,MAAM,OAAO,eAAe,EAAE,KAAK,KAAK,CAAC;CAC5D,MAAM,MAAkC,CAAC;CACzC,KAAK,MAAM,aAAa,YAAY;EAClC,IAAI,UAAU,SAAA,6BAAA,QAEZ;EAEF,IAAI,CAAC,UAAU,MAAM,SAAS,oBAAoB,GAChD;EAEF,MAAM,QAAQ,YAAY,KAAK,UAAU,IAAI;EAC7C,IAAI,UAAU,MACZ;EAEF,IAAI,KAAK;GACP,aAAa,UAAU;GACvB,MAAM,UAAU;GAChB,OAAO,UAAU;GACjB,cAAc,MAAM;EACtB,CAAC;CACH;CACA,OAAO;AACT"}
@@ -0,0 +1,66 @@
1
+ import { readFile, readdir, stat } from "node:fs/promises";
2
+ import { join, posix, relative } from "node:path";
3
+ //#region src/tools/docker-tar.ts
4
+ function octal(value, size) {
5
+ return value.toString(8).padStart(size - 1, "0") + "\0";
6
+ }
7
+ function checksum(header) {
8
+ let sum = 0;
9
+ for (const byte of header) sum += byte;
10
+ return sum;
11
+ }
12
+ function fileHeader(name, size, mode = 420) {
13
+ const header = Buffer.alloc(512, 0);
14
+ header.write(name, 0, 100, "utf8");
15
+ header.write(octal(mode, 8), 100, 8, "utf8");
16
+ header.write(octal(0, 8), 108, 8, "utf8");
17
+ header.write(octal(0, 8), 116, 8, "utf8");
18
+ header.write(octal(size, 12), 124, 12, "utf8");
19
+ header.write(octal(Math.floor(Date.now() / 1e3), 12), 136, 12, "utf8");
20
+ header.write(" ", 148, 8, "utf8");
21
+ header.write("0", 156, 1, "utf8");
22
+ header.write("ustar\0", 257, 6, "utf8");
23
+ header.write("00", 263, 2, "utf8");
24
+ const sum = checksum(header);
25
+ header.write(`${sum.toString(8).padStart(6, "0")}\0 `, 148, 8, "utf8");
26
+ return header;
27
+ }
28
+ function align512(size) {
29
+ return Math.ceil(size / 512) * 512;
30
+ }
31
+ async function packDockerContext(contextDir, dockerfileName) {
32
+ const files = [];
33
+ async function walk(dir) {
34
+ const entries = await readdir(dir, { withFileTypes: true });
35
+ for (const entry of entries) {
36
+ const full = join(dir, entry.name);
37
+ if (entry.isDirectory()) {
38
+ await walk(full);
39
+ continue;
40
+ }
41
+ if (!entry.isFile()) continue;
42
+ const info = await stat(full);
43
+ const name = posix.normalize(relative(contextDir, full).split("\\").join("/"));
44
+ files.push({
45
+ name,
46
+ data: await readFile(full),
47
+ mode: info.mode & 511
48
+ });
49
+ }
50
+ }
51
+ await walk(contextDir);
52
+ if (!files.some((file) => file.name === dockerfileName || file.name.endsWith(`/${dockerfileName}`))) throw new Error(`dockerfile ${dockerfileName} missing in ${contextDir}`);
53
+ const chunks = [];
54
+ for (const file of files) {
55
+ const header = fileHeader(file.name, file.data.length, file.mode);
56
+ const padded = Buffer.alloc(align512(file.data.length));
57
+ file.data.copy(padded);
58
+ chunks.push(header, padded);
59
+ }
60
+ chunks.push(Buffer.alloc(1024, 0));
61
+ return Buffer.concat(chunks);
62
+ }
63
+ //#endregion
64
+ export { packDockerContext };
65
+
66
+ //# sourceMappingURL=docker-tar-_wf8UrSj.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docker-tar-_wf8UrSj.js","names":[],"sources":["../src/tools/docker-tar.ts"],"sourcesContent":["import { readFile, readdir, stat } from 'node:fs/promises'\nimport { join, relative, posix } from 'node:path'\n\nfunction pad(value: string, size: number): Buffer {\n const buf = Buffer.alloc(size, 0)\n buf.write(value, 0, 'utf8')\n return buf\n}\n\nfunction octal(value: number, size: number): string {\n const body = value.toString(8)\n return body.padStart(size - 1, '0') + '\\0'\n}\n\nfunction checksum(header: Buffer): number {\n let sum = 0\n for (const byte of header) {\n sum += byte\n }\n return sum\n}\n\nfunction fileHeader(name: string, size: number, mode = 0o644): Buffer {\n const header = Buffer.alloc(512, 0)\n header.write(name, 0, 100, 'utf8')\n header.write(octal(mode, 8), 100, 8, 'utf8')\n header.write(octal(0, 8), 108, 8, 'utf8')\n header.write(octal(0, 8), 116, 8, 'utf8')\n header.write(octal(size, 12), 124, 12, 'utf8')\n header.write(octal(Math.floor(Date.now() / 1000), 12), 136, 12, 'utf8')\n header.write(' ', 148, 8, 'utf8')\n header.write('0', 156, 1, 'utf8')\n header.write('ustar\\0', 257, 6, 'utf8')\n header.write('00', 263, 2, 'utf8')\n const sum = checksum(header)\n header.write(`${sum.toString(8).padStart(6, '0')}\\0 `, 148, 8, 'utf8')\n return header\n}\n\nfunction align512(size: number): number {\n return Math.ceil(size / 512) * 512\n}\n\nexport async function packDockerContext(contextDir: string, dockerfileName: string): Promise<Buffer> {\n const files: Array<{ name: string; data: Buffer; mode: number }> = []\n async function walk(dir: string): Promise<void> {\n const entries = await readdir(dir, { withFileTypes: true })\n for (const entry of entries) {\n const full = join(dir, entry.name)\n if (entry.isDirectory()) {\n await walk(full)\n continue\n }\n if (!entry.isFile()) {\n continue\n }\n const info = await stat(full)\n const name = posix.normalize(relative(contextDir, full).split('\\\\').join('/'))\n files.push({ name, data: await readFile(full), mode: info.mode & 0o777 })\n }\n }\n await walk(contextDir)\n if (!files.some((file) => file.name === dockerfileName || file.name.endsWith(`/${dockerfileName}`))) {\n throw new Error(`dockerfile ${dockerfileName} missing in ${contextDir}`)\n }\n const chunks: Buffer[] = []\n for (const file of files) {\n const header = fileHeader(file.name, file.data.length, file.mode)\n const padded = Buffer.alloc(align512(file.data.length))\n file.data.copy(padded)\n chunks.push(header, padded)\n }\n chunks.push(Buffer.alloc(1024, 0))\n return Buffer.concat(chunks)\n}\n"],"mappings":";;;AASA,SAAS,MAAM,OAAe,MAAsB;CAElD,OADa,MAAM,SAAS,CAClB,CAAC,CAAC,SAAS,OAAO,GAAG,GAAG,IAAI;AACxC;AAEA,SAAS,SAAS,QAAwB;CACxC,IAAI,MAAM;CACV,KAAK,MAAM,QAAQ,QACjB,OAAO;CAET,OAAO;AACT;AAEA,SAAS,WAAW,MAAc,MAAc,OAAO,KAAe;CACpE,MAAM,SAAS,OAAO,MAAM,KAAK,CAAC;CAClC,OAAO,MAAM,MAAM,GAAG,KAAK,MAAM;CACjC,OAAO,MAAM,MAAM,MAAM,CAAC,GAAG,KAAK,GAAG,MAAM;CAC3C,OAAO,MAAM,MAAM,GAAG,CAAC,GAAG,KAAK,GAAG,MAAM;CACxC,OAAO,MAAM,MAAM,GAAG,CAAC,GAAG,KAAK,GAAG,MAAM;CACxC,OAAO,MAAM,MAAM,MAAM,EAAE,GAAG,KAAK,IAAI,MAAM;CAC7C,OAAO,MAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI,GAAG,EAAE,GAAG,KAAK,IAAI,MAAM;CACtE,OAAO,MAAM,YAAY,KAAK,GAAG,MAAM;CACvC,OAAO,MAAM,KAAK,KAAK,GAAG,MAAM;CAChC,OAAO,MAAM,WAAW,KAAK,GAAG,MAAM;CACtC,OAAO,MAAM,MAAM,KAAK,GAAG,MAAM;CACjC,MAAM,MAAM,SAAS,MAAM;CAC3B,OAAO,MAAM,GAAG,IAAI,SAAS,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG,EAAE,MAAM,KAAK,GAAG,MAAM;CACrE,OAAO;AACT;AAEA,SAAS,SAAS,MAAsB;CACtC,OAAO,KAAK,KAAK,OAAO,GAAG,IAAI;AACjC;AAEA,eAAsB,kBAAkB,YAAoB,gBAAyC;CACnG,MAAM,QAA6D,CAAC;CACpE,eAAe,KAAK,KAA4B;EAC9C,MAAM,UAAU,MAAM,QAAQ,KAAK,EAAE,eAAe,KAAK,CAAC;EAC1D,KAAK,MAAM,SAAS,SAAS;GAC3B,MAAM,OAAO,KAAK,KAAK,MAAM,IAAI;GACjC,IAAI,MAAM,YAAY,GAAG;IACvB,MAAM,KAAK,IAAI;IACf;GACF;GACA,IAAI,CAAC,MAAM,OAAO,GAChB;GAEF,MAAM,OAAO,MAAM,KAAK,IAAI;GAC5B,MAAM,OAAO,MAAM,UAAU,SAAS,YAAY,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,KAAK,GAAG,CAAC;GAC7E,MAAM,KAAK;IAAE;IAAM,MAAM,MAAM,SAAS,IAAI;IAAG,MAAM,KAAK,OAAO;GAAM,CAAC;EAC1E;CACF;CACA,MAAM,KAAK,UAAU;CACrB,IAAI,CAAC,MAAM,MAAM,SAAS,KAAK,SAAS,kBAAkB,KAAK,KAAK,SAAS,IAAI,gBAAgB,CAAC,GAChG,MAAM,IAAI,MAAM,cAAc,eAAe,cAAc,YAAY;CAEzE,MAAM,SAAmB,CAAC;CAC1B,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,SAAS,WAAW,KAAK,MAAM,KAAK,KAAK,QAAQ,KAAK,IAAI;EAChE,MAAM,SAAS,OAAO,MAAM,SAAS,KAAK,KAAK,MAAM,CAAC;EACtD,KAAK,KAAK,KAAK,MAAM;EACrB,OAAO,KAAK,QAAQ,MAAM;CAC5B;CACA,OAAO,KAAK,OAAO,MAAM,MAAM,CAAC,CAAC;CACjC,OAAO,OAAO,OAAO,MAAM;AAC7B"}