Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
layout: default
title: AI PatchLab Scans
description: "108 curated security scans of open-source AI agents, MCP servers and LLM apps - 24 confirmed fixes, run local-first with Semgrep, Gitleaks, Trivy and pip-audit."
description: "109 curated security scans of open-source AI agents, MCP servers and LLM apps - 24 confirmed fixes, run local-first with Semgrep, Gitleaks, Trivy and pip-audit."
---

# AI PatchLab Scans
Expand All @@ -20,7 +20,7 @@ remediation and confidence rules to normalize the findings.

> **Want this run privately against your own codebase?** I do independent
> security review of AI agents, MCP servers, and LLM apps —
> [**work with me →**]({{ '/work-with-me' | relative_url }}). 108 scans, 24 confirmed fixes, methodology in the open.
> [**work with me →**]({{ '/work-with-me' | relative_url }}). 109 scans, 24 confirmed fixes, methodology in the open.

> **OpenAI just launched [Daybreak](https://openai.com/index/daybreak-securing-the-world/) and Patch the Planet.**
> Same remediation loop, opposite trade-off: their path is a cloud frontier model;
Expand Down Expand Up @@ -103,7 +103,7 @@ login and static assets. Fifty-two flagged, none reported.

## All scans

108 scans, newest first. **Findings** is the raw count the tools produced;
109 scans, newest first. **Findings** is the raw count the tools produced;
**Real** is what survived curation. The gap between those two columns is the
entire job.

Expand All @@ -118,6 +118,7 @@ filed, which is the usual outcome of a clean scan.

| Date | Repository | Findings | Real | Outcome |
| --- | --- | ---: | --- | --- |
| 2026-09-22 | [overwirehq/claude-code-telegram](scans/overwirehq-claude-code-telegram.html) | 54 | 0 first-party — dependency | — |
| 2026-09-21 | [HarnessRouter/harnessrouter](scans/harnessrouter-harnessrouter.html) | 117 | 1 real — dependency | — |
| 2026-09-20 | [TencentCloud/Octop](scans/tencentcloud-octop.html) | 300 | 1 real — withheld | private |
| 2026-09-19 | [hydropix/TranslateBooksWithLLMs](scans/hydropix-translatebookswithllms.html) | 55 | 1 real — withheld | private |
Expand Down
4 changes: 3 additions & 1 deletion docs/scan-log.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ description: "The complete AI PatchLab scan log: every public repository scanned

# Full scan log

Every scan in the series, newest first, with the summary written on the day of the scan. 108 scans. For the compact index, see the [scan log home]({{ '/' | relative_url }}).
Every scan in the series, newest first, with the summary written on the day of the scan. 109 scans. For the compact index, see the [scan log home]({{ '/' | relative_url }}).

- **2026-09-22** — [overwirehq/claude-code-telegram](scans/overwirehq-claude-code-telegram.html) — 54 findings at `medium+` (1 critical, 21 high, 30 medium), **0 first-party defects — dependency currency, post-only** — a **Telegram bot that gives authorised users remote access to Claude Code**, i.e. it runs file and Bash tools on a host machine by design, so the whole security surface *is* the boundary around that execution (2k★, MIT, org-backed; active — 15 merged PRs from 7 authors and 9 closed issues in 60 days; strict-norm: real `SECURITY.md`, PVR enabled). Picked with the manual queue at zero. **The story is what precise security documentation looks like.** The real boundary is the `can_use_tool` callback that stops Claude — when steered off-course by content it reads mid-task — from acting outside the approved directory, and the maintainers know and *state* exactly what it covers: path validation is scoped in the docs to precisely six tools (`Read`, `Write`, `Edit`, `MultiEdit`, `NotebookEdit`, `NotebookRead`), while `Grep`/`Glob`/`LS` are deliberately left to the OS sandbox. Reading that as "Read is guarded but Grep is not, so the boundary is incomplete" is the plausible-but-wrong finding this site exists to resist — the boundary is [advertised, not accidental](scans/tracecathq-tracecat.html), and `ROADMAP-v2.md` even records the SDK's own `CanUseToolShadowedWarning` naming every tool the callback will never see, filed as tracking item #221. Two more choices earn credit: guarded tools are deliberately *stripped from* the SDK `allowed_tools` list (a pre-approved tool never produces a `can_use_tool` request, so leaving them in would render the checks silently inert — [issue #219](https://github.com/overwirehq/claude-code-telegram/issues/219)), and `autoAllowBashIfSandboxed` is disabled whenever the boundary checks must run, closing a second bypass. That is a maintainer who traced how the framework resolves permissions rather than trusting that a config allow-list is an enforcement boundary — it isn't, and the code says so. **When the machine is built and documented this carefully, the finding moves to the dependency manifest.** The 30 Trivy advisories in `poetry.lock` split cleanly by reachability: **17 are opt-in-only and not reachable on a default install** — `starlette` (6) and `python-multipart` (3) need the FastAPI webhook server (`ENABLE_API_SERVER=false` default); `pyjwt` (5) needs token auth (`ENABLE_TOKEN_AUTH=false` default, and the SECURITY.md documents token auth as non-functional, [#58](https://github.com/overwirehq/claude-code-telegram/issues/58)); the MCP SDK highs (3) need MCP enabled — while **13 are unconditionally installed** (`anyio` incl. the critical IDNA/TLS advisory, the `cryptography`/OpenSSL cluster, `urllib3`, `idna`, `requests`, `pydantic-settings`, `python-dotenv`) and are the genuine currency gap. The direct deps are current; the drift is transitive, which the repo's existing `.github/dependabot.yml` (configured for *version* updates, not *security* updates) will not raise on its own. **No advisory filed** — there is no first-party vulnerability. Of the other 24: 16 `github-actions-mutable-action-tag` (SHA-pin hardening); one `pull_request_target` checkout wrapped in a 40-line threat-model header (read-only tool allowlist, secrets scrubbed, "worst case is a prompt-injected review comment" — [the trigger decides severity](scans/lightseekorg-tokenspeed.html)); two `sqlalchemy-execute-raw-query` [identifier FPs](scans/aurelio-labs-semantic-router.html) (only a generated run of `?` placeholders is interpolated, values bound via `execute(query, params)`); and three gitleaks hits that are a `your-api-secret` placeholder plus two fake tokens in a test asserting the project's own `_redact_secrets()` helper scrubs them ([credited defence](scans/realiti4-claude-swap.html)). Coverage `partial` and honestly so: pip-audit resolved **no dependencies** from a `pyproject.toml` declaring `dynamic = ["dependencies"]` — reading identically to "clean" — and Trivy's `poetry.lock` read carried the run, the [same dependency blind spot](scans/harnessrouter-harnessrouter.html) as yesterday reached by a different manifest shape.

- **2026-09-21** — [HarnessRouter/harnessrouter](scans/harnessrouter-harnessrouter.html) — 117 findings (2 critical, 42 high, 70 medium), **1 real — dependency currency, post-only** — a **self-hosted control plane that turns agent CLIs (Codex, Claude Code, Hermes, and a dozen more) into a single OpenAI-compatible API** (1.7k★, Apache-2.0, org-backed with a hosted "Cloud" edition; very active — 91 merged PRs from 5 authors in 60 days). Picked with the manual queue at zero: strict-norm (real `SECURITY.md`, PVR enabled, commercial backing), which is a fair target when the backlog is clear. **The story here is a well-built authorization layer that survived the sweep the series usually breaks projects on, so the one finding moved to the dependency manifest.** The route inventory — 113 gateway routes — comes back with exactly five answering without a credential once the project's own identity idioms (`_owned_session`, `_pub_org_member`, `_principal`) are resolved: `/v1/uhp`, `/healthz`, `/readyz`, `/version`, and `/share/{token}` where the unguessable token *is* the credential. That is the [name-matched sweep](scans/mai-with-u-maibot.html) returning empty for the right reason. Two design choices earn explicit credit: the self-hosted BFF stamps its internal trust key onto a gateway call **only for a request carrying a valid session cookie** — an earlier build stamped it unconditionally, and the code comments document catching and fixing exactly that [conditional-verification](scans/sentelabsai-openexecutive.html) class before I arrived — and the gateway **binds loopback with only the UI port published**, so the [DNS-rebinding shape](scans/liaohch3-claude-tap.html) that has caught several desktop apps here does not apply (the published surface is the session-gated Next.js app with `X-Frame-Options: DENY`). **The actionable finding is dependency currency, and it only surfaced because the two dependency tools disagreed about whether there was anything to scan.** pip-audit reported **no manifest** — it scans the repo root, and the requirements live in `gateway/requirements.txt` and `runner/requirements.txt` one level down — which on a monorepo reads identically to "clean". Trivy's whole-tree walk read both and found the pinned `next` **15.5.23** is one patch behind **15.5.24**, which fixes two criticals: [CVE-2026-75604](https://github.com/advisories) (Windows-hosted RCE — **dropped, the image is Linux**) and [GHSA-2xp9-vwfh-vxw4](https://github.com/advisories) (AVIF image-optimizer RCE — the optimizer runs at its default-enabled setting and the middleware matcher **excludes `_next/image`**, so the surface is reachable unauthenticated; default-empty `remotePatterns` constrains full exploitation, and with no Docker Linux engine available I could not run the primitive, so I claim the reachable surface and the currency gap, not a demonstrated RCE). `gateway/requirements.txt` also carries `PyJWT` 2.10.1 (CVE-2026-48526, auth-bypass) and `cryptography`/`aiohttp`/`python-multipart` CVEs — low reachability self-hosted (gateway loopback, `HR_IDENTITY_MODE=off`) but the hosted build shares the code. **No advisory filed**: the actionable item is a published upstream CVE with a one-line fix (`next >=15.5.24` + a `dependabot.yml` covering npm *and* pip, of which there is none today), not a first-party defect, and the "run the exploit primitive before filing" rule forbids an RCE-shaped advisory I can't demonstrate. Of the other 116: 29 `github-actions-mutable-action-tag` (SHA-pin hardening); 11 workflow shell-injection all on `workflow_dispatch`/`push:tags` triggers ([trigger decides severity](scans/lightseekorg-tokenspeed.html) — write access already required); 13 subprocess-audit hits in `runner/`, which runs agent CLIs as a per-session uid ([running code is the product](scans/realiti4-claude-swap.html)); a `ws://` `detect-insecure-websocket` false positive (matched a `.replace()` scheme-transform string); a test-fixture key in `gateway/tests/`. Coverage `partial` and honestly so — Semgrep's errors are non-Python config/data files, no first-party module skipped. The backlog item is real and is the day's [tooling note](scans/whiteguo233-openbiliclaw.html): `scan_dependency` is root-only, so on a monorepo it silently disagrees with Trivy — it should descend into subdirectory manifests or emit a louder meta-finding.
- **2026-09-20** — [TencentCloud/Octop](scans/tencentcloud-octop.html) — 300 findings (300 above the medium floor, 3 of them scan-coverage meta findings), **1 real — withheld** — a **self-hosted, multi-user, multi-agent AI assistant** (4.3k★, MIT, created July 2026 by TencentCloud; very active — 26 merged PRs from 6+ authors in 60 days). Picked on responsiveness and on shape: a multi-user control plane with a real permission system is exactly where an authorization gap hides. The class, stated without a recipe: **one sensitive control-plane surface is gated by authentication alone, not by any permission key — and no key for it exists in the catalogue at all.** Octop ships a proper default-deny permission model — 27 module keys, a `require_permission(key)` factory, invited users created with the `USER` role and an *empty* permission set — and enforces it consistently across the API except for this one router, which sits behind bare "are you logged in?". A zero-permission invited user (the invite-onboarding default) reaches it, and the confinement that should contain the blast radius is a per-user policy left **unset (unrestricted) by default**; the shipped container image broadens that default scope well beyond any single user's own workspace. This is the [inert-security-flag](scans/mnemosyne-oss-mnemosyne.html) family inverted (the pattern is applied everywhere *but* one place) and the [scan-the-seam](scans/mljar-mercury.html) shape (the project's own permission catalogue is the contract; the finding is the route-group it forgot to include). Post-auth, not pre-auth — detail (route set, reachability chain, container specifics) withheld pending a fix. What made the write-up honest was discarding two coherent-but-wrong findings first: the `tarfile.extractall` hits both pass `filter=tarfile.data_filter` (credited FP), and the setup-takeover chain collapsed on [running the remedy against the real deployment](scans/roflcoopter-viseron.html) — the Docker entrypoint pre-creates the admin before the server listens, and every setup route re-checks `user_count == 0` server-side, so no wizard-race state is reachable. The setup wizard is genuinely hardened (CLI one-time password required by default, `127.0.0.1` bind default, a lockdown middleware that 503s until an admin exists). Of the 297 tool findings none was an exploitable vuln: the lone Critical is a valid `anyio` 4.14.1→4.14.2 bump (CVE-2026-63374, IDNA-only TLS spoofing on an already-redirected connection); 61+31 SQL hits are the [identifier false positive](scans/mnemosyne-oss-mnemosyne.html) at record volume (`_scope_filter` builds `"col = ?"` fragments, values bound as `?`); three `run-shell-injection` are `${{ github.ref_name }}` on a push-tags trigger (needs push access — the [trigger decides severity](scans/lightseekorg-tokenspeed.html)); the SSH/API-key hits are a doc placeholder, a `_SIGN_SECRET` commented as *public* third-party material, and a test fixture; six `logger-credential-leak` log only `.kind`/exceptions. Reported privately by email (repo PVR is disabled); GitHub private vulnerability reporting off, SECURITY.md forbids public issues. Scan coverage incomplete: pip-audit timed out at 300s, but **Trivy read `uv.lock`** so the dependency surface was still examined; Semgrep's 50 errors (0 timeouts) are all non-Python config/data/test files — no first-party Python module skipped.
Expand Down
Loading
Loading