Beta — Glance scan is live on npm today. Glance guard is in private beta on real machines. Fixes is next.
Research archive

Thirteen agent repos with disclosed vulnerabilities

Real vulnerabilities in real projects, not a synthetic benchmark. MCP servers, agent frameworks, and one skill file. Every case shows the code as written and the fix, with the original disclosure linked and the finder credited. None of it is our own discovery unless the credit says so. Every code block marked with a commit was read straight out of that repository at that commit. For what the scanner reports today, see what Glance finds by category.

13artifacts, all public
and safe to link
3with published CVEs
on the NVD
12/13with a public disclosure
you can read
3source issue still open,
checked 2026-09-06
Severity
Ecosystem

01

mcp-server-sqlite

src/sqlite/src/mcp_server_sqlite/server.py:320-325 at e8f0b15

Critical MCP Python Archived Self-assigned grade

describe_table interpolates the caller's table_name straight into a PRAGMA. A name like `mytable); DROP TABLE users; --` runs as a second statement, so a tool that only reads schema can delete data.

The source issue was closed (completed) when this was checked on 2026-09-06.

src/sqlite/src/mcp_server_sqlite/server.py:320-325 at e8f0b15
            elif name == "describe_table":
                if not arguments or "table_name" not in arguments:
                    raise ValueError("Missing table_name argument")
                results = db._execute_query(
                    f"PRAGMA table_info({arguments['table_name']})"
                )

Fix Validate the table name against an identifier pattern, then bind it as a parameter to pragma_table_info() instead of formatting it into SQL.

02

mcp-server-mysql

src/db/utils.ts:20-24 at a9eb08e

Critical MCP Node Fixed upstream Self-assigned grade

The schema is chosen by running a regex over the raw SQL. MySQL treats a comment as whitespace, so USE/**/other_db never matches, the permission check is applied to the wrong schema, and the statement still executes.

The source issue was closed (completed) when this was checked on 2026-09-06.

src/db/utils.ts:20-24 at a9eb08e
  // Case 1: USE database statement
  const useMatch = sql.match(/USE\s+`?([a-zA-Z0-9_]+)`?/i);
  if (useMatch && useMatch[1]) {
    return useMatch[1];
  }

Fix Parse the statement into an AST and read the USE target from it. A regex over SQL text can always be split by a comment.

03

mobile-mcp

src/android.ts:397-399 at 40a8e76

High MCP TypeScript Fixed upstream

A malicious page can inject a non-http URL that triggers Android Intent actions — opening attacker-controlled apps or reading local device files via file:///.

src/android.ts:397-399 at 40a8e76
	public async openUrl(url: string): Promise<void> {
		this.adb("shell", "am", "start", "-a", "android.intent.action.VIEW", "-d", this.escapeShellText(url));
	}

Fix Reject any scheme outside http and https before the URL reaches ADB. escapeShellText protects the shell, not the intent system.

CVE-2026-353948.3 HIGH · CVSS 3.1
04

Langchain-Chatchat

libs/chatchat-server/chatchat/server/agent/tools_factory/shell.py:12-16 at 49165d6

Critical Python Python Self-assigned grade

Pre-auth RCE: anyone who can reach the API endpoint can run arbitrary shell commands as the server process. CVSS 9.8. No agent required to exploit.

The source issue was closed (not_planned) when this was checked on 2026-09-06.

libs/chatchat-server/chatchat/server/agent/tools_factory/shell.py:12-16 at 49165d6
@regist_tool(title="系统命令")
def shell(query: str = Field(description="The command to execute")):
    """Use Shell to execute system shell commands"""
    tool = ShellTool()
    return BaseToolOutput(tool.run(tool_input=query))

Fix Remove ShellTool from public agents. If shell access is required, strict allowlist + shell=False + authentication.

CVE-2026-30617 (adjacent)9.8 CRITICAL · self-assessed
05

LangChain PythonReplTool

langchain/tools/python/tool.py:30-34 at e519a81

High Python Python By design

exec() in the host process gives LLM-generated code full access to the filesystem, env vars, and network — prompt injection turns this into instant RCE.

langchain/tools/python/tool.py:30-34 at e519a81
    def _run(self, query: str) -> str:
        """Use the tool."""
        if self.sanitize_input:
            query = query.strip().strip("```")
        return self.python_repl.run(query)

Fix Run in subprocess with timeout and dropped privileges, or use a container sandbox (e2b, modal). Never exec() in the host process.

CVE-2023-293749.3 CRITICAL · CVSS 4.0
06

PraisonAI

src/praisonai-agents/praisonaiagents/memory/hooks.py:302-310 at 84c4d22

Critical Python Python Fixed upstream

Memory hooks execute with shell=True — a poisoned memory entry (written by the LLM) can inject shell commands that run as the agent process. CVE-2026-40111.

src/praisonai-agents/praisonaiagents/memory/hooks.py:302-310 at 84c4d22
            # Execute
            result = subprocess.run(
                command,
                shell=True,
                cwd=str(self.workspace_path),
                env=env,
                capture_output=True,
                text=True,
                timeout=hook.timeout

Fix Split the command with shlex and run it with shell=False. Upstream shipped exactly this in the following commit.

CVE-2026-401119.3 CRITICAL · CVSS 4.0
07

aden-hive/hive

tools/src/aden_tools/tools/file_system_toolkits/execute_command_tool/execute_command_tool.py:46-53 at 5fbaae5

Critical Python Python Self-assigned grade

Two bugs together: shell injection via agent-controlled args, plus every environment variable (API keys, tokens, secrets) forwarded to every subprocess.

The source issue was closed (completed) when this was checked on 2026-09-06.

tools/src/aden_tools/tools/file_system_toolkits/execute_command_tool/execute_command_tool.py:46-53 at 5fbaae5
            result = subprocess.run(
                command,
                shell=True,
                cwd=secure_cwd,
                capture_output=True,
                text=True,
                timeout=60
            )

Fix shell=False with shlex.split, plus a minimal explicit env. The docstring already promises constraints the code never enforced.

08

mcp-shell

executor.go:100-105 at fcbb32b

High MCP Go Self-assigned grade

validateCommand at security.go:23 matches patterns against the raw string, and that same string is then handed to bash -c. Command substitution rebuilds a blocked word out of fragments, so the check passes and the command still runs.

The source issue was closed (completed) when this was checked on 2026-09-06.

executor.go:100-105 at fcbb32b
func (e *CommandExecutor) executeSecureCommand(
	ctx context.Context,
	command string,
	useBase64 bool,
) (*ExecutionResult, error) {
	cmd := exec.CommandContext(ctx, "bash", "-c", command)

Fix Parse with shlex, allowlist the base binary, and exec the argument vector directly. Dropping bash -c is what removes the bypass, not a longer blocklist.

09

CrewAI LiteAgent

lib/crewai/src/crewai/lite_agent.py:614-627 at 143e902

High Python Python Self-assigned grade

Anything written into agent memory — including content from web pages or tool responses — gets promoted to system prompt trust on the next turn. One malicious page poisons all future reasoning.

The source issue was open when this was checked on 2026-09-06.

lib/crewai/src/crewai/lite_agent.py:614-627 at 143e902
        memory_block = ""
        try:
            matches = self._memory.recall(query, limit=10)
            if matches:
                memory_block = "Relevant memories:\n" + "\n".join(
                    f"- {m.record.content}" for m in matches
                )
            if memory_block:
                formatted = I18N_DEFAULT.slice("memory").format(memory=memory_block)
                if self._messages and self._messages[0].get("role") == "system":
                    existing_content = self._messages[0].get("content", "")
                    if not isinstance(existing_content, str):
                        existing_content = ""
                    self._messages[0]["content"] = existing_content + "\n\n" + formatted

Fix Cap and strip each memory before it is joined, and append it as a user message rather than editing the system message. Memory is something the agent read, not something the operator wrote.

10

AutoGen WebSurfer

python/packages/autogen-ext/src/autogen_ext/agents/web_surfer/_prompts.py:45-46 at 027ecf0

High Python Python Self-assigned grade

The page title is whatever the page says it is. It is read at _multimodal_web_surfer.py:885 and interpolated into the QA prompt, so a page titled 'Ignore previous instructions' arrives as part of the operator's own text.

The source issue was open when this was checked on 2026-09-06.

python/packages/autogen-ext/src/autogen_ext/agents/web_surfer/_prompts.py:45-46 at 027ecf0
def WEB_SURFER_QA_PROMPT(title: str, question: str | None = None) -> str:
    base_prompt = f"We are visiting the webpage '{title}'. Its full-text content are pasted below, along with a screenshot of the page's current viewport."

Fix Cap the title, strip newlines, and wrap it in a delimiter the prompt tells the model to treat as data. Never concatenate page-sourced text into instruction prose.

11

CrewAI examples

crews/instagram_post/tasks.py:5-8 at 5e3b1e0

High Python Python Self-assigned grade

The example interpolates caller-supplied product_website and product_details straight into the task description. Developers copy these crews into their own projects, so one crafted input rewrites the task and every agent step after it.

The source issue was open when this was checked on 2026-09-06.

crews/instagram_post/tasks.py:5-8 at 5e3b1e0
	def product_analysis(self, agent, product_website, product_details):
		return Task(description=dedent(f"""\
			Analyze the given product website: {product_website}.
			Extra details provided by the customer: {product_details}.

Fix Put caller input in a delimited block the instruction tells the agent to read as data. Never f-string it into the instruction itself.

12

sickn33 SKILL.md

skills/database/SKILL.md:34 illustrative path, not a location in any repository

High Skill Markdown Anti-pattern Self-assigned grade

Credentials hardcoded in a skill file are sent to the LLM API in every agent context window — and committed to version control history. Two separate exfiltration surfaces.

The credentials in this sample are redacted here. The point is the shape of the mistake, not the strings: a skill file is loaded verbatim into agent context, so anything written in one travels to every API call.

illustrative, not a quotation
## Database Connection

Connect to the production database:

```python
import psycopg2
conn = psycopg2.connect(
    host="[redacted]",
    database="customers",
    user="app_user",
    password="[redacted]"  # production password
)
```

API key for analytics: sk-ant-api03-[redacted]

Fix Use environment variables. Rotate any exposed credentials immediately. Run detect-secrets in CI to catch this before commit.

13

IntegSec/VulnerableMCP

src/tools/command-executor.ts:28-40 at 4cfe462

Lab Lab TypeScript Benchmark lab

A public MCP security benchmark, deliberately vulnerable, used here to show a scan of code nobody wrote for us. The tool takes a command string from the caller and hands it to execSync with the full process environment attached, so a single shell metacharacter turns a tool call into arbitrary code execution with whatever secrets that process holds.

src/tools/command-executor.ts:28-40 at 4cfe462
export async function handleExecuteCommand(args: any) {
  const { command } = args;

  try {
    // VULNERABILITY: Direct command execution without sanitization!
    // Should use parameterized commands or allowlist of safe commands
    // Should NEVER concatenate user input into shell commands

    // WRONG: Directly executing user-provided command
    const output = execSync(command, {
      encoding: 'utf-8',
      // VULNERABILITY: No timeout
      timeout: 60000,  // Way too long!

Fix Never pass a caller-supplied string to a shell. Map named actions to fixed argument vectors, use execFile rather than exec, cut the timeout, and pass a minimal environment instead of process.env.

Nothing here is under embargo and none of it is our own discovery unless the credit says so. Where a project has shipped a fix we say so. Where it has not, we say that too. If you maintain one of these and want a correction or a removal, email golem@forwardemail.net and we will act on it.

Early access

Scanning is the free part. The watching is the point.

Request early access