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 @@
|
|
|
1
|
+
{"version":3,"file":"engagement-container-store-L_Gms-zi.js","names":["httpRequest"],"sources":["../src/tools/errors.ts","../src/tools/persist.ts","../src/tools/output-capture.ts","../src/tools/docker-engine.ts","../src/tools/engagement-container-store.ts"],"sourcesContent":["export type ToolBrokerErrorCode =\n | 'unknown_tool'\n | 'invalid_arguments'\n | 'invalid_timeout'\n | 'task_not_found'\n | 'agent_run_not_found'\n | 'task_run_mismatch'\n | 'out_of_scope'\n | 'capability_required'\n | 'denied'\n | 'not_pending'\n | 'call_not_found'\n | 'backend_failure'\n | 'toolbox_image_unavailable'\n | 'mcp_unauthorized'\n | 'persist_failed'\n\nexport class ToolBrokerError extends Error {\n readonly code: ToolBrokerErrorCode\n\n constructor(code: ToolBrokerErrorCode, message: string) {\n super(message)\n this.name = 'ToolBrokerError'\n this.code = code\n }\n}","import { randomUUID } from 'node:crypto'\nimport { mkdir, readFile, rename, unlink, writeFile } from 'node:fs/promises'\nimport { dirname } from 'node:path'\nimport { ToolBrokerError } from './errors.js'\n\nexport function toolsRuntimeDir(engagementDir: string): string {\n return `${engagementDir}/runtime/tools`\n}\n\nexport async function writeJsonAtomic(target: string, value: unknown): Promise<void> {\n await mkdir(dirname(target), { recursive: true })\n const tmp = `${target}.tmp-${randomUUID()}`\n try {\n await writeFile(tmp, `${JSON.stringify(value, null, 2)}\\n`, 'utf8')\n await rename(tmp, target)\n } catch (error) {\n try {\n await unlink(tmp)\n } catch {\n // tmp may not exist\n }\n throw new ToolBrokerError(\n 'persist_failed',\n error instanceof Error ? error.message : `Failed to write ${target}`,\n )\n }\n}\n\nexport async function readJsonFile(path: string): Promise<unknown | null> {\n try {\n return JSON.parse(await readFile(path, 'utf8')) as unknown\n } catch (error) {\n const code = (error as { code?: string }).code\n if (code === 'ENOENT') {\n return null\n }\n if (error instanceof SyntaxError) {\n return null\n }\n throw error\n }\n}","import type { ToolOutputLimits } from '../core/model.js';\n\nexport interface CapturedOutputs {\n readonly files: Readonly<Record<string, string>>\n readonly binaryFiles: Readonly<Record<string, Buffer>>\n readonly inline: { stdout?: string; stderr?: string }\n readonly truncated: boolean\n readonly captureLimitReached: boolean\n readonly binary: boolean\n}\n\nexport class BoundedCapture {\n truncated = false\n captureLimitReached = false\n buffered = 0\n private remaining: number\n private readonly stdoutParts: Buffer[] = []\n private readonly stderrParts: Buffer[] = []\n\n constructor(maxTotalCapturedBytes: number) {\n this.remaining = maxTotalCapturedBytes\n }\n\n get full(): boolean {\n return this.remaining <= 0\n }\n\n accept(stream: 'stdout' | 'stderr', chunk: Buffer): void {\n if (chunk.length === 0) {\n return\n }\n if (this.remaining <= 0) {\n this.captureLimitReached = true\n this.truncated = true\n return\n }\n let take = chunk\n if (chunk.length > this.remaining) {\n take = chunk.subarray(0, this.remaining)\n this.captureLimitReached = true\n this.truncated = true\n }\n this.remaining -= take.length\n this.buffered += take.length\n if (stream === 'stderr') {\n this.stderrParts.push(take)\n } else {\n this.stdoutParts.push(take)\n }\n }\n\n stdout(): Buffer {\n return Buffer.concat(this.stdoutParts)\n }\n\n stderr(): Buffer {\n return Buffer.concat(this.stderrParts)\n }\n}\n\nexport class DockerMuxCapture {\n readonly inner: BoundedCapture\n private leftover = Buffer.alloc(0)\n\n constructor(maxTotalCapturedBytes: number) {\n this.inner = new BoundedCapture(maxTotalCapturedBytes)\n }\n\n pushMuxed(chunk: Buffer): void {\n this.leftover = Buffer.concat([this.leftover, chunk])\n while (this.leftover.length >= 8) {\n const type = this.leftover[0]\n if (type !== 1 && type !== 2 && type !== 0) {\n this.inner.accept('stdout', this.leftover)\n this.leftover = Buffer.alloc(0)\n return\n }\n const size = this.leftover.readUInt32BE(4)\n if (this.leftover.length < 8 + size) {\n return\n }\n const payload = this.leftover.subarray(8, 8 + size)\n this.leftover = this.leftover.subarray(8 + size)\n this.inner.accept(type === 2 ? 'stderr' : 'stdout', payload)\n }\n }\n}\n\nexport function isBinaryBuffer(buf: Buffer): boolean {\n if (buf.includes(0)) {\n return true\n }\n const sample = buf.subarray(0, Math.min(buf.length, 4096))\n try {\n const decoded = new TextDecoder('utf8', { fatal: true }).decode(sample)\n return decoded.includes('\\uFFFD')\n } catch {\n return true\n }\n}\n\nexport function applyCaptureLimits(\n streams: { readonly stdout: Buffer; readonly stderr: Buffer },\n limits: ToolOutputLimits,\n): CapturedOutputs {\n let truncated = false\n let captureLimitReached = false\n let remaining = limits.maxTotalCapturedBytes\n const take = (buf: Buffer, name: string): Buffer => {\n if (remaining <= 0) {\n captureLimitReached = true\n truncated = true\n return Buffer.alloc(0)\n }\n if (buf.length > remaining) {\n captureLimitReached = true\n truncated = true\n const slice = buf.subarray(0, remaining)\n remaining = 0\n void name\n return slice\n }\n remaining -= buf.length\n return buf\n }\n const stdout = take(streams.stdout, 'stdout')\n const stderr = take(streams.stderr, 'stderr')\n const stdoutBinary = isBinaryBuffer(stdout) && stdout.length > 0\n const stderrBinary = isBinaryBuffer(stderr) && stderr.length > 0\n const files: Record<string, string> = {}\n const binaryFiles: Record<string, Buffer> = {}\n const inline: { stdout?: string; stderr?: string } = {}\n const put = (name: 'stdout' | 'stderr', buf: Buffer, binary: boolean) => {\n if (buf.length === 0) {\n return\n }\n if (binary) {\n binaryFiles[`${name}.bin`] = buf\n return\n }\n const text = buf.toString('utf8')\n files[`${name}.txt`] = text\n if (buf.length <= limits.maxInlineOutputBytes && !truncated) {\n inline[name] = text\n }\n }\n put('stdout', stdout, stdoutBinary)\n put('stderr', stderr, stderrBinary)\n return {\n files,\n binaryFiles,\n inline,\n truncated,\n captureLimitReached,\n binary: stdoutBinary || stderrBinary,\n }\n}\n","import { request as httpRequest } from 'node:http'\nimport type { ToolCallId } from '../core/model.js';\nimport { DEFAULT_TOOL_OUTPUT_LIMITS } from '../core/model.js';\nimport { DockerMuxCapture } from './output-capture.js'\n\nexport interface DockerImageInspect {\n readonly id: string\n readonly digest?: string\n}\n\nexport interface DockerContainerInspect {\n readonly id: string\n readonly running: boolean\n readonly image: string\n}\n\n/** Minimal container row for listing (P0-B GC scans by label/name/image). */\nexport interface DockerContainerListItem {\n readonly id: string\n readonly name: string\n readonly image: string\n readonly running: boolean\n readonly labels?: Readonly<Record<string, string>>\n}\n\nexport interface DockerListContainersInput {\n readonly all?: boolean\n /** Docker label filter: `key=value`. */\n readonly label?: string\n}\n\nexport interface DockerHostConfig {\n readonly privileged?: boolean\n readonly networkMode?: string\n readonly pidMode?: string\n readonly ipcMode?: string\n readonly capAdd?: readonly string[]\n readonly binds?: readonly string[]\n}\n\nexport interface DockerCreateContainerInput {\n readonly image: string\n readonly name?: string\n readonly cmd?: readonly string[]\n readonly env?: readonly string[]\n readonly labels?: Readonly<Record<string, string>>\n readonly hostConfig: DockerHostConfig\n}\n\nexport interface DockerExecInput {\n readonly containerId: string\n readonly argv: readonly string[]\n readonly cwd?: string\n readonly env?: readonly string[]\n readonly timeoutMs?: number\n readonly abort?: AbortSignal\n readonly maxTotalCapturedBytes?: number\n readonly onExecCreated?: (execId: string) => void\n}\n\nexport interface DockerExecResult {\n readonly execId: string\n readonly exitCode: number\n readonly stdout: Buffer\n readonly stderr: Buffer\n readonly truncated: boolean\n readonly captureLimitReached: boolean\n}\n\nexport interface DockerEngineClient {\n inspectImage(ref: string): Promise<DockerImageInspect | undefined>\n buildImage(input: { contextDir: string; dockerfile: string; tag: string }): Promise<DockerImageInspect>\n pullImage(image: string): Promise<DockerImageInspect>\n createContainer(input: DockerCreateContainerInput): Promise<{ containerId: string }>\n inspectContainer(containerId: string): Promise<DockerContainerInspect | undefined>\n startContainer(containerId: string): Promise<void>\n stopContainer(containerId: string): Promise<void>\n removeContainer(containerId: string): Promise<void>\n /** List containers (P0-B GC). `label` filters on `dsh.pentester.*` labels. */\n listContainers(input?: DockerListContainersInput): Promise<readonly DockerContainerListItem[]>\n exec(input: DockerExecInput): Promise<DockerExecResult>\n cancelExec(execId: string): Promise<'confirmed' | 'requested' | 'unknown'>\n}\n\n/**\n * The only extra capabilities a Toolbox container may receive. nmap's file\n * capabilities (`cap_net_bind_service,cap_net_admin,cap_net_raw=eip`) require\n * these to be present in the container's capability set, or exec fails with\n * `Operation not permitted`. This stays a strict whitelist: arbitrary capAdd\n * (e.g. SYS_ADMIN, DAC_OVERRIDE) remains forbidden.\n */\nexport const ALLOWED_CONTAINER_CAP_ADD: readonly string[] = ['NET_ADMIN', 'NET_RAW', 'NET_BIND_SERVICE']\n\nexport function assertSafeHostConfig(config: DockerHostConfig): void {\n if (config.privileged === true) {\n throw new Error('privileged containers are not allowed')\n }\n if (config.networkMode === 'host') {\n throw new Error('host network is not allowed')\n }\n if (config.pidMode === 'host') {\n throw new Error('host pid namespace is not allowed')\n }\n if (config.ipcMode === 'host') {\n throw new Error('host ipc namespace is not allowed')\n }\n for (const cap of config.capAdd ?? []) {\n if (!ALLOWED_CONTAINER_CAP_ADD.includes(cap)) {\n throw new Error(`capability ${cap} is not allowed`)\n }\n }\n for (const bind of config.binds ?? []) {\n if (/docker\\.sock/i.test(bind) || bind.includes(':/') && bind.startsWith('/:') ) {\n throw new Error('forbidden bind')\n }\n if (bind === '/' || bind.startsWith('/:') || /(^|:)\\/:/.test(bind)) {\n throw new Error('forbidden bind')\n }\n }\n}\n\nexport const SAFE_CREATE_HOST_CONFIG: DockerHostConfig = {\n privileged: false,\n networkMode: 'bridge',\n pidMode: '',\n ipcMode: '',\n capAdd: [...ALLOWED_CONTAINER_CAP_ADD],\n}\nexport function dockerSocketPath(): string {\n return process.env.DOCKER_HOST?.startsWith('unix://')\n ? process.env.DOCKER_HOST.slice('unix://'.length)\n : '/var/run/docker.sock'\n}\n\nexport class UnixDockerEngine implements DockerEngineClient {\n readonly kind = 'unix' as const\n firewallObjectsCreated = 0\n killExecStarts = 0\n lastKillPid?: number\n private readonly socketPath: string\n private readonly api: string\n private readonly inflight = new Map<string, { abort: AbortController; containerId: string }>()\n\n constructor(socketPath = dockerSocketPath(), api = 'v1.41') {\n this.socketPath = socketPath\n this.api = api\n }\n\n async inspectImage(ref: string): Promise<DockerImageInspect | undefined> {\n const encoded = encodeURIComponent(ref)\n const res = await this.json('GET', `/images/${encoded}/json`)\n if (res.status === 404) {\n return undefined\n }\n if (res.status >= 400) {\n throw new Error(`inspect image failed: ${res.status}`)\n }\n const body = res.body as { Id?: string; RepoDigests?: string[] }\n return { id: body.Id ?? ref, digest: body.RepoDigests?.[0] }\n }\n\n async buildImage(input: { contextDir: string; dockerfile: string; tag: string }): Promise<DockerImageInspect> {\n const { packDockerContext } = await import('./docker-tar.js')\n const tar = await packDockerContext(input.contextDir, input.dockerfile)\n const res = await this.raw(\n 'POST',\n `/build?t=${encodeURIComponent(input.tag)}&dockerfile=${encodeURIComponent(input.dockerfile)}`,\n tar,\n 'application/x-tar',\n )\n if (res.status >= 400) {\n throw new Error(`build failed: ${res.status} ${res.body.toString('utf8').slice(0, 200)}`)\n }\n const inspect = await this.inspectImage(input.tag)\n if (inspect === undefined) {\n throw new Error('build completed but image missing')\n }\n return inspect\n }\n\n async pullImage(image: string): Promise<DockerImageInspect> {\n const [name, tag] = image.includes(':') ? image.split(/:(?=[^:]+$)/) : [image, 'latest']\n const res = await this.raw(\n 'POST',\n `/images/create?fromImage=${encodeURIComponent(name ?? image)}&tag=${encodeURIComponent(tag ?? 'latest')}`,\n )\n if (res.status >= 400) {\n throw new Error(`pull failed: ${res.status}`)\n }\n const inspect = await this.inspectImage(image)\n if (inspect === undefined) {\n throw new Error('pull completed but image missing')\n }\n return inspect\n }\n\n async createContainer(input: DockerCreateContainerInput): Promise<{ containerId: string }> {\n assertSafeHostConfig(input.hostConfig)\n const res = await this.json('POST', `/containers/create${input.name !== undefined ? `?name=${encodeURIComponent(input.name)}` : ''}`, {\n Image: input.image,\n Cmd: input.cmd ?? ['sleep', 'infinity'],\n Env: input.env ?? [],\n Labels: input.labels ?? {},\n HostConfig: {\n Privileged: false,\n NetworkMode: input.hostConfig.networkMode ?? 'bridge',\n PidMode: input.hostConfig.pidMode ?? '',\n IpcMode: input.hostConfig.ipcMode ?? '',\n CapAdd: [...(input.hostConfig.capAdd ?? [])],\n Binds: input.hostConfig.binds ?? [],\n SecurityOpt: ['no-new-privileges'],\n },\n })\n if (res.status >= 400) {\n throw new Error(`create container failed: ${res.status} ${JSON.stringify(res.body)}`)\n }\n return { containerId: (res.body as { Id: string }).Id }\n }\n\n async inspectContainer(containerId: string): Promise<DockerContainerInspect | undefined> {\n const res = await this.json('GET', `/containers/${encodeURIComponent(containerId)}/json`)\n if (res.status === 404) {\n return undefined\n }\n if (res.status >= 400) {\n throw new Error(`inspect container failed: ${res.status}`)\n }\n const body = res.body as { Id: string; State?: { Running?: boolean }; Config?: { Image?: string }; Image?: string }\n return {\n id: body.Id,\n running: body.State?.Running === true,\n image: body.Config?.Image ?? body.Image ?? '',\n }\n }\n\n async startContainer(containerId: string): Promise<void> {\n const res = await this.raw('POST', `/containers/${encodeURIComponent(containerId)}/start`)\n if (res.status >= 400 && res.status !== 304) {\n throw new Error(`start container failed: ${res.status}`)\n }\n }\n\n async stopContainer(containerId: string): Promise<void> {\n const res = await this.raw('POST', `/containers/${encodeURIComponent(containerId)}/stop`)\n if (res.status >= 400 && res.status !== 304 && res.status !== 404) {\n throw new Error(`stop container failed: ${res.status}`)\n }\n }\n\n async removeContainer(containerId: string): Promise<void> {\n const res = await this.raw('DELETE', `/containers/${encodeURIComponent(containerId)}?force=true`)\n if (res.status >= 400 && res.status !== 404) {\n throw new Error(`remove container failed: ${res.status}`)\n }\n }\n\n async listContainers(input?: DockerListContainersInput): Promise<readonly DockerContainerListItem[]> {\n const query = new URLSearchParams()\n if (input?.all === true) {\n query.set('all', '1')\n }\n if (input?.label !== undefined) {\n query.set('filters', JSON.stringify({ label: [input.label] }))\n }\n const suffix = query.toString().length > 0 ? `?${query.toString()}` : ''\n const res = await this.json('GET', `/containers/json${suffix}`)\n if (res.status >= 400) {\n throw new Error(`list containers failed: ${res.status}`)\n }\n const rows = res.body as Array<{\n Id?: string\n Names?: string[]\n Image?: string\n State?: string\n Labels?: Record<string, string>\n }>\n return rows.map((row) => ({\n id: row.Id ?? '',\n name: row.Names?.[0]?.replace(/^\\//, '') ?? '',\n image: row.Image ?? '',\n running: row.State === 'running',\n ...(row.Labels === undefined ? {} : { labels: row.Labels }),\n })).filter((item) => item.id.length > 0)\n }\n\n async exec(input: DockerExecInput): Promise<DockerExecResult> {\n const created = await this.json('POST', `/containers/${encodeURIComponent(input.containerId)}/exec`, {\n AttachStdout: true,\n AttachStderr: true,\n Cmd: input.argv,\n WorkingDir: input.cwd,\n Env: input.env,\n })\n if (created.status >= 400) {\n throw new Error(`exec create failed: ${created.status}`)\n }\n const execId = (created.body as { Id: string }).Id\n input.onExecCreated?.(execId)\n const abort = new AbortController()\n this.inflight.set(execId, { abort, containerId: input.containerId })\n if (input.abort !== undefined) {\n input.abort.addEventListener('abort', () => abort.abort())\n }\n const limit = input.maxTotalCapturedBytes ?? DEFAULT_TOOL_OUTPUT_LIMITS.maxTotalCapturedBytes\n try {\n const started = await this.streamExecStart(execId, abort.signal, limit)\n if (started.status >= 400) {\n throw new Error(`exec start failed: ${started.status}`)\n }\n const inspected = await this.json('GET', `/exec/${encodeURIComponent(execId)}/json`)\n const exitCode = (inspected.body as { ExitCode?: number }).ExitCode ?? 0\n return {\n execId,\n exitCode,\n stdout: started.stdout,\n stderr: started.stderr,\n truncated: started.truncated,\n captureLimitReached: started.captureLimitReached,\n }\n } finally {\n this.inflight.delete(execId)\n }\n }\n\n async cancelExec(execId: string): Promise<'confirmed' | 'requested' | 'unknown'> {\n const current = this.inflight.get(execId)\n const inspected = await this.json('GET', `/exec/${encodeURIComponent(execId)}/json`)\n const pid = (inspected.body as { Pid?: number }).Pid\n if (typeof pid === 'number' && pid > 0 && current !== undefined) {\n this.lastKillPid = pid\n const kill = await this.json('POST', `/containers/${encodeURIComponent(current.containerId)}/exec`, {\n AttachStdout: false,\n AttachStderr: false,\n Cmd: ['kill', '-TERM', String(pid)],\n })\n if (kill.status < 400) {\n const killId = (kill.body as { Id?: string }).Id\n if (typeof killId === 'string' && killId.length > 0) {\n await this.raw(\n 'POST',\n `/exec/${encodeURIComponent(killId)}/start`,\n Buffer.from(JSON.stringify({ Detach: true, Tty: false })),\n 'application/json',\n )\n this.killExecStarts += 1\n }\n }\n current.abort.abort()\n return 'requested'\n }\n current?.abort.abort()\n return current !== undefined ? 'requested' : 'unknown'\n }\n\n private streamExecStart(\n execId: string,\n signal: AbortSignal,\n maxTotalCapturedBytes: number,\n ): Promise<{\n status: number\n stdout: Buffer\n stderr: Buffer\n truncated: boolean\n captureLimitReached: boolean\n }> {\n return new Promise((resolve, reject) => {\n const capture = new DockerMuxCapture(maxTotalCapturedBytes)\n const body = Buffer.from(JSON.stringify({ Detach: false, Tty: false }))\n const req = httpRequest({\n socketPath: this.socketPath,\n path: `/${this.api}/exec/${encodeURIComponent(execId)}/start`,\n method: 'POST',\n headers: {\n Host: 'localhost',\n 'Content-Type': 'application/json',\n 'Content-Length': body.length,\n },\n }, (res) => {\n res.on('data', (chunk) => {\n capture.pushMuxed(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk))\n })\n res.on('end', () => {\n resolve({\n status: res.statusCode ?? 0,\n stdout: capture.inner.stdout(),\n stderr: capture.inner.stderr(),\n truncated: capture.inner.truncated,\n captureLimitReached: capture.inner.captureLimitReached,\n })\n })\n })\n req.on('error', reject)\n signal.addEventListener('abort', () => {\n req.destroy(new Error('aborted'))\n })\n req.write(body)\n req.end()\n })\n }\n\n private json(method: string, path: string, body?: unknown): Promise<{ status: number; body: unknown }> {\n const payload = body === undefined ? undefined : Buffer.from(JSON.stringify(body))\n return this.raw(method, path, payload, 'application/json').then((res) => ({\n status: res.status,\n body: res.body.length === 0 ? {} : JSON.parse(res.body.toString('utf8')) as unknown,\n }))\n }\n\n private raw(\n method: string,\n path: string,\n body?: Buffer,\n contentType?: string,\n signal?: AbortSignal,\n ): Promise<{ status: number; body: Buffer }> {\n return new Promise((resolve, reject) => {\n const req = httpRequest({\n socketPath: this.socketPath,\n path: `/${this.api}${path}`,\n method,\n headers: {\n Host: 'localhost',\n ...(contentType !== undefined ? { 'Content-Type': contentType } : {}),\n ...(body !== undefined ? { 'Content-Length': body.length } : {}),\n },\n }, (res) => {\n const chunks: Buffer[] = []\n res.on('data', (chunk) => {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk))\n })\n res.on('end', () => {\n resolve({ status: res.statusCode ?? 0, body: Buffer.concat(chunks) })\n })\n })\n req.on('error', reject)\n signal?.addEventListener('abort', () => {\n req.destroy(new Error('aborted'))\n })\n if (body !== undefined) {\n req.write(body)\n }\n req.end()\n })\n }\n}\n\nexport function toolCallAbortKey(toolCallId: ToolCallId): string {\n return toolCallId\n}\n","import type { EngagementId } from '../core/model.js';\nimport type { ToolboxId } from '../core/model.js';\nimport type { DockerEngineClient, DockerImageInspect } from './docker-engine.js'\nimport { SAFE_CREATE_HOST_CONFIG } from './docker-engine.js'\nimport { readJsonFile, writeJsonAtomic } from './persist.js'\n\n/**\n * P0-B: ONE persistent execution container per Engagement × runtime-profile.\n *\n * Container ownership changed: `agentRunId`/`taskId` NEVER participate in the\n * container identity. The key is `engagementId + runtimeProfile` (the factory\n * Kali image is the single runtime profile in the MVP), so every Worker /\n * Task / Stage of an Engagement reuses the SAME container. Provenance\n * (taskId / agentRunId / toolCallId) stays in the audit log only.\n *\n * Lifecycle:\n * - lazy create on first execution (ensure)\n * - Task completed / AgentRun completed / Session ended → KEEP\n * - Engagement CLOSED → stop + remove\n * - explicit reset → stop + remove + recreate\n * - plugin restart → recover/reuse (binding store read + engine inspect)\n * - orphan/stale managed containers (no live Engagement) → GC removes\n *\n * Concurrency: `ensure` is singleflighted per key — N concurrent first-time\n * callers create exactly ONE container and all receive the same id.\n */\n\nexport const PENTESTER_MANAGED_LABEL = 'dsh.pentester.managed'\nexport const PENTESTER_ENGAGEMENT_LABEL = 'dsh.pentester.engagement'\nexport const PENTESTER_RUNTIME_PROFILE_LABEL = 'dsh.pentester.runtime-profile'\nexport const PENTESTER_WORKSPACE_LABEL = 'dsh.pentester.workspace'\nexport const PENTESTER_MANAGED_TRUE = 'true'\n\n/** MVP runtime profile: the single factory Kali image. */\nexport const RUNTIME_PROFILE_KALI_DEFAULT = 'kali-default'\n\nexport interface EngagementRuntimeProfile {\n readonly engagementId: EngagementId\n /** Runtime profile id (MVP: kali-default). */\n readonly profile: string\n /** Image ref backing this profile (factory Kali image). */\n readonly image: string\n /** Trusted host workspace path mounted to /workspace (never model-supplied). */\n readonly workspacePath?: string\n /** Engagement workspace path mounted to /artifacts. */\n readonly artifactsPath?: string\n /** Stable workspace identity label value (hashed, never the raw path). */\n readonly workspaceLabel?: string\n /** P1-4: human-readable slug for the container name (never a UUID). */\n readonly nameSlug?: string\n}\n\nexport interface EngagementContainerBinding {\n readonly engagementId: EngagementId\n readonly profile: string\n readonly image: string\n readonly containerId: string\n readonly imageId?: string\n readonly imageSource?: 'existing' | 'built' | 'pulled'\n readonly createdAt: string\n}\n\nexport interface EnsureEngagementContainerInput {\n readonly profile: EngagementRuntimeProfile\n readonly engine: DockerEngineClient\n readonly resolvedImage: DockerImageInspect\n /** Image resolve source (existing/built/pulled) for audit. */\n readonly imageSource: 'existing' | 'built' | 'pulled'\n}\n\nexport class EngagementContainerStore {\n private readonly path: string\n private bindings: Record<string, EngagementContainerBinding>\n /** singleflight: key → in-flight ensure promise. */\n private readonly inflight = new Map<string, Promise<string>>()\n\n private constructor(path: string, bindings: Record<string, EngagementContainerBinding>) {\n this.path = path\n this.bindings = bindings\n }\n\n static keyOf(engagementId: EngagementId, profile: string): string {\n return `${engagementId}::${profile}`\n }\n\n static async open(engagementDir: string): Promise<EngagementContainerStore> {\n const path = `${engagementDir}/runtime/engagement-containers.json`\n const raw = await readJsonFile(path)\n const bindings: Record<string, EngagementContainerBinding> =\n raw !== null && typeof raw === 'object' && !Array.isArray(raw) && 'bindings' in raw\n ? ((raw as { bindings: Record<string, EngagementContainerBinding> }).bindings ?? {})\n : {}\n return new EngagementContainerStore(path, bindings)\n }\n\n list(): readonly EngagementContainerBinding[] {\n return Object.values(this.bindings)\n }\n\n get(key: string): EngagementContainerBinding | undefined {\n return this.bindings[key]\n }\n\n /** Remove a binding record (after the container itself is removed). */\n async remove(key: string): Promise<void> {\n if (this.bindings[key] === undefined) {\n return\n }\n const next = { ...this.bindings }\n delete next[key]\n this.bindings = next\n await writeJsonAtomic(this.path, { bindings: this.bindings })\n }\n\n async put(binding: EngagementContainerBinding): Promise<void> {\n const key = EngagementContainerStore.keyOf(binding.engagementId, binding.profile)\n this.bindings = { ...this.bindings, [key]: binding }\n await writeJsonAtomic(this.path, { bindings: this.bindings })\n }\n\n /**\n * Ensure exactly ONE running container exists for the profile. Concurrency\n * safe: concurrent first-time callers singleflight to a single create.\n * Returns the container id.\n */\n ensure(input: EnsureEngagementContainerInput): Promise<string> {\n const key = EngagementContainerStore.keyOf(input.profile.engagementId, input.profile.profile)\n const inflight = this.inflight.get(key)\n if (inflight !== undefined) {\n return inflight\n }\n const run = this.ensureInner(key, input)\n this.inflight.set(key, run)\n void run.finally(() => {\n this.inflight.delete(key)\n })\n return run\n }\n\n private async ensureInner(\n key: string,\n input: EnsureEngagementContainerInput,\n ): Promise<string> {\n const existing = this.bindings[key]\n if (existing !== undefined) {\n const inspect = await input.engine.inspectContainer(existing.containerId)\n if (inspect === undefined) {\n // Container was removed out-of-band → recreate.\n return this.createAndRecord(key, input)\n }\n if (!inspect.running) {\n await input.engine.startContainer(existing.containerId)\n }\n return existing.containerId\n }\n return this.createAndRecord(key, input)\n }\n\n private async createAndRecord(\n key: string,\n input: EnsureEngagementContainerInput,\n ): Promise<string> {\n const profile = input.profile\n const binds: string[] = []\n if (profile.workspacePath !== undefined && profile.workspacePath.length > 0) {\n const { mkdir } = await import('node:fs/promises')\n await mkdir(profile.workspacePath, { recursive: true })\n binds.push(`${profile.workspacePath}:/workspace`)\n if (profile.artifactsPath !== undefined && profile.artifactsPath.length > 0) {\n await mkdir(profile.artifactsPath, { recursive: true })\n binds.push(`${profile.artifactsPath}:/artifacts`)\n }\n } else if (profile.artifactsPath !== undefined && profile.artifactsPath.length > 0) {\n // Legacy fallback: no trusted workspace → engagement workspace is /workspace.\n const { mkdir } = await import('node:fs/promises')\n await mkdir(profile.artifactsPath, { recursive: true })\n binds.push(`${profile.artifactsPath}:/workspace`)\n }\n const created = await input.engine.createContainer({\n image: profile.image,\n name: engagementContainerName(profile.engagementId, profile.profile, profile.nameSlug),\n cmd: ['sleep', 'infinity'],\n env: [],\n labels: {\n [PENTESTER_MANAGED_LABEL]: PENTESTER_MANAGED_TRUE,\n [PENTESTER_ENGAGEMENT_LABEL]: profile.engagementId,\n [PENTESTER_RUNTIME_PROFILE_LABEL]: profile.profile,\n ...(profile.workspaceLabel === undefined ? {} : { [PENTESTER_WORKSPACE_LABEL]: profile.workspaceLabel }),\n },\n hostConfig: {\n ...SAFE_CREATE_HOST_CONFIG,\n binds,\n },\n })\n await input.engine.startContainer(created.containerId)\n const binding: EngagementContainerBinding = {\n engagementId: profile.engagementId,\n profile: profile.profile,\n image: profile.image,\n containerId: created.containerId,\n imageId: input.resolvedImage.id,\n ...(input.resolvedImage.digest === undefined ? {} : { imageSource: input.imageSource }),\n createdAt: new Date().toISOString(),\n }\n // In the (rare) race where a concurrent ensure already recorded a binding\n // whose container still EXISTS after our create, prefer the recorded one\n // and remove our duplicate. A stale recorded binding (container removed\n // out-of-band) is NOT preferred — we keep the freshly created one.\n const raced = this.bindings[key]\n if (raced !== undefined && raced.containerId !== created.containerId) {\n const racedInspect = await input.engine.inspectContainer(raced.containerId)\n if (racedInspect !== undefined) {\n try {\n await input.engine.removeContainer(created.containerId)\n } catch {\n // best-effort\n }\n return raced.containerId\n }\n }\n await this.put(binding)\n return created.containerId\n }\n\n /** Remove + recreate the container for a profile (explicit reset). */\n async reset(\n key: string,\n input: EnsureEngagementContainerInput,\n ): Promise<string> {\n const existing = this.bindings[key]\n if (existing !== undefined) {\n try {\n await input.engine.stopContainer(existing.containerId)\n } catch {\n // best-effort\n }\n try {\n await input.engine.removeContainer(existing.containerId)\n } catch {\n // best-effort\n }\n await this.remove(key)\n }\n return this.ensure(input)\n }\n\n /**\n * Engagement CLOSED: stop + remove the managed container and forget the\n * binding. Returns the removed container id, if any.\n */\n async closeEngagement(engagementId: EngagementId, engine: DockerEngineClient): Promise<string | undefined> {\n const keys = Object.keys(this.bindings).filter((key) => key.startsWith(`${engagementId}::`))\n let removed: string | undefined\n for (const key of keys) {\n const binding = this.bindings[key]\n if (binding === undefined) continue\n try {\n await engine.stopContainer(binding.containerId)\n } catch {\n // best-effort\n }\n try {\n await engine.removeContainer(binding.containerId)\n } catch {\n // best-effort\n }\n removed = binding.containerId\n await this.remove(key)\n }\n return removed\n }\n}\n\n/** Deterministic container name: readable slug + profile (never agentRun/taskId,\n * never a UUID). E.g. `pentest-192-168-107-5-kali`. Falls back to a short\n * engagement hash only when no readable slug is available. */\nexport function engagementContainerName(\n engagementId: EngagementId,\n profile: string,\n slug?: string,\n): string {\n const readable = slug !== undefined && slug.length > 0 ? safeName(slug) : `eng-${safeName(engagementId).slice(0, 12)}`\n const profileTail = profile === 'kali-default' ? 'kali' : safeName(profile)\n return `pentest-${readable}-${profileTail}`\n}\n\nfunction safeName(value: string): string {\n return value.replace(/[^a-zA-Z0-9_.-]/g, '').slice(0, 40) || 'x'\n}\n\nexport type { ToolboxId }\n"],"mappings":"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"}
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import Schema from "@deepseek-ai/schemastery";
|
|
2
|
+
import { Context } from "@deepseek-ai/cordis";
|
|
3
|
+
//#region src/model.d.ts
|
|
4
|
+
interface AgentProfile {
|
|
5
|
+
readonly id: string;
|
|
6
|
+
readonly name: string;
|
|
7
|
+
readonly description: string;
|
|
8
|
+
readonly systemPrompt: string;
|
|
9
|
+
readonly skills: readonly string[];
|
|
10
|
+
readonly mcpServers: readonly string[];
|
|
11
|
+
readonly model?: string;
|
|
12
|
+
}
|
|
13
|
+
//#endregion
|
|
14
|
+
//#region src/index.d.ts
|
|
15
|
+
declare const name = "dsh-pentester";
|
|
16
|
+
declare const inject: string[];
|
|
17
|
+
interface Config {
|
|
18
|
+
verbose: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Global docker host (unix:///path or http://host:port). Fallback chain:
|
|
21
|
+
* this config > DOCKER_HOST > /var/run/docker.sock.
|
|
22
|
+
*/
|
|
23
|
+
dockerHost?: string;
|
|
24
|
+
/** Optional image tag overrides keyed by toolbox name (default: kali). */
|
|
25
|
+
toolboxImages?: Record<string, string>;
|
|
26
|
+
}
|
|
27
|
+
declare const Config: Schema<Config>;
|
|
28
|
+
declare function apply(ctx: Context, config: Config): Promise<void>;
|
|
29
|
+
/** run-state.mjs 读取的 stage → AgentProfile 清单文件名(与 preset 同目录发布)。 */
|
|
30
|
+
declare const STAGE_PROFILES_FILE = "stage-profiles.json";
|
|
31
|
+
/**
|
|
32
|
+
* 生成并发布 stage → [{id, name}] 清单(docs/plan.md §7:Root compact state
|
|
33
|
+
* 的 Available AgentProfiles 从 STAGE_DEFINITIONS + AgentProfile registry 生成,
|
|
34
|
+
* 不硬编码第二套映射)。随 preset 目录发布,run-state.mjs 同目录读取。
|
|
35
|
+
*/
|
|
36
|
+
declare function writeStageProfilesManifest(presetDir: string, profiles: ReadonlyMap<string, AgentProfile>): Promise<void>;
|
|
37
|
+
/** 把随包发布的 pentester preset 拷贝到 DSH 用户 preset 根目录。 */
|
|
38
|
+
declare function publishPentesterPreset(home?: string): Promise<string | undefined>;
|
|
39
|
+
//#endregion
|
|
40
|
+
export { Config, STAGE_PROFILES_FILE, apply, inject, name, publishPentesterPreset, writeStageProfilesManifest };
|
|
41
|
+
//# sourceMappingURL=index.d.ts.map
|