Vulnerability
Three independent Low-severity hardening observations found during the CI-1429 security audit, none individually exploitable, batched into one issue per the security-audit protocol (P3 batching):
1. /metrics endpoint bypasses auth and rate limiting by design
Location: crates/zeph-gateway/src/handlers.rs:208-234 (feature-gated behind prometheus), documented at crates/zeph-gateway/src/server.rs:257-259.
The Prometheus scrape endpoint is intentionally unauthenticated, matching standard Prometheus practice. If the prometheus feature is compiled into a gateway binary that is directly internet-facing (rather than behind a reverse proxy / network ACL), any caller with network access can read operational counters (request rates, etc.).
Remediation: Document that /metrics must be restricted via network policy or reverse-proxy ACL when the gateway is exposed beyond a trusted network; consider an opt-in bearer check for /metrics for deployments without a fronting proxy.
2. MCP OAuth vault-key collision between server IDs differing only by separator
Location: src/bootstrap/oauth.rs:27-31 (key derivation ZEPH_MCP_OAUTH_{SERVER_ID}, uppercased with - replaced by _); collision documented in test vault_key_collision_documented (src/bootstrap/oauth.rs:113-122) but never enforced at config load time.
Two operator-configured MCP server IDs like my-app and my_app collide into the same vault key, so one server's stored OAuth token can overwrite/leak into the other's slot. Not attacker-reachable (server IDs come from the operator's own local config, not untrusted input), so this is a configuration footgun rather than a vulnerability.
Remediation: Add validation at MCP config load (src/bootstrap/mcp.rs or equivalent) that rejects two configured server IDs whose derived vault keys collide, rather than only documenting the collision in a test comment.
3. GrantKind::Tool in zeph-subagent is unused and implies a mechanism that doesn't exist
Location: crates/zeph-subagent/src/grants.rs:68-73.
GrantKind::Tool ("a tool name granted at runtime beyond the definition's static policy") is defined, documented, and unit-tested, but never constructed or consulted anywhere outside grants.rs/its own tests — no approve_tool/grant_tool function exists alongside approve_secret. Actual tool-call enforcement is correctly done via the static ToolPolicy allow/deny list in crates/zeph-subagent/src/filter.rs:117-128,190. No security impact today since nothing relies on the unused variant to gate access, but the module's implied capability (a TTL-bounded runtime tool grant) is misleading to future readers/maintainers reasoning about the trust boundary.
Remediation: Either wire up GrantKind::Tool into actual dispatch-time enforcement (mirroring approve_secret/deliver_secret), or remove the dead variant and its tests to avoid the false impression of a working escalation-control mechanism.
Severity
Low (P3) — none of the three findings are independently exploitable; all are hardening/hygiene observations found while auditing less-sampled crates (zeph-gateway, zeph-mcp/oauth.rs, zeph-subagent) during CI-1429.
Attack Scenario
No attacker-reachable exploit for any of the three. #1 requires a specific feature flag plus a misconfigured deployment topology; #2 requires operator-introduced config ambiguity, not external input; #3 is dead code with no live code path relying on it.
Remediation
See per-item remediation above. All three are appropriate for a routine hardening pass, not urgent fixes.
References
Found during CI-1429 read-only security audit (crates/zeph-orchestration, crates/zeph-subagent, crates/zeph-gateway, crates/zeph-mcp/src/oauth.rs sweep). No RUSTSEC/CWE mapping — internal hardening findings.
Vulnerability
Three independent Low-severity hardening observations found during the CI-1429 security audit, none individually exploitable, batched into one issue per the security-audit protocol (P3 batching):
1.
/metricsendpoint bypasses auth and rate limiting by designLocation:
crates/zeph-gateway/src/handlers.rs:208-234(feature-gated behindprometheus), documented atcrates/zeph-gateway/src/server.rs:257-259.The Prometheus scrape endpoint is intentionally unauthenticated, matching standard Prometheus practice. If the
prometheusfeature is compiled into a gateway binary that is directly internet-facing (rather than behind a reverse proxy / network ACL), any caller with network access can read operational counters (request rates, etc.).Remediation: Document that
/metricsmust be restricted via network policy or reverse-proxy ACL when the gateway is exposed beyond a trusted network; consider an opt-in bearer check for/metricsfor deployments without a fronting proxy.2. MCP OAuth vault-key collision between server IDs differing only by separator
Location:
src/bootstrap/oauth.rs:27-31(key derivationZEPH_MCP_OAUTH_{SERVER_ID}, uppercased with-replaced by_); collision documented in testvault_key_collision_documented(src/bootstrap/oauth.rs:113-122) but never enforced at config load time.Two operator-configured MCP server IDs like
my-appandmy_appcollide into the same vault key, so one server's stored OAuth token can overwrite/leak into the other's slot. Not attacker-reachable (server IDs come from the operator's own local config, not untrusted input), so this is a configuration footgun rather than a vulnerability.Remediation: Add validation at MCP config load (
src/bootstrap/mcp.rsor equivalent) that rejects two configured server IDs whose derived vault keys collide, rather than only documenting the collision in a test comment.3.
GrantKind::Toolin zeph-subagent is unused and implies a mechanism that doesn't existLocation:
crates/zeph-subagent/src/grants.rs:68-73.GrantKind::Tool("a tool name granted at runtime beyond the definition's static policy") is defined, documented, and unit-tested, but never constructed or consulted anywhere outsidegrants.rs/its own tests — noapprove_tool/grant_toolfunction exists alongsideapprove_secret. Actual tool-call enforcement is correctly done via the staticToolPolicyallow/deny list incrates/zeph-subagent/src/filter.rs:117-128,190. No security impact today since nothing relies on the unused variant to gate access, but the module's implied capability (a TTL-bounded runtime tool grant) is misleading to future readers/maintainers reasoning about the trust boundary.Remediation: Either wire up
GrantKind::Toolinto actual dispatch-time enforcement (mirroringapprove_secret/deliver_secret), or remove the dead variant and its tests to avoid the false impression of a working escalation-control mechanism.Severity
Low (P3) — none of the three findings are independently exploitable; all are hardening/hygiene observations found while auditing less-sampled crates (
zeph-gateway,zeph-mcp/oauth.rs,zeph-subagent) during CI-1429.Attack Scenario
No attacker-reachable exploit for any of the three. #1 requires a specific feature flag plus a misconfigured deployment topology; #2 requires operator-introduced config ambiguity, not external input; #3 is dead code with no live code path relying on it.
Remediation
See per-item remediation above. All three are appropriate for a routine hardening pass, not urgent fixes.
References
Found during CI-1429 read-only security audit (crates/zeph-orchestration, crates/zeph-subagent, crates/zeph-gateway, crates/zeph-mcp/src/oauth.rs sweep). No RUSTSEC/CWE mapping — internal hardening findings.