Back to skill

Security audit

Searxng Web

Security checks for vulnerabilities and agentic risk

Overview

This skill is a small local SearXNG search wrapper with some documentation and hardening gaps, but no hidden or purpose-mismatched behavior was found.

Install only if you control and trust the SearXNG service reachable at host.docker.internal:8081. Treat search queries as visible to that local service and avoid sending secrets or highly sensitive text; adding request timeouts, size limits, and clearer docs would improve the skill.

Vulnerability Patterns
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Warning
Location
searxng_search.js:3
Finding
Unbounded Input and HTTP Response Buffering with No Request Timeout## Vulnerability Details **File Location**: `searxng_search.js`, lines 3–4 **Vulnerability Type**: Resource exhaustion and denial of service **Risk Level**: Medium **Vulnerable code:** ```javascript function readStdin(){return new Promise((res,rej)=>{let d="";process.stdin.setEncoding("utf8");process.stdin.on("data",c=>d+=c);process.stdin.on("end",()=>res(d));process.stdin.on("error",rej);});} function getJson(url){return new Promise((res,rej)=>{http.get(url, r=>{let d="";r.setEncoding("utf8");r.on("data",c=>d+=c);r.on("end",()=>{if(r.statusCode&&r.statusCode>=400)return rej(new Error(`http ${r.statusCode}: ${d.slice(0,300)}`));try{res(JSON.parse(d));}catch(e){rej(new Error(`invalid json: ${e.message}. body=${d.slice(0,300)}`));}});}).on("error",rej);});} ``` ### Technical Analysis The `readStdin` function appends every incoming chunk to the string `d` without enforcing a maximum input size. Likewise, `getJson` buffers the entire HTTP response in memory before parsing it and imposes neither a response-size limit nor a request timeout. The `slice(0,300)` operations only limit text included in error messages after the complete response has already been buffered. They therefore do not mitigate memory exhaustion. Because the HTTP request has no timeout, an endpoint that accepts the connection but responds indefinitely or never completes can also keep the Node.js runner occupied without bound. Exploitation requires the ability to submit an oversized invocation payload, influence the configured local SearXNG service, or cause that service to return an unusually large, endless, or stalled response. ### Attack Path 1. An attacker submits an abnormally large tool input, or causes the local SearXNG endpoint at `host.docker.internal:8081` to produce a large, endless, or stalled response. 2. The runner repeatedly concatenates incoming chunks into the in-memory `d` string. 3. No byte limit aborts input or response processing, and no tim ...[truncated 689 chars]
Remediation
## Remediation Suggestions - Enforce a strict maximum byte count while reading standard input and reject oversized payloads before parsing JSON. - Enforce a response-size ceiling while consuming HTTP response chunks; destroy the request or response stream immediately when the limit is exceeded. - Configure explicit connection and total-response timeouts and call `request.destroy()` when they expire. - Consider using an `AbortController` or equivalent cancellation mechanism to ensure all timeout paths release sockets and memory. - Validate `Content-Length` when present, while retaining streamed byte counting because that header can be absent or inaccurate. - Define limits appropriate to the expected search response size and return a stable, non-sensitive error code when a limit is exceeded. - Add tests covering oversized standard input, oversized HTTP responses, slow responses, connections that never complete, and concurrent requests.
Vulnerability Patterns
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (3)

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The skill explicitly proxies user queries over plain HTTP to a local service at host.docker.internal, but the description does not warn users that their search terms leave the agent boundary and traverse an unencrypted channel. This can expose sensitive queries to local network interception, container-host trust boundary issues, or unintended disclosure to the host-side service.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The code sends the user's search query to `http://host.docker.internal:8081/search` over plain HTTP, which provides no transport encryption or server authentication. This can expose potentially sensitive queries to interception or modification by a local network attacker, container-network adversary, or any component able to observe traffic between the runtime and the target service.

Vague Triggers

Low
Confidence
78% confidence
Finding
This is a markdown file, so vague-trigger review applies. The usage section presents an incomplete example ending with a shell command fragment, without clearly constraining how or when the skill should be invoked, which makes activation and usage conditions unclear.

Static analysis

No suspicious patterns detected.