Skip to the content.

zilliztech/memsearch — security scan

Repository: zilliztech/memsearch Commit scanned: 36216d63 (main at scan time) Scan date: 2026-08-17 Disclosure status: disclosed — one real, exploitability-shaped finding filed as a single focused issue (no working private channel: private vulnerability reporting is disabled and there is no SECURITY.md at the repo, .github, or org level). A fix PR accompanies the issue. Outcome (2026-08-28):fixedPR #682 merged and issue #681 closed as completed, eleven days after filing. The execSync shell string became execFileSync with an argv array, matching the dsh and openclaw siblings.

Summary

Severity Count
Critical 0
High 38
Medium 52
Low
Info

Total findings: 90 above the medium floor · 1 real (shell-injection in one of four sibling plugins), the rest lockfile-only dependency CVEs and workflow / FP classes covered below.

memsearch is Zilliz’s cross-platform semantic-memory layer for AI coding agents (2.5k★, MIT, Python core + one TypeScript/JS plugin per host: Claude Code, Codex, DeepSeek Harness, OpenClaw, OpenCode). It indexes a project’s markdown memory into Milvus and exposes search to the agent. I picked it because it ships five near-parallel implementations of the same host-integration logic — the single best differential surface a scan can ask for — and because it is not a code executor, so an unintended bash call is a defect, not product surface.

Top finding

1. Shell injection in the OpenCode plugin’s collection-name derivation

What was checked and cleared

The interesting part of this scan is how much of the surface is correctly built. Each of these was a candidate finding that a read closed out:

Patterns observed

A five-way clone is a gift to a reviewer, and a liability to a maintainer. The same deriveCollectionName exists in five plugins. Four converged on the safe argv form; one kept a shell string. This is the “Nth implementation that differs” shape — the bug isn’t that anyone wrote unsafe code from scratch, it’s that a shared behaviour drifted in exactly one copy and no single-file review would ever see the other four to notice. The scripts/sync-prompts.sh in the repo keeps the prompt files in lockstep across plugins; there is no equivalent guard keeping the invocation form in lockstep, and that’s precisely where the one defect lives.

Almost every scanner “High” here is lockfile noise. 36 of the 38 highs are Trivy/pip-audit CVEs read out of uv.lock (pillow, aiohttp, langchain-core, transformers, urllib3, pyasn1, cryptography). But the runtime dependency set in pyproject.toml is nine packages — pymilvus, milvus-lite, click, watchdog, pathspec, setuptools, tomli_w, tomli, openai — and none of pillow/aiohttp/langchain/transformers is among them; they arrive transitively via optional [local]/[onnx] extras or the dev/docs groups and the resolved lock. The one runtime dep that is flagged, setuptools (pinned >=78.1.1,<81, CVE fixed in 83.0.0), carries a real “bound excludes the fix” shape — but the CVE is a MANIFEST.in-bypass at sdist build time, and memsearch builds with hatchling, not setuptools. So the honest count of reachable dependency risk is zero, and the honest count of real findings is one.

“Not a code executor” is the curation lever. For a memory tool, spawning bash derive-collection.sh <path> is internal plumbing, not an advertised capability — so the advertised-boundary test says command substitution triggered by a directory name is unintended, and therefore a finding. On a code-executor project the same exec would be product surface. Same construct, opposite verdict, decided entirely by what the tool claims to do.

Notes on the tool

Disclosure timeline

Reproduce

git clone https://github.com/elfrost/ai-patchlab
cd ai-patchlab
pip install -e ".[dev]"
python scanner/run_scan.py \
  --from-git-url "https://github.com/zilliztech/memsearch" \
  --reports-dir reports/zilliztech-memsearch \
  --min-severity medium

The one real finding was not produced by the scanner — it came from diffing the five plugins’ derive-collection.sh invocations by hand. The differential that proves it (POSIX /bin/sh -c command-substitution on the OpenCode form vs. the argv form the other plugins use) is a ten-line Node/bash script; the shape is described in the Top finding above.

External tools (Semgrep, Gitleaks, Trivy, pip-audit) need to be installed separately — see the project README.