Back to skill

Security audit

Agent-manager-for-AI-planner

Security checks for vulnerabilities and agentic risk

Overview

Agent Manager is a coherent orchestration service, but its shared-deployment controls have serious access-control and outbound-safety gaps that users should review before installing.

Install only in a tightly controlled environment unless the access-control issues are fixed. Avoid shared or multi-tenant use, keep tool registration disabled, do not configure callback credentials for untrusted registrants, restrict outbound provider use explicitly, and treat persisted run files as sensitive plaintext records.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Tool Hijacking and SpoofingModifies or replaces tools so legitimate-looking calls execute attacker logic
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
Findings (7)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
src/app.ts:46
Finding
Broken read authentication permits cross-tenant run disclosure<![CDATA[ ## Vulnerability Details **File Location**: `src/app.ts:46-51`, affecting run read routes at `src/app.ts:292-388` and `src/app.ts:400-407` **Vulnerability Type**: Broken authentication and missing object-level authorization **Risk Level**: High ### Vulnerable Code ```ts const requireReadToken = (req: express.Request): { ok: true } | { ok: false } => { const token = req.header('X-Run-Token')?.trim(); if (tokenRequired()) return token ? { ok: true } : { ok: false }; if (getConfig().ALLOW_ANONYMOUS_READ || getConfig().NODE_ENV === 'test') return { ok: true }; return token ? { ok: true } : { ok: false }; }; ``` Affected routes retrieve a run solely by its identifier: ```ts app.get('/v1/run/:id', (req, res) => { const token = requireReadToken(req); if (!token.ok) return sendError(res, 401, { code: 'AUTH_INVALID_TOKEN', message: 'Missing or invalid X-Run-Token', retryable: false, at: 'auth' }); const run = orchestrator.getRun(req.params.id); if (!run) return sendError(res, 404, { code: 'RUN_NOT_FOUND', message: 'Run not found', retryable: false, at: req.params.id }); return res.json(run); }); ``` ### Technical Analysis When `REQUIRE_RUN_TOKEN=1`, `requireReadToken` accepts any non-empty `X-Run-Token`. It does not validate the supplied value against `RUN_TOKENS`, unlike `parseTokenOwner`. After this incomplete authentication check, the read endpoints do not compare the authenticated principal with `run.token_owner`. This creates both an authentication bypass and an insecure direct object reference. The same pattern affects: - `GET /v1/run/:id` - `GET /v1/run/:id/stream` - `GET /v1/run/:id/report` - `GET /v1/run/:id/replay` - `GET /v1/run/:id/events` Run identifiers are generated using cryptographically strong UUIDs, so blind enumeration is difficult. However, any run ID leaked through logs, browser history, telemetry, links, or another application defect becomes sufficient to exploit the issue. ...[truncated 738 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace `requireReadToken` with a function that validates the token against `RUN_TOKENS` and returns the authenticated owner. 2. Require `run.token_owner === authenticatedOwner` on every run-scoped read route. 3. Perform ownership checks inside the orchestrator or storage layer so future routes cannot bypass them. 4. Return a uniform `404` for nonexistent and unauthorized run IDs to reduce object discovery. 5. Add tests using two valid tenants and an invalid token for every run read endpoint. 6. Do not treat possession of a run UUID as authorization. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
src/app.ts:283
Finding
Missing ownership checks permit cross-tenant cancellation and result injection<![CDATA[ ## Vulnerability Details **File Location**: `src/app.ts:283-290` and `src/app.ts:390-398` **Vulnerability Type**: Broken object-level authorization **Risk Level**: High ### Vulnerable Code ```ts app.post('/v1/run/:id/cancel', (req, res) => { const token = parseTokenOwner(req); if (!token.ok) return sendError(res, 401, { code: 'AUTH_INVALID_TOKEN', message: 'Missing or invalid X-Run-Token', retryable: false, at: 'auth' }); const run = orchestrator.cancelRun(req.params.id); if (!run) return sendError(res, 404, { code: 'RUN_NOT_FOUND', message: 'Run not found', retryable: false, at: req.params.id }); return res.json(run); }); ``` ```ts app.post('/v1/run/:id/task/:name/inject', (req, res) => { const token = parseTokenOwner(req); if (!token.ok) return sendError(res, 401, { code: 'AUTH_INVALID_TOKEN', message: 'Missing or invalid X-Run-Token', retryable: false, at: 'auth' }); const payload = req.body?.payload; const meta = req.body?.meta; const updated = orchestrator.injectTaskResult( req.params.id, req.params.name, payload, meta ); if (!updated) return sendError(res, 404, { code: 'RUN_NOT_FOUND', message: 'Run not found', retryable: false, at: req.params.id }); return res.json({ ok: true, run_id: updated.id, task: req.params.name }); }); ``` ### Technical Analysis Both mutation routes authenticate a token through `parseTokenOwner`, but the resulting `tokenOwner` is never used to authorize access to the target run. The orchestrator methods receive only the run ID and therefore cannot enforce tenant ownership at these call sites. This means authentication as any valid tenant is sufficient to mutate another tenant's run when its UUID is known. Task-result injection is particularly dangerous because injected payloads may become dependency input or final output. It therefore compromises both integrity and confidentiality of the orchestration ...[truncated 822 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Retrieve the target run before mutation and verify that its `token_owner` matches the authenticated owner. 2. Change mutation APIs to require the owner explicitly, for example: `cancelRun(runId, owner)` and `injectTaskResult(runId, owner, taskName, payload, meta)`. 3. Enforce the ownership comparison atomically inside the orchestrator or run store. 4. Restrict result injection to appropriate run and task states. 5. Validate injected payload and metadata against explicit schemas and size limits. 6. Add cross-tenant negative tests for cancellation and injection. ]]>

T07 · Tool Hijacking and Spoofing

Error
Location
src/app.ts:138
Finding
Unauthenticated global callback-tool registration enables tool hijacking<![CDATA[ ## Vulnerability Details **File Location**: `src/app.ts:138-157` and `src/services/tools.ts:120-174` **Vulnerability Type**: Unauthenticated tool registration and global handler replacement **Risk Level**: High ### Vulnerable Code ```ts app.post('/v1/tools/register', (req, res) => { if (((getConfig().ENABLE_TOOL_REGISTER ?? getConfig().ENABLE_TOOL_REGISTRATION) !== '1')) { return sendError(res, 403, { code: 'TOOL_REGISTER_DISABLED', message: 'Tool registration is disabled', retryable: false, at: 'tools/register' }); } const parse = z.array(ToolSpecSchema).safeParse(req.body?.tools ?? req.body); if (!parse.success) { return sendError(res, 400, { code: 'TOOL_SPEC_INVALID', message: parse.error.message, retryable: false, at: 'tools/register' }); } try { return res.json(orchestrator.getTools().register(parse.data)); } catch (error) { // Error handling omitted } }); ``` Registration globally replaces the tool specification and handler: ```ts this.tools.set(validated.name, validated); if (validated.callback_url) { const url = validated.callback_url; this.handlers.set(validated.name, async (input, ctx) => { // Sends the tool input to the registered callback }); } ``` ### Technical Analysis The registration endpoint checks only whether tool registration is enabled. It does not authenticate or authorize the caller. `TOOL_ALLOWLIST` limits the names that can be registered, but does not identify who may register them. Registration writes into global maps, and `Map.set` replaces any existing entry with the same name. Consequently, an unauthenticated client can redefine an allowed tool and redirect future calls to a different callback URL accepted by the destination policy. The feature is disabled by default, which reduces default exposure, but enabling the documented feature creates a high-impact shared-deployment vulnerability. ### Attack Path 1. ...[truncated 801 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require strong administrator authentication and authorization for tool registration. 2. Do not allow external requests to overwrite built-in or previously registered tools. 3. Bind externally registered tools to a tenant or isolated namespace rather than global maps. 4. Configure callback URLs server-side or require exact immutable scheme, host, port, and path rules. 5. Maintain an auditable registration history and support explicit administrative revocation. 6. Reject duplicate names unless an authenticated administrator uses a dedicated update operation. 7. Add tests proving anonymous callers and ordinary tenant tokens cannot register or replace tools. ]]>

T07 · Tool Hijacking and Spoofing

Error
Location
src/types.ts:4
Finding
Client-controlled auth references can cause misuse of environment-backed callback credentials<![CDATA[ ## Vulnerability Details **File Location**: `src/types.ts:4-11`, `src/config.ts:59`, and `src/services/tools.ts:143-153` **Vulnerability Type**: Client-controlled secret selection **Risk Level**: High ### Vulnerable Code ```ts export const ToolSpecSchema = z.object({ name: z.string().min(1), description: z.string().min(1), input_schema: JsonSchemaZod, timeout_ms: z.number().int().positive(), tags: z.array(z.string()), callback_url: z.string().url().optional(), auth_ref: z.string().min(1).optional() }); ``` ```ts export const getToolAuth = (ref: string): string | undefined => process.env[`TOOL_AUTH_${ref}`]; ``` ```ts const authHeader = validated.auth_ref ? getToolAuth(validated.auth_ref) : undefined; const body = JSON.stringify({ input, run_id: ctx.runId, token_owner: ctx.tokenOwner }); const res = await safeFetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', ...(authHeader ? { Authorization: `Bearer ${authHeader}` } : {}) }, body, timeoutMs: validated.timeout_ms, maxBytes: env.TOOL_CALLBACK_MAX_BYTES, signal }); ``` ### Technical Analysis The externally supplied tool specification controls `auth_ref`. The service converts that value into an environment variable name and sends the resulting secret as a bearer token to the selected callback. Because the registration endpoint is unauthenticated when enabled, an attacker may attempt to guess configured `TOOL_AUTH_*` suffixes and bind one to a callback URL. Destination allowlisting is a partial control, but it does not provide credential isolation where multiple parties share an allowed gateway host, wildcard domain, or path-based routing service. The credential may not be directly visible in the HTTP response, but it is transmitted to the callback destination and can be captured by an attacker who controls that endpoint. ### Attack Path 1. External tool registration is enabled. 2. The attacker guesses or learns ...[truncated 775 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove `auth_ref` from the client-controlled `ToolSpecSchema`. 2. Define a server-side mapping from immutable tool IDs to exact callback destinations and credentials. 3. Bind each credential to an exact scheme, hostname, port, and path prefix. 4. Require administrator authorization before a tool can use any credential. 5. Use scoped, short-lived credentials with minimal permissions. 6. Validate reference names against a fixed server-side enumeration if dynamic references are unavoidable. 7. Prevent callback URL changes independently of the selected credential. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
src/providers/openai.ts:25
Finding
OpenAI provider bypasses the documented deny-by-default outbound policy<![CDATA[ ## Vulnerability Details **File Location**: `src/providers/openai.ts:25-47` **Vulnerability Type**: Outbound security control bypass **Risk Level**: Medium ### Vulnerable Code ```ts const started = Date.now(); const response = await fetch( 'https://api.openai.com/v1/chat/completions', { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${getConfig().OPENAI_API_KEY}` }, signal: args.signal, body: JSON.stringify({ model: args.task.model, messages: args.messages.map((message) => ({ role: message.role, content: message.content, ...(message.role === 'tool' ? { tool_call_id: message.tool_call_id } : {}), ...(message.name ? { name: message.name } : {}) })), tools: args.tools.map((tool) => ({ type: 'function', function: { name: tool.name, description: tool.description, parameters: tool.input_schema } })), max_tokens: args.task.max_output_tokens, temperature: 0 }) } ); ``` ### Technical Analysis The OpenAI provider uses native `fetch` directly. It does not invoke `safeFetch` or `checkOutboundUrl`, so it ignores the configured outbound allowlists. This contradicts the declared security behavior in `skill.md`, which states that provider calls require an outbound allowlist and that traffic is blocked by default until allowlists are configured. The implementation also omits: - `MAX_PROVIDER_REQUEST_BYTES` enforcement - A provider-specific timeout when `args.signal` has no deadline - Response body size enforcement - Explicit `redirect: "error"` Transmitting task messages and tool definitions is necessary for OpenAI functionality, but bypassing the central outbound policy exceeds the documented minimum privileges. ### Attack Path 1. An operator configures `OPENAI_API_KEY` but leaves outbound allowlists empty, relying on the documented ...[truncated 607 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Route OpenAI requests through the same hardened outbound client used by callback tools. 2. Require an explicit exact-host allowlist entry for `api.openai.com`. 3. Enforce `MAX_PROVIDER_REQUEST_BYTES` before transmission. 4. Apply a bounded timeout even when the caller does not provide one. 5. Set `redirect: "error"` explicitly. 6. Enforce a maximum response body size before JSON parsing. 7. Add tests proving that OpenAI calls fail when outbound policy is unconfigured. 8. Align documentation and implementation so all providers use one outbound security policy. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
src/lib/safe-fetch.ts:41
Finding
Outbound destination validation is vulnerable to DNS rebinding and time-of-check/time-of-use races<![CDATA[ ## Vulnerability Details **File Location**: `src/lib/safe-fetch.ts:41-64` and `src/lib/safe-fetch.ts:88-102`; also `src/providers/gateway.ts:43-72` **Vulnerability Type**: DNS rebinding and SSRF protection bypass **Risk Level**: Medium ### Vulnerable Code ```ts const enforceDestination = async ( url: URL, resolverOverride?: ( host: string ) => Promise<Array<{ address: string }>> ): Promise<void> => { const cfg = getConfig(); const host = url.hostname.toLowerCase(); // Scheme and allowlist checks omitted const addrs = resolverOverride ? await resolverOverride(host) : await lookup(host, { all: true, verbatim: true }); if (addrs.length === 0) throw new Error('OUTBOUND_DNS_EMPTY'); for (const addr of addrs) { if (isBlockedIp(addr.address)) { throw new Error('OUTBOUND_PRIVATE_ADDRESS_BLOCKED'); } } }; ``` ```ts export const safeFetch = async ( rawUrl: string, opts: SafeFetchOptions ): Promise<{ status: number; headers: Headers; bodyText: string }> => { const url = new URL(rawUrl); await enforceDestination(url, opts.resolver); const res = await fetch(url, { method: opts.method ?? 'GET', headers: opts.headers, body: opts.body, redirect: 'error', signal }); // Response handling omitted }; ``` The gateway uses the same check-then-connect pattern: ```ts await checkOutboundUrl(endpoint, { allowlistRaw: env.OUTBOUND_ALLOWLIST, allowHttp: false }); const response = await fetch(endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json', ...(env.GATEWAY_API_KEY ? { Authorization: `Bearer ${env.GATEWAY_API_KEY}` } : {}) }, redirect: 'error', signal, body: bodyPayload }); ``` ### Technical Analysis The security check resolves the hostname and validates the returned addresses. Native `fetch` then performs a separate DNS resolution when opening the connection. The validated address is not pinned to the socket. An attacker who co ...[truncated 1116 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Resolve the hostname once and pin a validated IP address to the actual socket connection. 2. Preserve the original hostname for TLS SNI, certificate validation, and the HTTP `Host` header. 3. Use an HTTP dispatcher or agent with a guarded custom DNS lookup that validates every address immediately before connection. 4. Disable connection reuse across destinations unless the connection is bound to the validated origin. 5. Continue to reject IP literals, redirects, private addresses, link-local addresses, and unsupported schemes. 6. Add DNS-rebinding tests that return a public address for validation and a private address for connection. 7. Consolidate gateway and callback traffic through one hardened, address-pinning outbound implementation. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
src/services/run-store.ts:241
Finding
Persistent run storage writes sensitive telemetry and payloads in plaintext<![CDATA[ ## Vulnerability Details **File Location**: `src/services/run-store.ts:241-244`; redaction is output-only at `src/security/redaction.ts:35-40` **Vulnerability Type**: Plaintext sensitive-data persistence **Risk Level**: Medium ### Vulnerable Code ```ts private persistRun(run: Run): void { if (!this.persist) return; writeFileSync( join(process.cwd(), 'runs', `${run.id}.json`), JSON.stringify(run, null, 2), 'utf8' ); } ``` Redaction is applied when formatting API event output, not before persistence: ```ts export const redactEventForOutput = ( event: { [k: string]: unknown } ): { [k: string]: unknown } => ({ ...event, data: redactTelemetryValue(event.data) }); ``` ### Technical Analysis When `PERSIST_RUNS=1`, the complete `Run` object is serialized to disk. This may include: - Original plans and task inputs - Model and tool outputs - Injected task results - Event payloads - Final output - Tenant owner identifiers The telemetry redaction feature operates while producing selected API responses. It does not sanitize the run object before `persistRun` writes it. No explicit restrictive file mode is supplied to `writeFileSync`; effective permissions therefore depend on the process umask and deployment environment. The records are also not encrypted at rest. ### Attack Path 1. An operator enables `PERSIST_RUNS=1`. 2. Tenants process sensitive prompts or tool payloads. 3. The service writes complete run objects into `runs/{id}.json`. 4. API redaction may hide selected fields from responses but leaves the persisted file unchanged. 5. A local user, compromised sibling process, backup reader, or filesystem disclosure vulnerability accesses the file. 6. The raw prompt, output, and telemetry data is recovered. ### Impact Assessment The issue can expose confidential prompts, model responses, task payloads, tool outputs, injected data, and tenant identifiers. Exposure persists beyond the immediate API request and may propagate into ...[truncated 26 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Apply an explicit persistence policy that removes or redacts sensitive fields before writing. 2. Encrypt sensitive persisted fields using a deployment-managed key. 3. Create the `runs` directory with restrictive permissions and write files with mode `0600`. 4. Use atomic writes to a protected temporary file followed by rename. 5. Define and enforce retention and secure-deletion policies for persisted runs. 6. Clearly distinguish API-output redaction from at-rest redaction in configuration and documentation. 7. Avoid persisting full prompts and payloads unless replay functionality explicitly requires them. 8. Add tests confirming that enabling telemetry redaction also protects persisted data when that behavior is expected. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (40)

Known Vulnerable Dependency: vitest==3.2.4 — 2 advisory(ies): CVE-2026-47429 (When Vitest UI server is listening, arbitrary file can be read and executed); CVE-2026-84373 (Vitest: Path Traversal / Arbitrary File Read via @vitest/mocker Redirect Mock)

Critical
Category
Supply Chain
Confidence
91% confidence
Finding
vitest 3.2.4 is present and carries advisories for arbitrary file read and possible execution when the Vitest UI server is listening, as well as the @vitest/mocker traversal issue. This is dev-only, but if test UI or related services are started in reachable environments, an attacker could access sensitive files or trigger more serious compromise of developer or CI systems.

Known Vulnerable Dependency: vitest==3.2.4 — 2 advisory(ies): CVE-2026-47429 (When Vitest UI server is listening, arbitrary file can be read and executed); CVE-2026-84373 (Vitest: Path Traversal / Arbitrary File Read via @vitest/mocker Redirect Mock)

Critical
Category
Supply Chain
Confidence
93% confidence
Finding
The manifest allows installation of a Vitest version flagged as vulnerable to arbitrary file read and possible code execution in the UI/mocking components. Even though Vitest is a devDependency, dev tools are often run locally or in CI, where exploitation could expose source code, secrets, tokens, or enable further compromise; this skill context does not mitigate the risk because test tooling commonly runs in privileged build environments.

Known Vulnerable Dependency: fast-uri==3.1.0 — 7 advisory(ies): CVE-2026-13676 (fast-uri vulnerable to host confusion via failed IDN canonicalization); CVE-2026-18446 (fast-uri vulnerable to host confusion via backslash authority introducer); CVE-2026-75975 (fast-uri vulnerable to server-side request forgery via malformed IPv6 normalizat) +4 more

High
Category
Supply Chain
Confidence
88% confidence
Finding
fast-uri 3.1.0 is included via AJV and is reported with multiple host confusion and SSRF-related issues. If the application validates or normalizes attacker-controlled URLs using this library chain, malformed inputs could bypass hostname checks or alter routing decisions, making the context more dangerous than a purely cosmetic parser bug.

Known Vulnerable Dependency: nanoid==3.3.11 — 3 advisory(ies): CVE-2026-67214 (nanoid: non-secure generators can loop indefinitely with negative size); CVE-2026-67213 (nanoid: custom generators can loop indefinitely when size is zero); CVE-2026-73086 (nanoid: Integer Overflow or Wraparound)

High
Category
Supply Chain
Confidence
80% confidence
Finding
nanoid 3.3.11 is flagged for several edge-case denial-of-service/integer handling issues. In this lockfile it appears only as a transitive dev dependency through PostCSS/Vite tooling, which makes exploitation less likely in production but still a genuine risk in build or dev flows handling attacker-controlled values.

Known Vulnerable Dependency: path-to-regexp==0.1.12 — 1 advisory(ies): CVE-2024-45296 (path-to-regexp vulnerable to Regular Expression Denial of Service via multiple r)

High
Category
Supply Chain
Confidence
93% confidence
Finding
path-to-regexp 0.1.12 is a real transitive runtime dependency of Express and has a known ReDoS issue. Since route matching can be exercised remotely by HTTP requests, crafted paths may consume excessive CPU and degrade service availability, which makes this materially relevant to an agent-management service.

Known Vulnerable Dependency: picomatch==4.0.3 — 2 advisory(ies): CVE-2026-33672 (Picomatch: Method Injection in POSIX Character Classes causes incorrect Glob Mat); CVE-2026-33671 (Picomatch has a ReDoS vulnerability via extglob quantifiers)

High
Category
Supply Chain
Confidence
84% confidence
Finding
picomatch 4.0.3 is present as a dev dependency and is flagged for method injection and ReDoS issues in glob matching. Because it is used by build/test tooling rather than the application runtime, the vulnerability is real but mainly affects developer or CI contexts where untrusted glob patterns might be processed.

Known Vulnerable Dependency: postcss==8.5.6 — 4 advisory(ies): CVE-2026-45623 (PostCSS: Arbitrary file read and information disclosure via attacker-controlled ); CVE-2026-69153 (PostCSS: incomplete fix of GHSA-6g55-p6wh-862q — attacker-controlled sourceMappi); CVE-2026-41305 (PostCSS has XSS via Unescaped </style> in its CSS Stringify Output) +1 more

High
Category
Supply Chain
Confidence
86% confidence
Finding
postcss 8.5.6 is flagged for multiple issues including arbitrary file read and XSS-related output handling. In this lockfile it is a dev dependency through Vite, so the main risk is during development/build workflows, but if a dev server or transform pipeline processes attacker-controlled files or sourcemaps the impact can extend to file disclosure or browser compromise.

Known Vulnerable Dependency: rollup==4.57.1 — 1 advisory(ies): CVE-2026-27606 (Rollup 4 has Arbitrary File Write via Path Traversal)

High
Category
Supply Chain
Confidence
80% confidence
Finding
rollup 4.57.1 is reported vulnerable to arbitrary file write via path traversal. Here it is a dev dependency used by Vite tooling, so the risk is most relevant in build/dev contexts, but arbitrary write vulnerabilities can be severe if untrusted package content, plugin input, or project files are processed in CI or shared environments.

Known Vulnerable Dependency: vite==7.3.1 — 5 advisory(ies): CVE-2026-39365 (Vite Vulnerable to Path Traversal in Optimized Deps `.map` Handling); CVE-2026-53571 (vite: `server.fs.deny` bypass on Windows alternate paths); CVE-2026-39363 (Vite Vulnerable to Arbitrary File Read via Vite Dev Server WebSocket) +2 more

High
Category
Supply Chain
Confidence
90% confidence
Finding
vite 7.3.1 is flagged for several path traversal and arbitrary file read issues in development server functionality. Although it is dev-only here, Vite dev servers are commonly network-accessible during development, and this skill context does not provide controls that would reduce that risk, so the finding remains significant for developer workstations and CI preview setups.

Ae1

High
Category
analysis-evasion
Content
This `skill.md` is a compact integration guide for Clawhub and other AI clients.
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
The read-token check only verifies that some X-Run-Token header is present when run tokens are required, but it does not validate that token against the configured allowlist. This creates an authentication bypass for all read endpoints using requireReadToken(), allowing an attacker with any arbitrary token value to access protected run data.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
The cancel and task-result injection endpoints perform state-changing actions on runs identified only by URL parameter, without confirming the caller owns the target run. This allows unauthorized users to disrupt other users' executions or tamper with task outputs, compromising integrity and availability.

Context-Inappropriate Capability

High
Confidence
99% confidence
Finding
The run read, stream, report, replay, and events endpoints fetch runs by ID and return their contents without checking that the authenticated caller owns the run. Because run IDs can be guessed, leaked, or observed, this becomes an insecure direct object reference that exposes logs, outputs, plan details, telemetry, and potentially sensitive task results across tenants.

External Transmission

Medium
Category
Data Exfiltration
Content
}

    const started = Date.now();
    const response = await fetch('https://api.openai.com/v1/chat/completions', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
}

    const started = Date.now();
    const response = await fetch('https://api.openai.com/v1/chat/completions', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
87% confidence
Finding
This code performs file writes by persisting each run as a JSON file under the working directory's runs folder. The file contains no confirmation prompt, logging, or explanatory comment/docstring disclosing that run data will be stored on disk, which is a safety-relevant behavior for a code file.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
When a tool has a callback_url, the code automatically POSTs attacker-controlled tool input together with internal identifiers (run_id and token_owner) to an external endpoint. Although there is an allowlist check, the callback URL validation is weak because it effectively accepts any HTTPS URL whenever TOOL_CALLBACK_ALLOWLIST is non-empty, so sensitive workflow metadata can be exfiltrated to arbitrary remote services through registered tools.

Missing User Warnings

Medium
Confidence
82% confidence
Finding
The file_store builtin writes arbitrary provided content to disk under the run artifacts directory using writeFileSync, but this handler contains no confirmation prompt, user-visible log/print, or explanatory comment/docstring warning about the write. For code files, safety-relevant file writes should have some form of disclosure unless the warning is provided elsewhere in markdown, which is not visible in this file.

Natural-Language Policy Violations

Medium
Confidence
98% confidence
Finding
This test enforces a blanket prohibition on CJK characters across README.md, src, and tests, which creates a discriminatory language gate unrelated to security. In a skill repository, this can suppress multilingual documentation, test cases, or user-facing text and can be used to block legitimate contributions or functionality for non-English users.

External Transmission

Medium
Category
Data Exfiltration
Content
const original = global.fetch;
    global.fetch = (async () => new Response('{"ok":true}', { status: 200, headers: { 'content-type': 'application/json' } })) as typeof fetch;
    try {
      const res = await safeFetch('https://api.example.com/v1', { resolver: async () => [{ address: '93.184.216.34' }] });
      expect(res.status).toBe(200);
    } finally {
      global.fetch = original;
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
const original = global.fetch;
    global.fetch = (async () => new Response('{"ok":true}', { status: 200, headers: { 'content-type': 'application/json' } })) as typeof fetch;
    try {
      const res = await safeFetch('https://api.example.com/v1', { resolver: async () => [{ address: '93.184.216.34' }] });
      expect(res.status).toBe(200);
    } finally {
      global.fetch = original;
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
const original = global.fetch;
    global.fetch = (async () => new Response('{"ok":true}', { status: 200, headers: { 'content-type': 'application/json' } })) as typeof fetch;
    try {
      const res = await safeFetch('https://api.example.com/v1', { resolver: async () => [{ address: '93.184.216.34' }] });
      expect(res.status).toBe(200);
    } finally {
      global.fetch = original;
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
const original = global.fetch;
    global.fetch = (async () => new Response('{"ok":true}', { status: 200, headers: { 'content-type': 'application/json' } })) as typeof fetch;
    try {
      const res = await safeFetch('https://api.example.com/v1', { resolver: async () => [{ address: '93.184.216.34' }] });
      expect(res.status).toBe(200);
    } finally {
      global.fetch = original;
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Internal Network Request

Medium
Category
Server-Side Request Forgery
Content
it('blocks private ip literals and dns private resolutions', async () => {
    process.env.OUTBOUND_ALLOW_ALL = '1';
    await expect(safeFetch('https://127.0.0.1/x', {})).rejects.toThrow('OUTBOUND_IP_LITERAL_FORBIDDEN');
    await expect(safeFetch('https://api.example.com/x', { resolver: async () => [{ address: '10.0.0.1' }] })).rejects.toThrow('OUTBOUND_PRIVATE_ADDRESS_BLOCKED');
  });
Confidence
70% confidence
Finding
Code issues a request to a loopback, link-local, or private-range host. This can reach internal services not meant to be exposed and is a common SSRF pivot.

Known Vulnerable Dependency: @vitest/mocker==3.2.4 — 1 advisory(ies): CVE-2026-84373 (Vitest: Path Traversal / Arbitrary File Read via @vitest/mocker Redirect Mock)

Low
Category
Supply Chain
Confidence
85% confidence
Finding
The lockfile includes @vitest/mocker 3.2.4, which is flagged for a path traversal/arbitrary file read issue in Vitest mock redirection behavior. In this file it is only a development/testing dependency, so exposure is limited to test or dev workflows rather than production runtime, but it is still a real vulnerable component present in the dependency graph.

Static analysis

Detected: suspicious.env_credential_access

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
tests/orchestrator.test.ts:9

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
tests/security-hardening.test.ts:44