From 5f76788b6ef9f1ab65f709ac97b0ccbba0555f7d Mon Sep 17 00:00:00 2001 From: elfrost <5491654+elfrost@users.noreply.github.com> Date: Thu, 24 Sep 2026 09:06:59 -0400 Subject: [PATCH 1/2] docs: correct boss-agent-cli precondition (manual start, no idle exit while connected) The post and the private report both described the bridge exposure as "a bounded window". Checked against 73e020f, both halves were wrong: nothing calls start_daemon_background() (the maintainer removed it as dead code in #440), so the user must start bridge mode by hand; and the idle exit requires no extension connection, so it never fires while the browser side is connected. The advisory was corrected the same day with a dated block; the finding, the fix and the severity are unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) --- docs/scan-log.md | 2 +- docs/scans/can4hou6joeng4-boss-agent-cli.md | 20 ++++++++++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/docs/scan-log.md b/docs/scan-log.md index 7feb3db..5ae92bc 100644 --- a/docs/scan-log.md +++ b/docs/scan-log.md @@ -8,7 +8,7 @@ description: "The complete AI PatchLab scan log: every public repository scanned Every scan in the series, newest first, with the summary written on the day of the scan. 110 scans. For the compact index, see the [scan log home]({{ '/' | relative_url }}). -- **2026-09-23** — [can4hou6joeng4/boss-agent-cli](scans/can4hou6joeng4-boss-agent-cli.html) — 26 findings at `medium+` (7 high, 16 medium, 3 meta), **1 real — withheld, filed privately** — a **CLI for the BOSS Zhipin recruitment platform** (2k★, MIT; a human drives it from a terminal wizard, an agent drives the same workflows over a JSON-envelope API, an ~77-tool MCP server, or a Python SDK) that authenticates by reusing the operator's own logged-in browser session. Responsive maintainer — 67 merged PRs from 7 human authors in 60 days — which is why it cleared the pre-check. **Zero of the 26 scanner findings is the vulnerability**, which is the whole point of the entry: the real item is the *absence* of a check on an otherwise-working local channel, the shape no rule can match, and it is the [desktop-loopback-inversion](scans/liaohch3-claude-tap.html) class — a `127.0.0.1` control surface that binds neither *who* may call it nor which host the request claims, driving privileged browser actions against a site where the operator is authenticated. Confirmed with a **server-side exploit primitive against the shipped daemon** (a foreign `Host` is served, a cross-origin "simple" request reaches the command channel with no preflight, a hostile `Origin` is not rejected) plus a read of the extension source for the browser half — the [run-the-primitive discipline](scans/observal-observal.html), because reading alone could not separate "the channel is drivable" from "the channel is drivable *and* blind does not blunt it." Graded **High not Critical** on an honest precondition (the bridge component must be actively running — a bounded window, not an always-on service) and the sensitive scope is the one site the tool is built around; **the fix was written before it was named**, narrow and verified not to break the tool's own two legitimate clients ([write the patch first](scans/roflcoopter-viseron.html)). The 26: eight SQL hits (five high) are the [#1 identifier FP](scans/aurelio-labs-semantic-router.html) — every interpolation is a hardcoded table/column name with values bound as `?`, settled by reading the call sites; eight of sixteen mediums are one `github-actions-mutable-action-tag` flood; the rest are two `sha1()[:16]` dedup-id digests, two `urllib` calls to a local DevTools endpoint and a build script, a React rule on a loopback health-check `fetch`, and a Python-3.7 compatibility rule that is not a security check. Coverage `partial` and stated so — Semgrep did not cover every file and the dependency scan left `uv.lock` unaudited (Trivy read it, the [luck that hides the bug](scans/roflcoopter-viseron.html)), neither touching the finding. **Strict-norm** (`SECURITY.md` forbids public vulnerability issues, gives an email and a PVR link) · PVR-enabled, filed **privately, accepted into triage first try** ([GHSA-xp54-2hxc-w78q](https://github.com/can4hou6joeng4/boss-agent-cli/security/advisories/GHSA-xp54-2hxc-w78q)) **with the `vulnerabilities` array** — the [viseron rule](scans/roflcoopter-viseron.html) holds again · post-only, finding withheld at class level under embargo +- **2026-09-23** — [can4hou6joeng4/boss-agent-cli](scans/can4hou6joeng4-boss-agent-cli.html) — 26 findings at `medium+` (7 high, 16 medium, 3 meta), **1 real — withheld, filed privately** — a **CLI for the BOSS Zhipin recruitment platform** (2k★, MIT; a human drives it from a terminal wizard, an agent drives the same workflows over a JSON-envelope API, an ~77-tool MCP server, or a Python SDK) that authenticates by reusing the operator's own logged-in browser session. Responsive maintainer — 67 merged PRs from 7 human authors in 60 days — which is why it cleared the pre-check. **Zero of the 26 scanner findings is the vulnerability**, which is the whole point of the entry: the real item is the *absence* of a check on an otherwise-working local channel, the shape no rule can match, and it is the [desktop-loopback-inversion](scans/liaohch3-claude-tap.html) class — a `127.0.0.1` control surface that binds neither *who* may call it nor which host the request claims, driving privileged browser actions against a site where the operator is authenticated. Confirmed with a **server-side exploit primitive against the shipped daemon** (a foreign `Host` is served, a cross-origin "simple" request reaches the command channel with no preflight, a hostile `Origin` is not rejected) plus a read of the extension source for the browser half — the [run-the-primitive discipline](scans/observal-observal.html), because reading alone could not separate "the channel is drivable" from "the channel is drivable *and* blind does not blunt it." Graded **High not Critical** on an honest precondition (the bridge component must be running, and the user has to start it deliberately; once started it stays up while the browser side is connected — *corrected 2026-09-24: first published as "a bounded window", the one claim reasoned about rather than checked, and wrong both ways; the private report was corrected the same day*) and the sensitive scope is the one site the tool is built around; **the fix was written before it was named**, narrow and verified not to break the tool's own two legitimate clients ([write the patch first](scans/roflcoopter-viseron.html)). The 26: eight SQL hits (five high) are the [#1 identifier FP](scans/aurelio-labs-semantic-router.html) — every interpolation is a hardcoded table/column name with values bound as `?`, settled by reading the call sites; eight of sixteen mediums are one `github-actions-mutable-action-tag` flood; the rest are two `sha1()[:16]` dedup-id digests, two `urllib` calls to a local DevTools endpoint and a build script, a React rule on a loopback health-check `fetch`, and a Python-3.7 compatibility rule that is not a security check. Coverage `partial` and stated so — Semgrep did not cover every file and the dependency scan left `uv.lock` unaudited (Trivy read it, the [luck that hides the bug](scans/roflcoopter-viseron.html)), neither touching the finding. **Strict-norm** (`SECURITY.md` forbids public vulnerability issues, gives an email and a PVR link) · PVR-enabled, filed **privately, accepted into triage first try** ([GHSA-xp54-2hxc-w78q](https://github.com/can4hou6joeng4/boss-agent-cli/security/advisories/GHSA-xp54-2hxc-w78q)) **with the `vulnerabilities` array** — the [viseron rule](scans/roflcoopter-viseron.html) holds again · post-only, finding withheld at class level under embargo - **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. diff --git a/docs/scans/can4hou6joeng4-boss-agent-cli.md b/docs/scans/can4hou6joeng4-boss-agent-cli.md index 44d27f6..fac296c 100644 --- a/docs/scans/can4hou6joeng4-boss-agent-cli.md +++ b/docs/scans/can4hou6joeng4-boss-agent-cli.md @@ -89,10 +89,22 @@ shipped code rather than reasoning about them: one, which exposes the credential material for the logged-in session directly. The precondition is honest and worth stating plainly: this is only exploitable -while the bridge component is actively running, which is a bounded window, not an -always-on service — and the sensitive scope is the one site the tool is built -around. That is why it is a High and not a Critical, and I graded it that way in -the report rather than inviting the maintainer to discover the scope themselves. +while the bridge component is running, and the user has to start it deliberately — +nothing in the package starts it for them. Once started, though, it stays up for as +long as the browser side stays connected, so the exposure is the whole browsing +session rather than a short window — and the sensitive scope is the one site the +tool is built around. That is why it is a High and not a Critical, and I graded it +that way in the report rather than inviting the maintainer to discover the scope +themselves. + +*Correction, 2026-09-24:* the first version of this paragraph, and of the private +report, called the exposure "a bounded window". That was the one claim in the +report I reasoned about instead of checking, and it was wrong in both directions. I +had described the bridge as starting on its own: a start routine exists, but +nothing calls it, and the maintainer has since removed it as dead code. I had also +described an idle timeout that, read in full, does not fire while the browser side +is connected. The private report was corrected the same day; the finding, the fix +and the severity are unchanged. **No rule described it.** Every scanner here looks for a dangerous *thing that is present* — a tainted sink, a bad call, a known-vulnerable version. This is the From a734821a67414d329998bb438decbc62a536a0c7 Mon Sep 17 00:00:00 2001 From: elfrost <5491654+elfrost@users.noreply.github.com> Date: Thu, 24 Sep 2026 09:30:49 -0400 Subject: [PATCH 2/2] =?UTF-8?q?docs:=20scan=20#111=20=E2=80=94=20superdesi?= =?UTF-8?q?gndev/treg=20(outbound=20SSRF=20on=20the=20guard's=20unguarded?= =?UTF-8?q?=20sibling,=20reported=20privately)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 96 findings at medium+, 1 real after curation — withheld, reported privately by email (SECURITY.md forbids public issues; PVR disabled). The real finding: treg's call-time SSRF guard (host_is_public) is wired into the /call relay but not into a sibling server-side outbound path a plain member can drive; the registration-time check there is static and allows any DNS name, so a name resolving to an internal address reaches the request unchecked. Member-authenticated, semi-blind, reaches internal + cloud-metadata from treg's egress; the injected credential is the member's own (not cross-tenant theft) — graded Medium. Confirmed by running treg's own two guard functions side by side on a host that resolves internally. Post published with the finding withheld at class level; private email drafted and staged (the send is the operator's step). verdicts.json validated (exit 0) and copied to corpus/. Also updates the boss-agent-cli forward pointer. Co-Authored-By: Claude Opus 4.8 --- corpus/verdicts/superdesigndev-treg.json | 297 ++++++++++++++++++++ docs/index.md | 7 +- docs/scan-log.md | 3 +- docs/scans/can4hou6joeng4-boss-agent-cli.md | 3 +- docs/scans/superdesigndev-treg.md | 178 ++++++++++++ 5 files changed, 483 insertions(+), 5 deletions(-) create mode 100644 corpus/verdicts/superdesigndev-treg.json create mode 100644 docs/scans/superdesigndev-treg.md diff --git a/corpus/verdicts/superdesigndev-treg.json b/corpus/verdicts/superdesigndev-treg.json new file mode 100644 index 0000000..47c2cbf --- /dev/null +++ b/corpus/verdicts/superdesigndev-treg.json @@ -0,0 +1,297 @@ +{ + "repository": "C:\\Users\\Elfrost\\AppData\\Local\\Temp\\ai-patchlab-clone-eb9ea3mu\\repo", + "generated_at": "2026-09-24T13:14:23.423309+00:00", + "total_dismissed": 99, + "by_reason": { + "ignore-pattern": 1, + "below-min-severity": 3, + "by-design": 45, + "credited-defense": 6, + "sql-identifier-fp": 17, + "not-reachable": 19, + "active-harm-fp": 4, + "domain-noun-collision": 3, + "dependency-currency": 1 + }, + "records": [ + { + "source": "scanner", + "reason_code": "ignore-pattern", + "tool": "semgrep", + "rule": "generic.unicode.security.bidi.contains-bidirectional-characters", + "count": 1, + "verdict": "", + "detail": "Suppressed by an ignore pattern." + }, + { + "source": "scanner", + "reason_code": "below-min-severity", + "tool": "semgrep", + "rule": "package_managers.dependabot.dependabot-missing-cooldown.dependabot-missing-cooldown", + "count": 2, + "verdict": "", + "detail": "Below the --min-severity floor (medium)." + }, + { + "source": "scanner", + "reason_code": "below-min-severity", + "tool": "semgrep", + "rule": "package_managers.uv.uv-missing-dependency-cooldown.uv-missing-dependency-cooldown", + "count": 1, + "verdict": "", + "detail": "Below the --min-severity floor (medium)." + }, + { + "source": "curation", + "reason_code": "by-design", + "tool": "semgrep", + "rule": "javascript.vue.security.audit.xss.templates.avoid-v-html", + "count": 16, + "verdict": "false-positive", + "detail": "HelpPage.vue x16: v-html renders window.TREG_TUTORIAL, the project's own static tutorial (web/tutorial.js) - first-party content, no user or upstream data." + }, + { + "source": "curation", + "reason_code": "credited-defense", + "tool": "semgrep", + "rule": "javascript.vue.security.audit.xss.templates.avoid-v-html", + "count": 2, + "verdict": "false-positive", + "detail": "App.vue:217 + CopyToolDialog.vue:16: snippet HTML from state/snippets.js + state/skills.js; every interpolated tool field (name, host, path, example, method, org) passes esc(&<>) inside element text before wrapping, and a source comment says why. Attribute values are static literals." + }, + { + "source": "curation", + "reason_code": "sql-identifier-fp", + "tool": "semgrep", + "rule": "python.sqlalchemy.security.audit.avoid-sqlalchemy-text.avoid-sqlalchemy-text", + "count": 17, + "verdict": "false-positive", + "detail": "16 in alembic/versions (SET lock_timeout/statement_timeout from module constants + CONCURRENTLY index DDL); 1 in infra/db.py:330, a test-reset TRUNCATE over ORM-metadata table names quoted by the dialect's identifier_preparer. No value is ever interpolated." + }, + { + "source": "curation", + "reason_code": "by-design", + "tool": "semgrep", + "rule": "yaml.github-actions.security.github-actions-mutable-action-tag", + "count": 14, + "verdict": "hardening", + "detail": "Same-rule flood across the workflow files: SHA-pinning third-party actions is supply-chain hardening, not a vulnerability." + }, + { + "source": "curation", + "reason_code": "by-design", + "tool": "semgrep", + "rule": "html.security.audit.missing-integrity.missing-integrity", + "count": 6, + "verdict": "false-positive", + "detail": "6 of 8 hits are /preconnect tags (treg.to self-links, pbs.twimg.com) - not resource loads, SRI does not apply." + }, + { + "source": "curation", + "reason_code": "by-design", + "tool": "semgrep", + "rule": "html.security.audit.missing-integrity.missing-integrity", + "count": 2, + "verdict": "hardening", + "detail": "landing.html:583/1367 load lenis@1.3.26 CSS+JS from cdn.jsdelivr.net without integrity=; the landing page shares the treg.to origin with the /app dashboard and there is no site-wide CSP, so an integrity hash is worthwhile supply-chain hardening (it only matters after a CDN/npm compromise)." + }, + { + "source": "curation", + "reason_code": "not-reachable", + "tool": "semgrep", + "rule": "python.lang.security.audit.dynamic-urllib-use-detected.dynamic-urllib-use-detected", + "count": 9, + "verdict": "false-positive", + "detail": "Operator/dev scripts only (scripts/catalog_drift.py, catalog_fx_update.py, indexnow_submit.py x3, usage_report.py x2, e2e_check.py, one .agents skill script) with operator-configured URLs; none is on a request path. The 2 usage_report.py hits came from the targeted re-run that recovered 5 files lost to a semgrep temp-file error." + }, + { + "source": "curation", + "reason_code": "active-harm-fp", + "tool": "semgrep", + "rule": "python.lang.security.audit.insecure-file-permissions.insecure-file-permissions", + "count": 4, + "verdict": "false-positive", + "detail": "os.chmod(, 0o700) x4 (cli.py:2836 fsjail dir, localproxy.py:257, shell.py:272/275 session+shim dirs) - the project TIGHTENING permissions; the rule's looser suggestion would weaken them." + }, + { + "source": "curation", + "reason_code": "by-design", + "tool": "semgrep", + "rule": "python.lang.security.audit.insecure-file-permissions.insecure-file-permissions", + "count": 3, + "verdict": "false-positive", + "detail": "0o755 on executables: cli.py:3049 egress loader + cli.py:3140 runner (root-owned; per the source comment the member cannot modify it), shell.py:148 PATH shims (tool name/route/binary path, no secret)." + }, + { + "source": "curation", + "reason_code": "credited-defense", + "tool": "semgrep", + "rule": "generic.unicode.security.bidi.contains-bidirectional-characters", + "count": 2, + "verdict": "false-positive", + "detail": "scripts/catalog_ingest.py:103 is the project's own ZERO_WIDTH regex that STRIPS zero-width/bidi characters from ingested catalog text - the rule fired on the sanitizer." + }, + { + "source": "curation", + "reason_code": "domain-noun-collision", + "tool": "semgrep", + "rule": "python.lang.security.audit.logging.logger-credential-leak.python-logger-credential-disclosure", + "count": 2, + "verdict": "false-positive", + "detail": "application/auth.py:952 logs a refresh-token FAMILY id + revoked count (reuse detection), not a token; application/call/evidence.py:160 logs the exception from the fail-closed credential-masking layer (_secret_renderings), which then replaces the evidence wholesale." + }, + { + "source": "curation", + "reason_code": "credited-defense", + "tool": "semgrep", + "rule": "python.flask.security.audit.directly-returned-format-string.directly-returned-format-string", + "count": 1, + "verdict": "false-positive", + "detail": "routers/auth.py:180 _login_callback_base returns https://{host} only when the parsed Host is in PUBLIC_HOST_ALIASES (treg.to, treg.superdesign.dev), built from the parsed value not the raw header; a URL string, not an HTML response." + }, + { + "source": "curation", + "reason_code": "by-design", + "tool": "semgrep", + "rule": "python.flask.security.audit.directly-returned-format-string.directly-returned-format-string", + "count": 1, + "verdict": "hardening", + "detail": "routers/billing.py:131 _return_base builds the Stripe return URL from the raw Host header (documented intent: preview/local servers return to themselves). Same-user only - the URL goes back to the requesting admin's own Checkout/portal session - but its login sibling already allowlists Host; reusing PUBLIC_HOST_ALIASES/public_url would align them." + }, + { + "source": "curation", + "reason_code": "credited-defense", + "tool": "semgrep", + "rule": "python.django.security.injection.tainted-url-host.tainted-url-host", + "count": 1, + "verdict": "false-positive", + "detail": "routers/auth.py:180 - same site as above: Host allowlisted against PUBLIC_HOST_ALIASES before use." + }, + { + "source": "curation", + "reason_code": "not-reachable", + "tool": "semgrep", + "rule": "javascript.lang.security.audit.detect-non-literal-regexp.detect-non-literal-regexp", + "count": 2, + "verdict": "false-positive", + "detail": "web/tutorial.js:514 (+ its dashboard-legacy copy): RegExp compiled from the project's own static highlighter RULES table; no attacker input reaches the pattern." + }, + { + "source": "curation", + "reason_code": "not-reachable", + "tool": "semgrep", + "rule": "javascript.lang.security.insecure-object-assign.insecure-object-assign", + "count": 1, + "verdict": "false-positive", + "detail": "frontend/src/state/tools.js:19 deep-copies the org's own tool CLI profile into fresh local objects for an edit form; no shared/global prototype is reachable." + }, + { + "source": "curation", + "reason_code": "by-design", + "tool": "semgrep", + "rule": "python.lang.security.insecure-hash-algorithms.insecure-hash-algorithm-sha1", + "count": 1, + "verdict": "false-positive", + "detail": "cli.py:1570 sha1(base_url)[:10] names a local catalog cache file per server - non-security identifier." + }, + { + "source": "curation", + "reason_code": "by-design", + "tool": "semgrep", + "rule": "python.lang.correctness.len-all-count.len-all-count", + "count": 1, + "verdict": "not-applicable", + "detail": "Correctness/perf lint, not a security rule." + }, + { + "source": "curation", + "reason_code": "by-design", + "tool": "semgrep", + "rule": "python.lang.compatibility.python37.python37-compatibility-importlib2", + "count": 1, + "verdict": "not-applicable", + "detail": "Python 3.7 compatibility lint; the project targets modern Python." + }, + { + "source": "curation", + "reason_code": "dependency-currency", + "tool": "trivy", + "rule": "CVE-2026-63374", + "count": 1, + "verdict": "hardening", + "detail": "anyio 4.14.1 -> 4.14.2 (uv.lock; the deploy example installs with --locked). IDNA 2003/2008 hostname confusion in connect_tcp/TLSStream.wrap. On the path: treg's async httpx calls go through httpcore's anyio backend. Exploitation needs a network-positioned attacker AND a non-ASCII upstream host whose IDNA 2003/2008 mappings differ. Lock refresh." + }, + { + "source": "curation", + "reason_code": "not-reachable", + "tool": "trivy", + "rule": "CVE-2026-63349", + "count": 1, + "verdict": "not-applicable", + "detail": "anyio extra_groups bug in run_process/open_process: treg never calls either (only anyio.to_thread in infra/stripe.py)." + }, + { + "source": "curation", + "reason_code": "not-reachable", + "tool": "trivy", + "rule": "CVE-2026-64847", + "count": 1, + "verdict": "not-applicable", + "detail": "anyio process-pool stderr DoS: treg never uses anyio.to_process." + }, + { + "source": "curation", + "reason_code": "not-reachable", + "tool": "trivy", + "rule": "CVE-2026-84382", + "count": 1, + "verdict": "not-applicable", + "detail": "httpx2 client-side decompression DoS: httpx2 is a transitive dep of the mcp server extra; treg never imports it and mcp.py uses server-side SDK modules only." + }, + { + "source": "curation", + "reason_code": "not-reachable", + "tool": "trivy", + "rule": "CVE-2026-84379", + "count": 1, + "verdict": "not-applicable", + "detail": "httpx2 client-side multipart header injection: same - no httpx2 client code runs in treg." + }, + { + "source": "curation", + "reason_code": "not-reachable", + "tool": "trivy", + "rule": "CVE-2026-84380", + "count": 1, + "verdict": "not-applicable", + "detail": "httpx2 client-side request smuggling: same - no httpx2 client code runs in treg." + }, + { + "source": "curation", + "reason_code": "not-reachable", + "tool": "trivy", + "rule": "CVE-2026-41205", + "count": 1, + "verdict": "not-applicable", + "detail": "mako via alembic: template lookup is used only when generating migration scripts (dev time), never on a request path." + }, + { + "source": "curation", + "reason_code": "not-reachable", + "tool": "trivy", + "rule": "CVE-2026-44307", + "count": 1, + "verdict": "not-applicable", + "detail": "mako via alembic: same as CVE-2026-41205." + }, + { + "source": "curation", + "reason_code": "domain-noun-collision", + "tool": "gitleaks", + "rule": "generic-api-key", + "count": 1, + "verdict": "false-positive", + "detail": "catalog/anyapi.extended.yaml:6722 is a YouTube API continuationToken - a public pagination cursor in a catalog example request, not a credential." + } + ] +} \ No newline at end of file diff --git a/docs/index.md b/docs/index.md index 942dae1..9c2ec4a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,7 +1,7 @@ --- layout: default title: AI PatchLab Scans -description: "110 curated security scans of open-source AI agents, MCP servers and LLM apps - 25 confirmed fixes, run local-first with Semgrep, Gitleaks, Trivy and pip-audit." +description: "111 curated security scans of open-source AI agents, MCP servers and LLM apps - 25 confirmed fixes, run local-first with Semgrep, Gitleaks, Trivy and pip-audit." --- # AI PatchLab Scans @@ -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 }}). 110 scans, 25 confirmed fixes, methodology in the open. +> [**work with me →**]({{ '/work-with-me' | relative_url }}). 111 scans, 25 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; @@ -103,7 +103,7 @@ login and static assets. Fifty-two flagged, none reported. ## All scans -110 scans, newest first. **Findings** is the raw count the tools produced; +111 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. @@ -118,6 +118,7 @@ filed, which is the usual outcome of a clean scan. | Date | Repository | Findings | Real | Outcome | | --- | --- | ---: | --- | --- | +| 2026-09-24 | [superdesigndev/treg](scans/superdesigndev-treg.html) | 96 | 1 real — withheld | private | | 2026-09-23 | [can4hou6joeng4/boss-agent-cli](scans/can4hou6joeng4-boss-agent-cli.html) | 26 | 1 real — withheld | private | | 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 | — | diff --git a/docs/scan-log.md b/docs/scan-log.md index 5ae92bc..108d98b 100644 --- a/docs/scan-log.md +++ b/docs/scan-log.md @@ -6,8 +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. 110 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. 111 scans. For the compact index, see the [scan log home]({{ '/' | relative_url }}). +- **2026-09-24** — [superdesigndev/treg](scans/superdesigndev-treg.html) — 96 findings at `medium+` (1 critical, 23 high, 69 medium, 3 meta), **1 real — withheld, reported privately by email** — **"OpenRouter for agent tools"** (3k★, source-available; hosted at treg.to and self-hostable), a multi-tenant server holding a team's provider credentials Fernet-encrypted and proxying calls to 3,000+ upstream endpoints with the credential injected server-side so it never reaches the caller. Its `SECURITY.md` makes two load-bearing promises: the proxy never hands a key to the caller, and outbound requests are *"re-resolved at call time and internal/metadata addresses are refused."* **Zero of the 96 scanner findings is the vulnerability** — the real item came from tabulating the server's outbound request paths and asking which the call-time host guard actually covers. It does have a proper guard, `host_is_public` in `infra/upstream/ssrf.py`, and its address classification is genuinely thorough (every loopback/link-local/CGNAT/NAT64 and decimal/hex/octal/short-form IPv4 literal I tried was refused). The finding is that the guard is wired into the main proxy path and **not** into a sibling outbound path a plain member can drive; the registration-time check that does run there is, by its own docstring, a static check that allows any DNS name, so a name resolving to an internal address passes it and nothing re-resolves before the request goes out — a member-authenticated, semi-blind [SSRF on the unguarded sibling of a guarded transport](scans/liaohch3-claude-tap.html). Confirmed with the [run-the-primitive](scans/observal-observal.html) discipline: treg's own two guard functions run side by side on a host that resolves internally return "allowed" (registration) and "refused" (call-time), and the sibling path calls only the first — one differential, using the project's code not my reasoning. Graded **Medium**: the injected credential on the affected path is the member's own, so it is internal + cloud-metadata reach, not cross-tenant theft; the org boundary itself held everywhere ([the probe could return "no" on isolation and did](scans/tracecathq-tracecat.html)). **The fix is one guard applied at the outbound path** and changes nothing for a legitimate public target. The 96: seventeen `sqlalchemy-text` are the [#1 identifier FP](scans/aurelio-labs-semantic-router.html) (Alembic `SET`/`CREATE INDEX CONCURRENTLY` from module constants + a test-reset `TRUNCATE` over dialect-quoted table names, no value interpolated); eighteen `v-html` render the project's own static tutorial/snippet HTML with an `esc(&<>)` helper on every interpolated field; four `insecure-file-permissions` are the project [tightening to `0o700`](scans/realiti4-claude-swap.html) (the rule's suggestion would loosen them); one Trivy critical is `anyio` 4.14.1→4.14.2 IDNA hostname-confusion, a worthwhile lockfile bump reachable only by a network-positioned attacker against a non-ASCII upstream host. Coverage `partial` and stated so — Semgrep hit a Windows per-file temp error on five large modules (recovered by a targeted re-run of just those five), and pip-audit left `uv.lock` unaudited while Trivy read it; neither touches the finding. **Strict-norm** (`SECURITY.md` forbids public vuln issues, PVR disabled — checked `{"enabled":false}` — names an email) · reported **privately by email to the maintainer**; post-only here, finding withheld at class level, **the send is the operator's step** - **2026-09-23** — [can4hou6joeng4/boss-agent-cli](scans/can4hou6joeng4-boss-agent-cli.html) — 26 findings at `medium+` (7 high, 16 medium, 3 meta), **1 real — withheld, filed privately** — a **CLI for the BOSS Zhipin recruitment platform** (2k★, MIT; a human drives it from a terminal wizard, an agent drives the same workflows over a JSON-envelope API, an ~77-tool MCP server, or a Python SDK) that authenticates by reusing the operator's own logged-in browser session. Responsive maintainer — 67 merged PRs from 7 human authors in 60 days — which is why it cleared the pre-check. **Zero of the 26 scanner findings is the vulnerability**, which is the whole point of the entry: the real item is the *absence* of a check on an otherwise-working local channel, the shape no rule can match, and it is the [desktop-loopback-inversion](scans/liaohch3-claude-tap.html) class — a `127.0.0.1` control surface that binds neither *who* may call it nor which host the request claims, driving privileged browser actions against a site where the operator is authenticated. Confirmed with a **server-side exploit primitive against the shipped daemon** (a foreign `Host` is served, a cross-origin "simple" request reaches the command channel with no preflight, a hostile `Origin` is not rejected) plus a read of the extension source for the browser half — the [run-the-primitive discipline](scans/observal-observal.html), because reading alone could not separate "the channel is drivable" from "the channel is drivable *and* blind does not blunt it." Graded **High not Critical** on an honest precondition (the bridge component must be running, and the user has to start it deliberately; once started it stays up while the browser side is connected — *corrected 2026-09-24: first published as "a bounded window", the one claim reasoned about rather than checked, and wrong both ways; the private report was corrected the same day*) and the sensitive scope is the one site the tool is built around; **the fix was written before it was named**, narrow and verified not to break the tool's own two legitimate clients ([write the patch first](scans/roflcoopter-viseron.html)). The 26: eight SQL hits (five high) are the [#1 identifier FP](scans/aurelio-labs-semantic-router.html) — every interpolation is a hardcoded table/column name with values bound as `?`, settled by reading the call sites; eight of sixteen mediums are one `github-actions-mutable-action-tag` flood; the rest are two `sha1()[:16]` dedup-id digests, two `urllib` calls to a local DevTools endpoint and a build script, a React rule on a loopback health-check `fetch`, and a Python-3.7 compatibility rule that is not a security check. Coverage `partial` and stated so — Semgrep did not cover every file and the dependency scan left `uv.lock` unaudited (Trivy read it, the [luck that hides the bug](scans/roflcoopter-viseron.html)), neither touching the finding. **Strict-norm** (`SECURITY.md` forbids public vulnerability issues, gives an email and a PVR link) · PVR-enabled, filed **privately, accepted into triage first try** ([GHSA-xp54-2hxc-w78q](https://github.com/can4hou6joeng4/boss-agent-cli/security/advisories/GHSA-xp54-2hxc-w78q)) **with the `vulnerabilities` array** — the [viseron rule](scans/roflcoopter-viseron.html) holds again · post-only, finding withheld at class level under embargo - **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. diff --git a/docs/scans/can4hou6joeng4-boss-agent-cli.md b/docs/scans/can4hou6joeng4-boss-agent-cli.md index fac296c..a6f0cb7 100644 --- a/docs/scans/can4hou6joeng4-boss-agent-cli.md +++ b/docs/scans/can4hou6joeng4-boss-agent-cli.md @@ -173,7 +173,8 @@ python scanner/run_scan.py --repo /tmp/scan-target --reports-dir ./reports/can4h ## More from this series +- **Next scan:** [superdesigndev/treg](superdesigndev-treg.html) — 2026-09-24, 1 real — withheld - **Previous scan:** [overwirehq/claude-code-telegram](overwirehq-claude-code-telegram.html) — 2026-09-22, 0 first-party -- [Every scan in the series]({{ '/' | relative_url }}) — 110 repositories, newest first +- [Every scan in the series]({{ '/' | relative_url }}) — 111 repositories, newest first - [Where a maintainer shipped a fix]({{ '/fixed' | relative_url }}) — the 25 that resolved - [Scans that found nothing]({{ '/clean' | relative_url }}) — published as they were diff --git a/docs/scans/superdesigndev-treg.md b/docs/scans/superdesigndev-treg.md new file mode 100644 index 0000000..2c007c4 --- /dev/null +++ b/docs/scans/superdesigndev-treg.md @@ -0,0 +1,178 @@ +--- +layout: default +title: "superdesigndev/treg: security scan" +description: "Security scan of superdesigndev/treg: 96 findings at medium+, 1 real — withheld, reported privately. Local-first curated review: Semgrep, Gitleaks, Trivy, pip-audit." +date: 2026-09-24 +--- + +# superdesigndev/treg — security scan + +**Repository:** [superdesigndev/treg](https://github.com/superdesigndev/treg) +**Commit scanned:** `240c595` +**Scan date:** 2026-09-24 +**Disclosure status:** withheld — one real finding reported privately by email, per the +project's `SECURITY.md` (which asks that vulnerabilities not be opened as public +issues; GitHub private vulnerability reporting is disabled on the repo, and the +policy names an email address, so email is the channel). Detail below is kept at +class level until the maintainer has had a chance to respond. **The private send +is the operator's step and had not gone out when this page was published.** + +## Summary + +| Severity | Count | +| --- | ---: | +| Critical | 1 | +| High | 23 | +| Medium | 69 | +| Low | — | +| Info | 3 (scanner meta) | + +**Total findings:** 96 at `--min-severity medium` (1 real after curation — withheld). +The one real finding is **not among the 96** — no rule reported it. It came from a +structural question about the server's outbound requests, not a pattern match. + +treg (3.0k★, source-available) is **"OpenRouter for agent tools"**: a multi-tenant +server that holds a team's provider credentials (Fernet-encrypted, server-side) and +proxies tool calls to 3,000+ upstream endpoints, injecting the credential so it never +reaches the caller. It runs hosted at treg.to and is self-hostable. A security model +like that lives and dies on two promises its own `SECURITY.md` makes plainly: the +proxy never hands a key to the caller, and outbound requests are re-resolved at call +time so they cannot be aimed at internal or cloud-metadata hosts. + +Maintenance is responsive: hundreds of merged PRs from several human authors in the +last 60 days, with real back-and-forth on closed issues. That responsiveness is why +it cleared the pre-check. + +## Scan coverage + +| Tool | Status | Detail | +| --- | --- | --- | +| `semgrep` | `partial` | did not cover every file it was pointed at (`semgrep-partial-coverage`) | +| `gitleaks` | `ran` | no coverage problem reported | +| `trivy` | `ran` | no coverage problem reported | +| `dependency-scan` | `partial` | a shipped lockfile was not covered (`dependency-scan-unaudited-lockfile`) | +| `ai-security-review` | `not_run` | disabled by default (ADR-010) | + +**Coverage complete:** no. Semgrep hit a per-file error on five large modules — a +Windows temp-file permission error mid-scan, not a parse failure — so I re-ran those +five files on their own and folded the two recovered findings (both dev-script +`urllib` calls) into the curation below. The dependency scan left the shipped +`uv.lock` unaudited by pip-audit; Trivy read it in the same run, which is what +carried the dependency picture. Neither gap touches the real finding, which is in +first-party code the scanners did read — they simply had no rule for its shape. + +## The one real finding, at class level + +**Severity: Medium. Class: a server-side outbound request path, reachable by any +member, that skips the call-time host check the main proxy enforces — an SSRF on the +unguarded sibling of a guarded transport.** + +The interesting thing about treg's SSRF posture is that it is *mostly right*. It has a +proper call-time guard that resolves a target host and refuses internal, loopback, +link-local, CGNAT, NAT64 and cloud-metadata addresses — I tried the whole zoo of +obfuscated IPv4 literals (decimal, hex, octal, short-form) and the NAT64 prefix, and +every one was refused. That guard is wired into the main proxy path, exactly where the +threat model expects it. + +The finding is that the same server makes outbound requests on **more than one** path, +and the call-time guard is applied on one of them and not on a sibling that an +ordinary member can drive. The registration-time check that *does* run on the sibling +is, by its own documented design, a static check that deliberately allows any DNS +name — so a name that resolves to an internal address passes it, and nothing +re-resolves-and-checks before the request goes out. The result is a member-authenticated +server-side request reachable from the product's own egress, with enough of a +status/error signal returned to make it useful. + +I confirmed the gap the way this series always tries to: not by reading that a check +was missing, but by running the project's *own* two guard functions side by side on a +host that resolves internally. One returns "allowed" (the registration check); the +other returns "refused" (the call-time check the sibling path never calls). That one +differential is the whole finding, and it is decisive because it uses treg's code, not +my reasoning about it. + +**Why Medium, and stated plainly:** the credential injected on the affected path is the +member's *own*, so this is not a route to another tenant's key — it is internal +reconnaissance and metadata reach, not cross-tenant theft. It needs an authenticated +member, which on a self-service product is a low bar but not zero. I graded it Medium +in the private report rather than inviting the maintainer to discover the scope +themselves. + +**The fix is one guard, applied at the outbound path, and it changes nothing for a +legitimate public target** — it only refuses the hosts the project's own documentation +already says are refused at call time. The private report gives the exact call sites +and the one-line change at each, plus a few clearly-labelled lower-confidence +authorization observations for the maintainer to assess. The org boundary itself held +everywhere I looked — I found no way for one tenant to reach another's data, and I say +so because a probe that can only ever return "yes" is not worth much; this one could +have returned "no" on the isolation question and did. + +**No rule described it.** Every scanner here looks for a dangerous *thing that is +present* — a tainted sink, a bad call, a known-vulnerable version. This is a check that +is present and correct on one path and simply *absent* on its sibling, which is the +shape no per-file pattern matcher can see. It is the same class as several earlier +findings in this series where a control guarded one transport and not the one beside +it. + +## Why the rest were dismissed + +| Reason | Count | | +| --- | ---: | --- | +| `by-design` | 45 | non-security, intended behaviour, or supply-chain hardening | +| `not-reachable` | 19 | dev/operator scripts, or a transitive CVE not on a request path | +| `sql-identifier-fp` | 17 | identifier-only interpolation, values bound | +| `credited-defense` | 6 | the rule fired on the project's own guard | +| `active-harm-fp` | 4 | the suggested change would *loosen* a `0o700` tightening | +| `domain-noun-collision` | 3 | a "token"/"key" that is a pagination cursor or a family id | +| `dependency-currency` | 1 | one lockfile bump worth making, not a first-party defect | + +The two biggest survivors are the recurring ones. Seventeen `sqlalchemy-text` hits are +the **#1 identifier false positive**: sixteen are Alembic migrations setting +`lock_timeout` from module constants and building `CREATE INDEX CONCURRENTLY` DDL, and +the last is a test-reset `TRUNCATE` over ORM-metadata table names quoted by the +dialect's own identifier preparer — no value is ever interpolated. Eighteen `v-html` +hits render the project's *own* static tutorial and syntax-highlighted snippet HTML, +where every interpolated tool field is passed through an `esc(&<>)` helper before +wrapping and a source comment says why. Four `insecure-file-permissions` hits are the +project **tightening** to `0o700` on its sandbox directories — the rule's looser +suggestion would weaken them, the [active-harm false-positive](realiti4-claude-swap.html) +shape. And the one Trivy critical (`anyio` 4.14.1 → 4.14.2, an IDNA hostname-confusion +issue) is a real lockfile bump worth making, reachable only by a network-positioned +attacker against a non-ASCII upstream host — hardening, not a first-party vulnerability. + +## Notes on the tool + +- **The real finding was invisible to all four scanners, and that is the point.** 96 + findings at `medium+`, zero of them the vulnerability. It came from tabulating the + server's outbound request paths and asking which ones the call-time host guard + actually covers — a question no per-file rule poses. +- **Coverage was partial in two ways, and the report says so.** Semgrep hit a + Windows-side per-file temp error on five large modules (recovered by a targeted + re-run), and pip-audit left `uv.lock` unaudited while Trivy read it. Both are in the + coverage block above; neither touches the finding. +- **Same-rule and same-shape flooding, again.** The 96 collapse to a handful of + families — eighteen `v-html`, seventeen `sqlalchemy-text`, fourteen unpinned-action + hits. Collapsing repeated same-rule hits into one entry with a count remains a + standing backlog item across the series. + +--- + +*Scanned with [AI PatchLab](https://github.com/elfrost/ai-patchlab). Findings are +curated by hand; scanner output alone is not a vulnerability report. This page will be +updated with full technical detail once the maintainer has responded and a fix has +shipped.* + +## Reproduce + +```bash +git clone https://github.com/superdesigndev/treg /tmp/scan-target +python scanner/run_scan.py --repo /tmp/scan-target --reports-dir ./reports/superdesigndev-treg --min-severity medium --ignore-samples +``` + +--- + +## More from this series + +- **Previous scan:** [can4hou6joeng4/boss-agent-cli](can4hou6joeng4-boss-agent-cli.html) — 2026-09-23, 1 real — withheld +- [Every scan in the series]({{ '/' | relative_url }}) — 111 repositories, newest first +- [Where a maintainer shipped a fix]({{ '/fixed' | relative_url }}) — the 25 that resolved +- [Scans that found nothing]({{ '/clean' | relative_url }}) — published as they were