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.
- package/README.md +39 -5
- package/agents/impact/profile.yml +19 -0
- package/agents/recon/profile.yml +23 -0
- package/agents/reporting/REPORT_TEMPLATE.md +333 -0
- package/agents/reporting/profile.yml +45 -0
- package/agents/threat-model/profile.yml +19 -0
- package/agents/validation/profile.yml +20 -0
- package/agents/vulnerability/profile.yml +19 -0
- package/agents/web/profile.yml +19 -0
- package/cordis.dev.patch.yml +16 -0
- package/cordis.patch.yml +2 -0
- package/docker/README.md +60 -0
- package/docker/kali/Dockerfile +881 -0
- package/docker/kali/README.md +104 -0
- package/docker/kali/REPORT_TEMPLATE.md +333 -0
- package/docker/kali/TOOL_PROMPT.md +362 -0
- package/docker/kali/bin/clone-kb +39 -0
- package/docker/kali/bin/entrypoint.sh +9 -0
- package/docker/kali/bin/gen-tools-json.sh +246 -0
- package/docker/kali/bin/record-traffic.sh +32 -0
- package/docker/kali/bin/tool-info +31 -0
- package/docker/kali/bin/tool-list +17 -0
- package/lib/catalog-BmeOyr6n.js +231 -0
- package/lib/catalog-BmeOyr6n.js.map +1 -0
- package/lib/catalog-DhE_r18k.js +231 -0
- package/lib/catalog-DhE_r18k.js.map +1 -0
- package/lib/client.js +15234 -0
- package/lib/client.js.map +7 -0
- package/lib/container-listing-BI6Xj8l2.js +56 -0
- package/lib/container-listing-BI6Xj8l2.js.map +1 -0
- package/lib/container-listing-BWZoBnN_.js +56 -0
- package/lib/container-listing-BWZoBnN_.js.map +1 -0
- package/lib/container-listing-CE5t-Y_H.js +56 -0
- package/lib/container-listing-CE5t-Y_H.js.map +1 -0
- package/lib/container-listing-DZPBvrLD.js +56 -0
- package/lib/container-listing-DZPBvrLD.js.map +1 -0
- package/lib/docker-tar-_wf8UrSj.js +66 -0
- package/lib/docker-tar-_wf8UrSj.js.map +1 -0
- package/lib/engagement-container-store-B9D8g0wq.js +641 -0
- package/lib/engagement-container-store-B9D8g0wq.js.map +1 -0
- package/lib/engagement-container-store-Cf-h0B4F.js +641 -0
- package/lib/engagement-container-store-Cf-h0B4F.js.map +1 -0
- package/lib/engagement-container-store-Cu1wuur1.js +639 -0
- package/lib/engagement-container-store-Cu1wuur1.js.map +1 -0
- package/lib/engagement-container-store-L_Gms-zi.js +632 -0
- package/lib/engagement-container-store-L_Gms-zi.js.map +1 -0
- package/lib/index.d.ts +41 -0
- package/lib/index.js +3200 -0
- package/lib/index.js.map +1 -0
- package/lib/model-CZoiogVs.js +149 -0
- package/lib/model-CZoiogVs.js.map +1 -0
- package/lib/model-DKZ5Rjik.js +145 -0
- package/lib/model-DKZ5Rjik.js.map +1 -0
- package/lib/worker-events-jFvaBp9x.js +351 -0
- package/lib/worker-events-jFvaBp9x.js.map +1 -0
- package/lib/worker-events-juSf0EDW.js +347 -0
- package/lib/worker-events-juSf0EDW.js.map +1 -0
- package/package.json +89 -8
- package/presets/pentester/agent.cordis.yml +194 -0
- package/presets/pentester/preset.yml +2 -0
- package/presets/pentester/run-state.mjs +210 -0
- package/index.js +0 -5
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# record-traffic.sh — 全流量记录(只记录,不分析)。
|
|
3
|
+
#
|
|
4
|
+
# 由宿主在 run 容器启动后立即执行一次(幂等):
|
|
5
|
+
# 1. tcpdump -i any 全网卡 pcap 落盘(100MB × 20 卷轮转,覆盖全部流量)
|
|
6
|
+
# 2. mitmdump -w flows 记录经 :8080 代理的 HTTP(S) 明文流(可选层)
|
|
7
|
+
# 全部写入 /workspace/traffic/(共享 volume,自动同步到本地镜像目录)。
|
|
8
|
+
set -euo pipefail
|
|
9
|
+
|
|
10
|
+
DIR=/workspace/traffic
|
|
11
|
+
mkdir -p "$DIR/pcap" "$DIR/mitm"
|
|
12
|
+
|
|
13
|
+
# 幂等:已在记录则直接退出
|
|
14
|
+
if [ -f /run/pentester-recording ] && pgrep -f 'tcpdump .*traffic/pcap' >/dev/null 2>&1; then
|
|
15
|
+
exit 0
|
|
16
|
+
fi
|
|
17
|
+
touch /run/pentester-recording
|
|
18
|
+
|
|
19
|
+
# 1) L3/L4 全量 pcap(始终开启,覆盖所有容器流量)
|
|
20
|
+
if command -v tcpdump >/dev/null 2>&1; then
|
|
21
|
+
nohup tcpdump -i any -U -s0 -C 100 -W 20 -w "$DIR/pcap/capture.pcap" \
|
|
22
|
+
>/dev/null 2>&1 &
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
# 2) HTTP(S) 明文层:mitmproxy 常驻 :8080(agent 可用 http_proxy/PROXY 走它
|
|
26
|
+
# 获得可读的请求/响应流;不强制,避免影响不兼容代理的工具)
|
|
27
|
+
if command -v mitmdump >/dev/null 2>&1; then
|
|
28
|
+
nohup mitmdump --listen-port 8080 -w "$DIR/flows.mitm" \
|
|
29
|
+
--set confdir="$DIR/mitm" >/dev/null 2>&1 &
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
echo "traffic recording started: $DIR"
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# tool-info <name> — detail for a single tool (description, flags, example).
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
JSON="${TOOLS_JSON:-/pentester/tools.json}"
|
|
6
|
+
if [ ! -f "$JSON" ]; then
|
|
7
|
+
echo "tools.json not found at $JSON" >&2
|
|
8
|
+
exit 1
|
|
9
|
+
fi
|
|
10
|
+
|
|
11
|
+
name="${1:-}"
|
|
12
|
+
if [ -z "$name" ]; then
|
|
13
|
+
echo "usage: tool-info <name>" >&2
|
|
14
|
+
exit 1
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
match=$(jq -c --arg n "$name" '
|
|
18
|
+
[.categories[] | .[] | select(.name == $n or .bin == $n)][0]
|
|
19
|
+
' "$JSON")
|
|
20
|
+
|
|
21
|
+
if [ "$match" = "null" ] || [ -z "$match" ]; then
|
|
22
|
+
echo "unknown tool: $name" >&2
|
|
23
|
+
exit 1
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
echo "$match" | jq -r '"name: " + .name,
|
|
27
|
+
"bin: " + .bin,
|
|
28
|
+
"description: " + .description,
|
|
29
|
+
"common_flags: " + ((.common_flags // []) | join(" ")),
|
|
30
|
+
"examples:",
|
|
31
|
+
(.examples[] | " " + .)'
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# tool-list — compact catalog overview. Agent discovery entry point.
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
|
|
5
|
+
JSON="${1:-/pentester/tools.json}"
|
|
6
|
+
if [ ! -f "$JSON" ]; then
|
|
7
|
+
echo "tools.json not found at $JSON" >&2
|
|
8
|
+
exit 1
|
|
9
|
+
fi
|
|
10
|
+
|
|
11
|
+
echo "Pentester toolbox catalog ($(jq -r '.image' "$JSON"), version $(jq -r '.version' "$JSON"))"
|
|
12
|
+
echo "bins: $(jq '.all_bins | length' "$JSON")"
|
|
13
|
+
echo
|
|
14
|
+
for category in $(jq -r '.categories | keys[]' "$JSON"); do
|
|
15
|
+
echo "── $category"
|
|
16
|
+
jq -r --arg c "$category" '.categories[$c][] | " " + .name + " — " + .description' "$JSON"
|
|
17
|
+
done
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { g as brand } from "./model-DKZ5Rjik.js";
|
|
2
|
+
//#region src/execution/catalog.ts
|
|
3
|
+
/**
|
|
4
|
+
* execution/catalog.ts — consolidated module (Wave 5; see docs/refactor/wave-5-mapping.md).
|
|
5
|
+
*/
|
|
6
|
+
function targetFromArgs(args) {
|
|
7
|
+
if (typeof args.target === "string" && args.target.length > 0) return [{ value: args.target }];
|
|
8
|
+
return [];
|
|
9
|
+
}
|
|
10
|
+
const TOOLBOX_RECON = brand("recon");
|
|
11
|
+
const TOOLBOX_WEB = brand("web");
|
|
12
|
+
const TOOLBOX_BASE = brand("base");
|
|
13
|
+
/** The single factory Toolbox image (see docker/kali). */
|
|
14
|
+
const FACTORY_TOOLBOX_IMAGE = "dsh-pentester/kali:latest";
|
|
15
|
+
const MCP_SERVER_RECON = brand("mcp-recon");
|
|
16
|
+
const TOOL_HTTP_PROBE = brand("http_probe");
|
|
17
|
+
const TOOL_SQLMAP_CHECK = brand("sqlmap_check");
|
|
18
|
+
const TOOL_CONTAINER_EXEC = brand("container_exec");
|
|
19
|
+
const TOOL_MCP_LOOKUP = brand("mcp_lookup");
|
|
20
|
+
function defaultToolDefinitions() {
|
|
21
|
+
return [
|
|
22
|
+
{
|
|
23
|
+
id: TOOL_HTTP_PROBE,
|
|
24
|
+
name: "http_probe",
|
|
25
|
+
description: "Read-only HTTP probe",
|
|
26
|
+
toolboxId: TOOLBOX_RECON,
|
|
27
|
+
backend: {
|
|
28
|
+
kind: "docker",
|
|
29
|
+
executor: "http_probe"
|
|
30
|
+
},
|
|
31
|
+
risk: "AUTO",
|
|
32
|
+
timeout: {
|
|
33
|
+
defaultMs: 5e3,
|
|
34
|
+
maxMs: 3e4
|
|
35
|
+
},
|
|
36
|
+
requiredArgumentKeys: ["target"],
|
|
37
|
+
scopeTargets: targetFromArgs
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
id: TOOL_SQLMAP_CHECK,
|
|
41
|
+
name: "sqlmap_check",
|
|
42
|
+
description: "High-risk injection validation",
|
|
43
|
+
toolboxId: TOOLBOX_WEB,
|
|
44
|
+
backend: {
|
|
45
|
+
kind: "docker",
|
|
46
|
+
executor: "sqlmap_check"
|
|
47
|
+
},
|
|
48
|
+
risk: "APPROVAL",
|
|
49
|
+
timeout: {
|
|
50
|
+
defaultMs: 8e3,
|
|
51
|
+
maxMs: 6e4
|
|
52
|
+
},
|
|
53
|
+
requiredArgumentKeys: ["target"],
|
|
54
|
+
scopeTargets: targetFromArgs
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
id: TOOL_CONTAINER_EXEC,
|
|
58
|
+
name: "container_exec",
|
|
59
|
+
description: "Execute in a granted Toolbox sandbox",
|
|
60
|
+
toolboxId: TOOLBOX_BASE,
|
|
61
|
+
backend: {
|
|
62
|
+
kind: "docker",
|
|
63
|
+
executor: "container_exec"
|
|
64
|
+
},
|
|
65
|
+
risk: "AUTO",
|
|
66
|
+
timeout: {
|
|
67
|
+
defaultMs: 1e4,
|
|
68
|
+
maxMs: 6e4
|
|
69
|
+
},
|
|
70
|
+
requiredArgumentKeys: ["toolboxId"]
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
id: TOOL_MCP_LOOKUP,
|
|
74
|
+
name: "mcp_lookup",
|
|
75
|
+
description: "Read-only MCP lookup",
|
|
76
|
+
toolboxId: TOOLBOX_RECON,
|
|
77
|
+
backend: {
|
|
78
|
+
kind: "mcp",
|
|
79
|
+
serverId: MCP_SERVER_RECON,
|
|
80
|
+
remoteToolName: "lookup"
|
|
81
|
+
},
|
|
82
|
+
risk: "AUTO",
|
|
83
|
+
timeout: {
|
|
84
|
+
defaultMs: 5e3,
|
|
85
|
+
maxMs: 2e4
|
|
86
|
+
},
|
|
87
|
+
requiredArgumentKeys: ["query"]
|
|
88
|
+
}
|
|
89
|
+
];
|
|
90
|
+
}
|
|
91
|
+
var InMemoryToolCatalog = class {
|
|
92
|
+
tools;
|
|
93
|
+
constructor(tools = defaultToolDefinitions()) {
|
|
94
|
+
this.tools = new Map(tools.map((tool) => [tool.id, tool]));
|
|
95
|
+
}
|
|
96
|
+
get(toolId) {
|
|
97
|
+
return this.tools.get(toolId);
|
|
98
|
+
}
|
|
99
|
+
listForToolbox(toolboxId) {
|
|
100
|
+
return [...this.tools.values()].filter((tool) => tool.toolboxId === toolboxId);
|
|
101
|
+
}
|
|
102
|
+
list() {
|
|
103
|
+
return [...this.tools.values()];
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
function isPlainObject(value) {
|
|
107
|
+
if (value === null || typeof value !== "object") return false;
|
|
108
|
+
const proto = Object.getPrototypeOf(value);
|
|
109
|
+
return proto === Object.prototype || proto === null;
|
|
110
|
+
}
|
|
111
|
+
/** True when a value can be serialized by lossless JSON (JSON-safe). */
|
|
112
|
+
function isLosslessJsonSafe(value) {
|
|
113
|
+
if (value === void 0) return false;
|
|
114
|
+
if (value === null) return true;
|
|
115
|
+
const type = typeof value;
|
|
116
|
+
if (type === "string" || type === "boolean") return true;
|
|
117
|
+
if (type === "number") return Number.isFinite(value);
|
|
118
|
+
if (Array.isArray(value)) return value.every(isLosslessJsonSafe);
|
|
119
|
+
if (isPlainObject(value)) return Object.values(value).every(isLosslessJsonSafe);
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
/** Build a JSON-safe model output from a ToolExecuteResult (lossless-safe). */
|
|
123
|
+
function modelOutputFromExecuteResult(result, now) {
|
|
124
|
+
const duration = now !== void 0 ? now() : void 0;
|
|
125
|
+
switch (result.status) {
|
|
126
|
+
case "succeeded": {
|
|
127
|
+
const data = isPlainObject(result.data) ? result.data : {};
|
|
128
|
+
const stdout = typeof data.stdout === "string" ? data.stdout : void 0;
|
|
129
|
+
const stderr = typeof data.stderr === "string" ? data.stderr : void 0;
|
|
130
|
+
const exitCode = typeof data.exitCode === "number" ? data.exitCode : 0;
|
|
131
|
+
return {
|
|
132
|
+
status: "completed",
|
|
133
|
+
...exitCode === void 0 ? {} : { exitCode },
|
|
134
|
+
...stdout === void 0 ? {} : { stdout },
|
|
135
|
+
...stderr === void 0 ? {} : { stderr },
|
|
136
|
+
...duration === void 0 ? {} : { durationMs: duration },
|
|
137
|
+
...result.toolCallId === void 0 ? {} : { toolCallId: result.toolCallId }
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
case "pending_approval": return {
|
|
141
|
+
status: "pending_approval",
|
|
142
|
+
...result.toolCallId === void 0 ? {} : { toolCallId: result.toolCallId }
|
|
143
|
+
};
|
|
144
|
+
case "denied": return {
|
|
145
|
+
status: "denied",
|
|
146
|
+
error: {
|
|
147
|
+
code: "DENIED",
|
|
148
|
+
message: result.reason,
|
|
149
|
+
retryable: false
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
case "capability_required": return {
|
|
153
|
+
status: "capability_required",
|
|
154
|
+
error: {
|
|
155
|
+
code: "CAPABILITY_REQUIRED",
|
|
156
|
+
message: String(result.toolboxId),
|
|
157
|
+
retryable: false
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
case "failed": return {
|
|
161
|
+
status: "failed",
|
|
162
|
+
error: {
|
|
163
|
+
code: result.kind,
|
|
164
|
+
message: result.message,
|
|
165
|
+
retryable: result.retryHint === "transient"
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
default: return {
|
|
169
|
+
status: "failed",
|
|
170
|
+
error: {
|
|
171
|
+
code: "UNKNOWN",
|
|
172
|
+
message: "unknown tool result status",
|
|
173
|
+
retryable: false
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
var InMemoryToolboxRegistry = class {
|
|
179
|
+
boxes;
|
|
180
|
+
constructor(boxes = defaultToolboxes()) {
|
|
181
|
+
this.boxes = new Map(boxes.map((box) => [box.id, box]));
|
|
182
|
+
}
|
|
183
|
+
get(id) {
|
|
184
|
+
return this.boxes.get(id);
|
|
185
|
+
}
|
|
186
|
+
list() {
|
|
187
|
+
return [...this.boxes.values()];
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
/**
|
|
191
|
+
* The single factory Toolbox image (`dsh-pentester/kali:latest`, docker/kali)
|
|
192
|
+
* serves every capability domain. The image is pre-built locally; provisioning
|
|
193
|
+
* is `pull` (an explicit management action) so execute never builds or pulls —
|
|
194
|
+
* missing image fails closed with `toolbox_image_unavailable`.
|
|
195
|
+
*/
|
|
196
|
+
const FACTORY_PROVISIONING = { pull: { image: FACTORY_TOOLBOX_IMAGE } };
|
|
197
|
+
function defaultToolboxes() {
|
|
198
|
+
return [
|
|
199
|
+
{
|
|
200
|
+
id: TOOLBOX_BASE,
|
|
201
|
+
name: "base",
|
|
202
|
+
description: "Sandbox exec (factory Kali image)",
|
|
203
|
+
toolIds: [TOOL_CONTAINER_EXEC],
|
|
204
|
+
image: FACTORY_TOOLBOX_IMAGE,
|
|
205
|
+
lifecycle: "agent_run",
|
|
206
|
+
provisioning: FACTORY_PROVISIONING
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
id: TOOLBOX_RECON,
|
|
210
|
+
name: "recon",
|
|
211
|
+
description: "Recon tools (factory Kali image)",
|
|
212
|
+
toolIds: [TOOL_HTTP_PROBE, TOOL_MCP_LOOKUP],
|
|
213
|
+
image: FACTORY_TOOLBOX_IMAGE,
|
|
214
|
+
lifecycle: "agent_run",
|
|
215
|
+
provisioning: FACTORY_PROVISIONING
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
id: TOOLBOX_WEB,
|
|
219
|
+
name: "web",
|
|
220
|
+
description: "Web tools (factory Kali image)",
|
|
221
|
+
toolIds: [TOOL_SQLMAP_CHECK],
|
|
222
|
+
image: FACTORY_TOOLBOX_IMAGE,
|
|
223
|
+
lifecycle: "agent_run",
|
|
224
|
+
provisioning: FACTORY_PROVISIONING
|
|
225
|
+
}
|
|
226
|
+
];
|
|
227
|
+
}
|
|
228
|
+
//#endregion
|
|
229
|
+
export { TOOLBOX_BASE as a, TOOL_CONTAINER_EXEC as c, defaultToolDefinitions as d, defaultToolboxes as f, MCP_SERVER_RECON as i, TOOL_HTTP_PROBE as l, modelOutputFromExecuteResult as m, InMemoryToolCatalog as n, TOOLBOX_RECON as o, isLosslessJsonSafe as p, InMemoryToolboxRegistry as r, TOOLBOX_WEB as s, FACTORY_TOOLBOX_IMAGE as t, TOOL_SQLMAP_CHECK as u };
|
|
230
|
+
|
|
231
|
+
//# sourceMappingURL=catalog-BmeOyr6n.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog-BmeOyr6n.js","names":[],"sources":["../src/execution/catalog.ts"],"sourcesContent":["/**\n * execution/catalog.ts — consolidated module (Wave 5; see docs/refactor/wave-5-mapping.md).\n */\nimport { brand, type McpServerId, type ToolId, type ToolboxId, type ToolDefinition, type ToolScopeTarget, type ToolboxDefinition } from '../core/model.js'\n\n// ── from src/tools/catalog.ts ────────────────────────────────────────\nexport interface ToolCatalog {\n get(toolId: ToolId): ToolDefinition | undefined\n listForToolbox(toolboxId: ToolboxId): readonly ToolDefinition[]\n list(): readonly ToolDefinition[]\n}\n\nfunction targetFromArgs(args: Record<string, unknown>): readonly ToolScopeTarget[] {\n if (typeof args.target === 'string' && args.target.length > 0) {\n return [{ value: args.target }]\n }\n return []\n}\n\nexport const TOOLBOX_RECON = brand<ToolboxId>('recon')\nexport const TOOLBOX_WEB = brand<ToolboxId>('web')\nexport const TOOLBOX_BASE = brand<ToolboxId>('base')\n/** The single factory Toolbox image (see docker/kali). */\nexport const FACTORY_TOOLBOX_IMAGE = 'dsh-pentester/kali:latest'\n/** Test-only fixture image (docker/fixtures/echo). */\nexport const FIXTURE_TOOLBOX_IMAGE = 'dsh-pentester-fixture-echo:local'\nexport const MCP_SERVER_RECON = brand<McpServerId>('mcp-recon')\n\nexport const TOOL_HTTP_PROBE = brand<ToolId>('http_probe')\nexport const TOOL_SQLMAP_CHECK = brand<ToolId>('sqlmap_check')\nexport const TOOL_CONTAINER_EXEC = brand<ToolId>('container_exec')\nexport const TOOL_MCP_LOOKUP = brand<ToolId>('mcp_lookup')\n\nexport function defaultToolDefinitions(): ToolDefinition[] {\n return [\n {\n id: TOOL_HTTP_PROBE,\n name: 'http_probe',\n description: 'Read-only HTTP probe',\n toolboxId: TOOLBOX_RECON,\n backend: { kind: 'docker', executor: 'http_probe' },\n risk: 'AUTO',\n timeout: { defaultMs: 5_000, maxMs: 30_000 },\n requiredArgumentKeys: ['target'],\n scopeTargets: targetFromArgs,\n },\n {\n id: TOOL_SQLMAP_CHECK,\n name: 'sqlmap_check',\n description: 'High-risk injection validation',\n toolboxId: TOOLBOX_WEB,\n backend: { kind: 'docker', executor: 'sqlmap_check' },\n risk: 'APPROVAL',\n timeout: { defaultMs: 8_000, maxMs: 60_000 },\n requiredArgumentKeys: ['target'],\n scopeTargets: targetFromArgs,\n },\n {\n id: TOOL_CONTAINER_EXEC,\n name: 'container_exec',\n description: 'Execute in a granted Toolbox sandbox',\n toolboxId: TOOLBOX_BASE,\n backend: { kind: 'docker', executor: 'container_exec' },\n risk: 'AUTO',\n timeout: { defaultMs: 10_000, maxMs: 60_000 },\n requiredArgumentKeys: ['toolboxId'],\n },\n {\n id: TOOL_MCP_LOOKUP,\n name: 'mcp_lookup',\n description: 'Read-only MCP lookup',\n toolboxId: TOOLBOX_RECON,\n backend: { kind: 'mcp', serverId: MCP_SERVER_RECON, remoteToolName: 'lookup' },\n risk: 'AUTO',\n timeout: { defaultMs: 5_000, maxMs: 20_000 },\n requiredArgumentKeys: ['query'],\n },\n ]\n}\n\nexport class InMemoryToolCatalog implements ToolCatalog {\n private readonly tools: Map<string, ToolDefinition>\n\n constructor(tools: readonly ToolDefinition[] = defaultToolDefinitions()) {\n this.tools = new Map(tools.map((tool) => [tool.id, tool]))\n }\n\n get(toolId: ToolId): ToolDefinition | undefined {\n return this.tools.get(toolId)\n }\n\n listForToolbox(toolboxId: ToolboxId): readonly ToolDefinition[] {\n return [...this.tools.values()].filter((tool) => tool.toolboxId === toolboxId)\n }\n\n list(): readonly ToolDefinition[] {\n return [...this.tools.values()]\n }\n}\n// ── from src/tools/normalize.ts ────────────────────────────────────────\n/**\n * Model-facing tool result normalization.\n *\n * DSH serializes tool results through a lossless-JSON validator that REJECTS\n * non-JSON values (`value is not lossless JSON`): undefined fields, Buffer,\n * Uint8Array, BigInt, Error, Map, Set, Date/class instances, streams, etc.\n *\n * Every result returned to the model must be a plain JSON value:\n * null | boolean | number | string | plain object | array\n *\n * This module provides the single boundary that converts internal executor\n * objects into JSON-safe model DTOs. NEVER pass internal objects (Docker\n * responses, exec results, Error instances) straight to the DSH tool result.\n */\n\nexport type JsonPrimitive = null | boolean | number | string\n// A JSON value for our purposes: primitives, arrays, or plain objects.\n// `unknown` members keep TypeScript happy while runtime guarantees JSON safety.\nexport type JsonValue = JsonPrimitive | readonly JsonValue[] | Readonly<Record<string, unknown>>\n\nexport function isPlainObject(value: unknown): value is Record<string, unknown> {\n if (value === null || typeof value !== 'object') {\n return false\n }\n const proto = Object.getPrototypeOf(value)\n return proto === Object.prototype || proto === null\n}\n\n/** True when a value can be serialized by lossless JSON (JSON-safe). */\nexport function isLosslessJsonSafe(value: unknown): boolean {\n if (value === undefined) {\n return false\n }\n if (value === null) {\n return true\n }\n const type = typeof value\n if (type === 'string' || type === 'boolean') {\n return true\n }\n if (type === 'number') {\n return Number.isFinite(value)\n }\n if (Array.isArray(value)) {\n return value.every(isLosslessJsonSafe)\n }\n if (isPlainObject(value)) {\n return Object.values(value).every(isLosslessJsonSafe)\n }\n // Buffer / Uint8Array / BigInt / Error / Map / Set / Date / class instance\n // / stream / function — not lossless-JSON safe.\n return false\n}\n\n/**\n * Deep-normalize an arbitrary value into a JSON-safe model value.\n * - undefined → omitted (callers decide) or null via `dropUndefined`\n * - Buffer/Uint8Array → base64 string (tagged) so binary output stays\n * representable without being a raw Buffer\n * - BigInt → string\n * - Error → { code?, message }\n * - Map/Set → array entries\n * - Date → ISO string\n * - class instances → plain enumerable fields\n * - everything else → dropped (undefined) or null\n */\nexport function normalizeModelToolResult(value: unknown): JsonValue | undefined {\n if (value === undefined) {\n return undefined\n }\n if (value === null) {\n return null\n }\n const type = typeof value\n if (type === 'string' || type === 'boolean') {\n return value as JsonValue\n }\n if (type === 'number') {\n return (Number.isFinite(value) ? value : null) as JsonValue\n }\n if (type === 'bigint') {\n return String(value) as JsonValue\n }\n if (Array.isArray(value)) {\n const out: JsonValue[] = []\n for (const item of value) {\n const normalized = normalizeModelToolResult(item)\n if (normalized !== undefined) {\n out.push(normalized)\n }\n }\n return out\n }\n if (Buffer.isBuffer(value)) {\n return { __buffer: true, base64: value.toString('base64'), bytes: value.length }\n }\n if (value instanceof Uint8Array) {\n return { __buffer: true, base64: Buffer.from(value).toString('base64'), bytes: value.byteLength }\n }\n if (value instanceof Error) {\n return {\n __error: true,\n ...(value.name === undefined ? {} : { name: value.name }),\n ...(value.message === undefined ? {} : { message: value.message }),\n }\n }\n if (value instanceof Map) {\n const entries: unknown[] = [...value.entries()].map(([k, v]) => [String(k), normalizeModelToolResult(v)] as const)\n return { __map: true, entries }\n }\n if (value instanceof Set) {\n const values: unknown[] = [...value.values()].map((item) => normalizeModelToolResult(item))\n return { __set: true, values }\n }\n if (value instanceof Date) {\n return { __date: true, iso: value.toISOString() }\n }\n if (isPlainObject(value)) {\n const out: Record<string, JsonValue> = {}\n for (const [key, inner] of Object.entries(value)) {\n const normalized = normalizeModelToolResult(inner)\n if (normalized !== undefined) {\n out[key] = normalized\n }\n }\n return out\n }\n // Function / Symbol / stream / opaque object: not representable → drop.\n return undefined\n}\n\n/**\n * container_exec / http_probe model-facing output DTO.\n * Fixed shape so DSH always receives JSON-safe plain values.\n */\nexport interface ModelExecOutput {\n readonly status: 'completed' | 'failed' | 'pending_approval' | 'denied' | 'capability_required'\n readonly exitCode?: number\n readonly stdout?: string\n readonly stderr?: string\n readonly durationMs?: number\n readonly toolCallId?: string\n readonly error?: {\n readonly code: string\n readonly message: string\n readonly retryable: boolean\n }\n}\n\n/** Build a JSON-safe model output from a ToolExecuteResult (lossless-safe). */\nexport function modelOutputFromExecuteResult(\n result: import('./outputs.js').ToolExecuteResult,\n now?: () => number,\n): ModelExecOutput {\n const duration = now !== undefined ? now() : undefined\n switch (result.status) {\n case 'succeeded': {\n const data = isPlainObject(result.data) ? result.data : {}\n const stdout = typeof data.stdout === 'string' ? data.stdout : undefined\n const stderr = typeof data.stderr === 'string' ? data.stderr : undefined\n const exitCode = typeof data.exitCode === 'number' ? data.exitCode : 0\n return {\n status: 'completed',\n ...(exitCode === undefined ? {} : { exitCode }),\n ...(stdout === undefined ? {} : { stdout }),\n ...(stderr === undefined ? {} : { stderr }),\n ...(duration === undefined ? {} : { durationMs: duration }),\n ...(result.toolCallId === undefined ? {} : { toolCallId: result.toolCallId }),\n }\n }\n case 'pending_approval':\n return {\n status: 'pending_approval',\n ...(result.toolCallId === undefined ? {} : { toolCallId: result.toolCallId }),\n }\n case 'denied':\n return {\n status: 'denied',\n error: { code: 'DENIED', message: result.reason, retryable: false },\n }\n case 'capability_required':\n return {\n status: 'capability_required',\n error: { code: 'CAPABILITY_REQUIRED', message: String(result.toolboxId), retryable: false },\n }\n case 'failed':\n return {\n status: 'failed',\n error: {\n code: result.kind,\n message: result.message,\n retryable: result.retryHint === 'transient',\n },\n }\n default:\n return { status: 'failed', error: { code: 'UNKNOWN', message: 'unknown tool result status', retryable: false } }\n }\n}\n// ── from src/tools/toolbox-registry.ts ────────────────────────────────────────\nexport class InMemoryToolboxRegistry {\n private readonly boxes: Map<string, ToolboxDefinition>\n\n constructor(boxes: readonly ToolboxDefinition[] = defaultToolboxes()) {\n this.boxes = new Map(boxes.map((box) => [box.id, box]))\n }\n\n get(id: ToolboxId): ToolboxDefinition | undefined {\n return this.boxes.get(id)\n }\n\n list(): readonly ToolboxDefinition[] {\n return [...this.boxes.values()]\n }\n}\n\n/**\n * The single factory Toolbox image (`dsh-pentester/kali:latest`, docker/kali)\n * serves every capability domain. The image is pre-built locally; provisioning\n * is `pull` (an explicit management action) so execute never builds or pulls —\n * missing image fails closed with `toolbox_image_unavailable`.\n */\nconst FACTORY_PROVISIONING = {\n pull: { image: FACTORY_TOOLBOX_IMAGE },\n} as const\n\nexport const FIXTURE_PROVISIONING = {\n dockerfile: { context: 'docker/fixtures/echo', dockerfile: 'Dockerfile' },\n} as const\n\nexport function defaultToolboxes(): ToolboxDefinition[] {\n return [\n {\n id: TOOLBOX_BASE,\n name: 'base',\n description: 'Sandbox exec (factory Kali image)',\n toolIds: [TOOL_CONTAINER_EXEC],\n image: FACTORY_TOOLBOX_IMAGE,\n lifecycle: 'agent_run',\n provisioning: FACTORY_PROVISIONING,\n },\n {\n id: TOOLBOX_RECON,\n name: 'recon',\n description: 'Recon tools (factory Kali image)',\n toolIds: [TOOL_HTTP_PROBE, TOOL_MCP_LOOKUP],\n image: FACTORY_TOOLBOX_IMAGE,\n lifecycle: 'agent_run',\n provisioning: FACTORY_PROVISIONING,\n },\n {\n id: TOOLBOX_WEB,\n name: 'web',\n description: 'Web tools (factory Kali image)',\n toolIds: [TOOL_SQLMAP_CHECK],\n image: FACTORY_TOOLBOX_IMAGE,\n lifecycle: 'agent_run',\n provisioning: FACTORY_PROVISIONING,\n },\n ]\n}\n\n"],"mappings":"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"}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { _ as brand } from "./model-CZoiogVs.js";
|
|
2
|
+
//#region src/execution/catalog.ts
|
|
3
|
+
/**
|
|
4
|
+
* execution/catalog.ts — consolidated module (Wave 5; see docs/refactor/wave-5-mapping.md).
|
|
5
|
+
*/
|
|
6
|
+
function targetFromArgs(args) {
|
|
7
|
+
if (typeof args.target === "string" && args.target.length > 0) return [{ value: args.target }];
|
|
8
|
+
return [];
|
|
9
|
+
}
|
|
10
|
+
const TOOLBOX_RECON = brand("recon");
|
|
11
|
+
const TOOLBOX_WEB = brand("web");
|
|
12
|
+
const TOOLBOX_BASE = brand("base");
|
|
13
|
+
/** The single factory Toolbox image (see docker/kali). */
|
|
14
|
+
const FACTORY_TOOLBOX_IMAGE = "dsh-pentester/kali:latest";
|
|
15
|
+
const MCP_SERVER_RECON = brand("mcp-recon");
|
|
16
|
+
const TOOL_HTTP_PROBE = brand("http_probe");
|
|
17
|
+
const TOOL_SQLMAP_CHECK = brand("sqlmap_check");
|
|
18
|
+
const TOOL_CONTAINER_EXEC = brand("container_exec");
|
|
19
|
+
const TOOL_MCP_LOOKUP = brand("mcp_lookup");
|
|
20
|
+
function defaultToolDefinitions() {
|
|
21
|
+
return [
|
|
22
|
+
{
|
|
23
|
+
id: TOOL_HTTP_PROBE,
|
|
24
|
+
name: "http_probe",
|
|
25
|
+
description: "Read-only HTTP probe",
|
|
26
|
+
toolboxId: TOOLBOX_RECON,
|
|
27
|
+
backend: {
|
|
28
|
+
kind: "docker",
|
|
29
|
+
executor: "http_probe"
|
|
30
|
+
},
|
|
31
|
+
risk: "AUTO",
|
|
32
|
+
timeout: {
|
|
33
|
+
defaultMs: 5e3,
|
|
34
|
+
maxMs: 3e4
|
|
35
|
+
},
|
|
36
|
+
requiredArgumentKeys: ["target"],
|
|
37
|
+
scopeTargets: targetFromArgs
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
id: TOOL_SQLMAP_CHECK,
|
|
41
|
+
name: "sqlmap_check",
|
|
42
|
+
description: "High-risk injection validation",
|
|
43
|
+
toolboxId: TOOLBOX_WEB,
|
|
44
|
+
backend: {
|
|
45
|
+
kind: "docker",
|
|
46
|
+
executor: "sqlmap_check"
|
|
47
|
+
},
|
|
48
|
+
risk: "APPROVAL",
|
|
49
|
+
timeout: {
|
|
50
|
+
defaultMs: 8e3,
|
|
51
|
+
maxMs: 6e4
|
|
52
|
+
},
|
|
53
|
+
requiredArgumentKeys: ["target"],
|
|
54
|
+
scopeTargets: targetFromArgs
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
id: TOOL_CONTAINER_EXEC,
|
|
58
|
+
name: "container_exec",
|
|
59
|
+
description: "Execute in a granted Toolbox sandbox",
|
|
60
|
+
toolboxId: TOOLBOX_BASE,
|
|
61
|
+
backend: {
|
|
62
|
+
kind: "docker",
|
|
63
|
+
executor: "container_exec"
|
|
64
|
+
},
|
|
65
|
+
risk: "AUTO",
|
|
66
|
+
timeout: {
|
|
67
|
+
defaultMs: 1e4,
|
|
68
|
+
maxMs: 6e4
|
|
69
|
+
},
|
|
70
|
+
requiredArgumentKeys: ["toolboxId"]
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
id: TOOL_MCP_LOOKUP,
|
|
74
|
+
name: "mcp_lookup",
|
|
75
|
+
description: "Read-only MCP lookup",
|
|
76
|
+
toolboxId: TOOLBOX_RECON,
|
|
77
|
+
backend: {
|
|
78
|
+
kind: "mcp",
|
|
79
|
+
serverId: MCP_SERVER_RECON,
|
|
80
|
+
remoteToolName: "lookup"
|
|
81
|
+
},
|
|
82
|
+
risk: "AUTO",
|
|
83
|
+
timeout: {
|
|
84
|
+
defaultMs: 5e3,
|
|
85
|
+
maxMs: 2e4
|
|
86
|
+
},
|
|
87
|
+
requiredArgumentKeys: ["query"]
|
|
88
|
+
}
|
|
89
|
+
];
|
|
90
|
+
}
|
|
91
|
+
var InMemoryToolCatalog = class {
|
|
92
|
+
tools;
|
|
93
|
+
constructor(tools = defaultToolDefinitions()) {
|
|
94
|
+
this.tools = new Map(tools.map((tool) => [tool.id, tool]));
|
|
95
|
+
}
|
|
96
|
+
get(toolId) {
|
|
97
|
+
return this.tools.get(toolId);
|
|
98
|
+
}
|
|
99
|
+
listForToolbox(toolboxId) {
|
|
100
|
+
return [...this.tools.values()].filter((tool) => tool.toolboxId === toolboxId);
|
|
101
|
+
}
|
|
102
|
+
list() {
|
|
103
|
+
return [...this.tools.values()];
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
function isPlainObject(value) {
|
|
107
|
+
if (value === null || typeof value !== "object") return false;
|
|
108
|
+
const proto = Object.getPrototypeOf(value);
|
|
109
|
+
return proto === Object.prototype || proto === null;
|
|
110
|
+
}
|
|
111
|
+
/** True when a value can be serialized by lossless JSON (JSON-safe). */
|
|
112
|
+
function isLosslessJsonSafe(value) {
|
|
113
|
+
if (value === void 0) return false;
|
|
114
|
+
if (value === null) return true;
|
|
115
|
+
const type = typeof value;
|
|
116
|
+
if (type === "string" || type === "boolean") return true;
|
|
117
|
+
if (type === "number") return Number.isFinite(value);
|
|
118
|
+
if (Array.isArray(value)) return value.every(isLosslessJsonSafe);
|
|
119
|
+
if (isPlainObject(value)) return Object.values(value).every(isLosslessJsonSafe);
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
/** Build a JSON-safe model output from a ToolExecuteResult (lossless-safe). */
|
|
123
|
+
function modelOutputFromExecuteResult(result, now) {
|
|
124
|
+
const duration = now !== void 0 ? now() : void 0;
|
|
125
|
+
switch (result.status) {
|
|
126
|
+
case "succeeded": {
|
|
127
|
+
const data = isPlainObject(result.data) ? result.data : {};
|
|
128
|
+
const stdout = typeof data.stdout === "string" ? data.stdout : void 0;
|
|
129
|
+
const stderr = typeof data.stderr === "string" ? data.stderr : void 0;
|
|
130
|
+
const exitCode = typeof data.exitCode === "number" ? data.exitCode : 0;
|
|
131
|
+
return {
|
|
132
|
+
status: "completed",
|
|
133
|
+
...exitCode === void 0 ? {} : { exitCode },
|
|
134
|
+
...stdout === void 0 ? {} : { stdout },
|
|
135
|
+
...stderr === void 0 ? {} : { stderr },
|
|
136
|
+
...duration === void 0 ? {} : { durationMs: duration },
|
|
137
|
+
...result.toolCallId === void 0 ? {} : { toolCallId: result.toolCallId }
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
case "pending_approval": return {
|
|
141
|
+
status: "pending_approval",
|
|
142
|
+
...result.toolCallId === void 0 ? {} : { toolCallId: result.toolCallId }
|
|
143
|
+
};
|
|
144
|
+
case "denied": return {
|
|
145
|
+
status: "denied",
|
|
146
|
+
error: {
|
|
147
|
+
code: "DENIED",
|
|
148
|
+
message: result.reason,
|
|
149
|
+
retryable: false
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
case "capability_required": return {
|
|
153
|
+
status: "capability_required",
|
|
154
|
+
error: {
|
|
155
|
+
code: "CAPABILITY_REQUIRED",
|
|
156
|
+
message: String(result.toolboxId),
|
|
157
|
+
retryable: false
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
case "failed": return {
|
|
161
|
+
status: "failed",
|
|
162
|
+
error: {
|
|
163
|
+
code: result.kind,
|
|
164
|
+
message: result.message,
|
|
165
|
+
retryable: result.retryHint === "transient"
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
default: return {
|
|
169
|
+
status: "failed",
|
|
170
|
+
error: {
|
|
171
|
+
code: "UNKNOWN",
|
|
172
|
+
message: "unknown tool result status",
|
|
173
|
+
retryable: false
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
var InMemoryToolboxRegistry = class {
|
|
179
|
+
boxes;
|
|
180
|
+
constructor(boxes = defaultToolboxes()) {
|
|
181
|
+
this.boxes = new Map(boxes.map((box) => [box.id, box]));
|
|
182
|
+
}
|
|
183
|
+
get(id) {
|
|
184
|
+
return this.boxes.get(id);
|
|
185
|
+
}
|
|
186
|
+
list() {
|
|
187
|
+
return [...this.boxes.values()];
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
/**
|
|
191
|
+
* The single factory Toolbox image (`dsh-pentester/kali:latest`, docker/kali)
|
|
192
|
+
* serves every capability domain. The image is pre-built locally; provisioning
|
|
193
|
+
* is `pull` (an explicit management action) so execute never builds or pulls —
|
|
194
|
+
* missing image fails closed with `toolbox_image_unavailable`.
|
|
195
|
+
*/
|
|
196
|
+
const FACTORY_PROVISIONING = { pull: { image: FACTORY_TOOLBOX_IMAGE } };
|
|
197
|
+
function defaultToolboxes() {
|
|
198
|
+
return [
|
|
199
|
+
{
|
|
200
|
+
id: TOOLBOX_BASE,
|
|
201
|
+
name: "base",
|
|
202
|
+
description: "Sandbox exec (factory Kali image)",
|
|
203
|
+
toolIds: [TOOL_CONTAINER_EXEC],
|
|
204
|
+
image: FACTORY_TOOLBOX_IMAGE,
|
|
205
|
+
lifecycle: "agent_run",
|
|
206
|
+
provisioning: FACTORY_PROVISIONING
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
id: TOOLBOX_RECON,
|
|
210
|
+
name: "recon",
|
|
211
|
+
description: "Recon tools (factory Kali image)",
|
|
212
|
+
toolIds: [TOOL_HTTP_PROBE, TOOL_MCP_LOOKUP],
|
|
213
|
+
image: FACTORY_TOOLBOX_IMAGE,
|
|
214
|
+
lifecycle: "agent_run",
|
|
215
|
+
provisioning: FACTORY_PROVISIONING
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
id: TOOLBOX_WEB,
|
|
219
|
+
name: "web",
|
|
220
|
+
description: "Web tools (factory Kali image)",
|
|
221
|
+
toolIds: [TOOL_SQLMAP_CHECK],
|
|
222
|
+
image: FACTORY_TOOLBOX_IMAGE,
|
|
223
|
+
lifecycle: "agent_run",
|
|
224
|
+
provisioning: FACTORY_PROVISIONING
|
|
225
|
+
}
|
|
226
|
+
];
|
|
227
|
+
}
|
|
228
|
+
//#endregion
|
|
229
|
+
export { TOOLBOX_BASE as a, TOOL_CONTAINER_EXEC as c, defaultToolDefinitions as d, defaultToolboxes as f, MCP_SERVER_RECON as i, TOOL_HTTP_PROBE as l, modelOutputFromExecuteResult as m, InMemoryToolCatalog as n, TOOLBOX_RECON as o, isLosslessJsonSafe as p, InMemoryToolboxRegistry as r, TOOLBOX_WEB as s, FACTORY_TOOLBOX_IMAGE as t, TOOL_SQLMAP_CHECK as u };
|
|
230
|
+
|
|
231
|
+
//# sourceMappingURL=catalog-DhE_r18k.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog-DhE_r18k.js","names":[],"sources":["../src/execution/catalog.ts"],"sourcesContent":["/**\n * execution/catalog.ts — consolidated module (Wave 5; see docs/refactor/wave-5-mapping.md).\n */\nimport { brand, type McpServerId, type ToolId, type ToolboxId, type ToolDefinition, type ToolScopeTarget, type ToolboxDefinition } from '../core/model.js'\n\n// ── from src/tools/catalog.ts ────────────────────────────────────────\nexport interface ToolCatalog {\n get(toolId: ToolId): ToolDefinition | undefined\n listForToolbox(toolboxId: ToolboxId): readonly ToolDefinition[]\n list(): readonly ToolDefinition[]\n}\n\nfunction targetFromArgs(args: Record<string, unknown>): readonly ToolScopeTarget[] {\n if (typeof args.target === 'string' && args.target.length > 0) {\n return [{ value: args.target }]\n }\n return []\n}\n\nexport const TOOLBOX_RECON = brand<ToolboxId>('recon')\nexport const TOOLBOX_WEB = brand<ToolboxId>('web')\nexport const TOOLBOX_BASE = brand<ToolboxId>('base')\n/** The single factory Toolbox image (see docker/kali). */\nexport const FACTORY_TOOLBOX_IMAGE = 'dsh-pentester/kali:latest'\n/** Test-only fixture image (docker/fixtures/echo). */\nexport const FIXTURE_TOOLBOX_IMAGE = 'dsh-pentester-fixture-echo:local'\nexport const MCP_SERVER_RECON = brand<McpServerId>('mcp-recon')\n\nexport const TOOL_HTTP_PROBE = brand<ToolId>('http_probe')\nexport const TOOL_SQLMAP_CHECK = brand<ToolId>('sqlmap_check')\nexport const TOOL_CONTAINER_EXEC = brand<ToolId>('container_exec')\nexport const TOOL_MCP_LOOKUP = brand<ToolId>('mcp_lookup')\n\nexport function defaultToolDefinitions(): ToolDefinition[] {\n return [\n {\n id: TOOL_HTTP_PROBE,\n name: 'http_probe',\n description: 'Read-only HTTP probe',\n toolboxId: TOOLBOX_RECON,\n backend: { kind: 'docker', executor: 'http_probe' },\n risk: 'AUTO',\n timeout: { defaultMs: 5_000, maxMs: 30_000 },\n requiredArgumentKeys: ['target'],\n scopeTargets: targetFromArgs,\n },\n {\n id: TOOL_SQLMAP_CHECK,\n name: 'sqlmap_check',\n description: 'High-risk injection validation',\n toolboxId: TOOLBOX_WEB,\n backend: { kind: 'docker', executor: 'sqlmap_check' },\n risk: 'APPROVAL',\n timeout: { defaultMs: 8_000, maxMs: 60_000 },\n requiredArgumentKeys: ['target'],\n scopeTargets: targetFromArgs,\n },\n {\n id: TOOL_CONTAINER_EXEC,\n name: 'container_exec',\n description: 'Execute in a granted Toolbox sandbox',\n toolboxId: TOOLBOX_BASE,\n backend: { kind: 'docker', executor: 'container_exec' },\n risk: 'AUTO',\n timeout: { defaultMs: 10_000, maxMs: 60_000 },\n requiredArgumentKeys: ['toolboxId'],\n },\n {\n id: TOOL_MCP_LOOKUP,\n name: 'mcp_lookup',\n description: 'Read-only MCP lookup',\n toolboxId: TOOLBOX_RECON,\n backend: { kind: 'mcp', serverId: MCP_SERVER_RECON, remoteToolName: 'lookup' },\n risk: 'AUTO',\n timeout: { defaultMs: 5_000, maxMs: 20_000 },\n requiredArgumentKeys: ['query'],\n },\n ]\n}\n\nexport class InMemoryToolCatalog implements ToolCatalog {\n private readonly tools: Map<string, ToolDefinition>\n\n constructor(tools: readonly ToolDefinition[] = defaultToolDefinitions()) {\n this.tools = new Map(tools.map((tool) => [tool.id, tool]))\n }\n\n get(toolId: ToolId): ToolDefinition | undefined {\n return this.tools.get(toolId)\n }\n\n listForToolbox(toolboxId: ToolboxId): readonly ToolDefinition[] {\n return [...this.tools.values()].filter((tool) => tool.toolboxId === toolboxId)\n }\n\n list(): readonly ToolDefinition[] {\n return [...this.tools.values()]\n }\n}\n// ── from src/tools/normalize.ts ────────────────────────────────────────\n/**\n * Model-facing tool result normalization.\n *\n * DSH serializes tool results through a lossless-JSON validator that REJECTS\n * non-JSON values (`value is not lossless JSON`): undefined fields, Buffer,\n * Uint8Array, BigInt, Error, Map, Set, Date/class instances, streams, etc.\n *\n * Every result returned to the model must be a plain JSON value:\n * null | boolean | number | string | plain object | array\n *\n * This module provides the single boundary that converts internal executor\n * objects into JSON-safe model DTOs. NEVER pass internal objects (Docker\n * responses, exec results, Error instances) straight to the DSH tool result.\n */\n\ntype JsonPrimitive = null | boolean | number | string\n// A JSON value for our purposes: primitives, arrays, or plain objects.\n// `unknown` members keep TypeScript happy while runtime guarantees JSON safety.\ntype JsonValue = JsonPrimitive | readonly JsonValue[] | Readonly<Record<string, unknown>>\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n if (value === null || typeof value !== 'object') {\n return false\n }\n const proto = Object.getPrototypeOf(value)\n return proto === Object.prototype || proto === null\n}\n\n/** True when a value can be serialized by lossless JSON (JSON-safe). */\nexport function isLosslessJsonSafe(value: unknown): boolean {\n if (value === undefined) {\n return false\n }\n if (value === null) {\n return true\n }\n const type = typeof value\n if (type === 'string' || type === 'boolean') {\n return true\n }\n if (type === 'number') {\n return Number.isFinite(value)\n }\n if (Array.isArray(value)) {\n return value.every(isLosslessJsonSafe)\n }\n if (isPlainObject(value)) {\n return Object.values(value).every(isLosslessJsonSafe)\n }\n // Buffer / Uint8Array / BigInt / Error / Map / Set / Date / class instance\n // / stream / function — not lossless-JSON safe.\n return false\n}\n\n/**\n * Deep-normalize an arbitrary value into a JSON-safe model value.\n * - undefined → omitted (callers decide) or null via `dropUndefined`\n * - Buffer/Uint8Array → base64 string (tagged) so binary output stays\n * representable without being a raw Buffer\n * - BigInt → string\n * - Error → { code?, message }\n * - Map/Set → array entries\n * - Date → ISO string\n * - class instances → plain enumerable fields\n * - everything else → dropped (undefined) or null\n */\nexport function normalizeModelToolResult(value: unknown): JsonValue | undefined {\n if (value === undefined) {\n return undefined\n }\n if (value === null) {\n return null\n }\n const type = typeof value\n if (type === 'string' || type === 'boolean') {\n return value as JsonValue\n }\n if (type === 'number') {\n return (Number.isFinite(value) ? value : null) as JsonValue\n }\n if (type === 'bigint') {\n return String(value) as JsonValue\n }\n if (Array.isArray(value)) {\n const out: JsonValue[] = []\n for (const item of value) {\n const normalized = normalizeModelToolResult(item)\n if (normalized !== undefined) {\n out.push(normalized)\n }\n }\n return out\n }\n if (Buffer.isBuffer(value)) {\n return { __buffer: true, base64: value.toString('base64'), bytes: value.length }\n }\n if (value instanceof Uint8Array) {\n return { __buffer: true, base64: Buffer.from(value).toString('base64'), bytes: value.byteLength }\n }\n if (value instanceof Error) {\n return {\n __error: true,\n ...(value.name === undefined ? {} : { name: value.name }),\n ...(value.message === undefined ? {} : { message: value.message }),\n }\n }\n if (value instanceof Map) {\n const entries: unknown[] = [...value.entries()].map(([k, v]) => [String(k), normalizeModelToolResult(v)] as const)\n return { __map: true, entries }\n }\n if (value instanceof Set) {\n const values: unknown[] = [...value.values()].map((item) => normalizeModelToolResult(item))\n return { __set: true, values }\n }\n if (value instanceof Date) {\n return { __date: true, iso: value.toISOString() }\n }\n if (isPlainObject(value)) {\n const out: Record<string, JsonValue> = {}\n for (const [key, inner] of Object.entries(value)) {\n const normalized = normalizeModelToolResult(inner)\n if (normalized !== undefined) {\n out[key] = normalized\n }\n }\n return out\n }\n // Function / Symbol / stream / opaque object: not representable → drop.\n return undefined\n}\n\n/**\n * container_exec / http_probe model-facing output DTO.\n * Fixed shape so DSH always receives JSON-safe plain values.\n */\ninterface ModelExecOutput {\n readonly status: 'completed' | 'failed' | 'pending_approval' | 'denied' | 'capability_required'\n readonly exitCode?: number\n readonly stdout?: string\n readonly stderr?: string\n readonly durationMs?: number\n readonly toolCallId?: string\n readonly error?: {\n readonly code: string\n readonly message: string\n readonly retryable: boolean\n }\n}\n\n/** Build a JSON-safe model output from a ToolExecuteResult (lossless-safe). */\nexport function modelOutputFromExecuteResult(\n result: import('./outputs.js').ToolExecuteResult,\n now?: () => number,\n): ModelExecOutput {\n const duration = now !== undefined ? now() : undefined\n switch (result.status) {\n case 'succeeded': {\n const data = isPlainObject(result.data) ? result.data : {}\n const stdout = typeof data.stdout === 'string' ? data.stdout : undefined\n const stderr = typeof data.stderr === 'string' ? data.stderr : undefined\n const exitCode = typeof data.exitCode === 'number' ? data.exitCode : 0\n return {\n status: 'completed',\n ...(exitCode === undefined ? {} : { exitCode }),\n ...(stdout === undefined ? {} : { stdout }),\n ...(stderr === undefined ? {} : { stderr }),\n ...(duration === undefined ? {} : { durationMs: duration }),\n ...(result.toolCallId === undefined ? {} : { toolCallId: result.toolCallId }),\n }\n }\n case 'pending_approval':\n return {\n status: 'pending_approval',\n ...(result.toolCallId === undefined ? {} : { toolCallId: result.toolCallId }),\n }\n case 'denied':\n return {\n status: 'denied',\n error: { code: 'DENIED', message: result.reason, retryable: false },\n }\n case 'capability_required':\n return {\n status: 'capability_required',\n error: { code: 'CAPABILITY_REQUIRED', message: String(result.toolboxId), retryable: false },\n }\n case 'failed':\n return {\n status: 'failed',\n error: {\n code: result.kind,\n message: result.message,\n retryable: result.retryHint === 'transient',\n },\n }\n default:\n return { status: 'failed', error: { code: 'UNKNOWN', message: 'unknown tool result status', retryable: false } }\n }\n}\n// ── from src/tools/toolbox-registry.ts ────────────────────────────────────────\nexport class InMemoryToolboxRegistry {\n private readonly boxes: Map<string, ToolboxDefinition>\n\n constructor(boxes: readonly ToolboxDefinition[] = defaultToolboxes()) {\n this.boxes = new Map(boxes.map((box) => [box.id, box]))\n }\n\n get(id: ToolboxId): ToolboxDefinition | undefined {\n return this.boxes.get(id)\n }\n\n list(): readonly ToolboxDefinition[] {\n return [...this.boxes.values()]\n }\n}\n\n/**\n * The single factory Toolbox image (`dsh-pentester/kali:latest`, docker/kali)\n * serves every capability domain. The image is pre-built locally; provisioning\n * is `pull` (an explicit management action) so execute never builds or pulls —\n * missing image fails closed with `toolbox_image_unavailable`.\n */\nconst FACTORY_PROVISIONING = {\n pull: { image: FACTORY_TOOLBOX_IMAGE },\n} as const\n\nexport const FIXTURE_PROVISIONING = {\n dockerfile: { context: 'docker/fixtures/echo', dockerfile: 'Dockerfile' },\n} as const\n\nexport function defaultToolboxes(): ToolboxDefinition[] {\n return [\n {\n id: TOOLBOX_BASE,\n name: 'base',\n description: 'Sandbox exec (factory Kali image)',\n toolIds: [TOOL_CONTAINER_EXEC],\n image: FACTORY_TOOLBOX_IMAGE,\n lifecycle: 'agent_run',\n provisioning: FACTORY_PROVISIONING,\n },\n {\n id: TOOLBOX_RECON,\n name: 'recon',\n description: 'Recon tools (factory Kali image)',\n toolIds: [TOOL_HTTP_PROBE, TOOL_MCP_LOOKUP],\n image: FACTORY_TOOLBOX_IMAGE,\n lifecycle: 'agent_run',\n provisioning: FACTORY_PROVISIONING,\n },\n {\n id: TOOLBOX_WEB,\n name: 'web',\n description: 'Web tools (factory Kali image)',\n toolIds: [TOOL_SQLMAP_CHECK],\n image: FACTORY_TOOLBOX_IMAGE,\n lifecycle: 'agent_run',\n provisioning: FACTORY_PROVISIONING,\n },\n ]\n}\n\n"],"mappings":"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"}
|