# Risk map — labstack/echo

> Canonical page: https://codevetter.com/unpack/echo/risk-map

# Risk map — labstack/echo

Scanned commit 99c395d846e079d4cffd29de9298d1dd829e8b04 (2026-10-01). Generated by codevetter codevetter 1.15.0 on 2026-10-02.

Source inspection; upstream code and tests were not executed.

These findings describe source-level boundaries and fragile behavior, not confirmed vulnerabilities. Most security decisions are configured by applications through middleware, validators, trust policies, and filesystem roots. Pool reuse and response commitment couple changes in dispatch, serialization, logging, and error handling. One connection-lifecycle concern is explicitly a hypothesis requiring focused verification.
- Static serving defaults to no path unescaping and rejects dot/empty segments, but explicitly enabling decoding can still make encoded separators resolve beyond route-based guards. Filesystem root selection is also significant: defaultFS sub-root construction accepts configured absolute or parent-relative roots outside the initial working directory. — [echo.go#L322-L345](https://github.com/labstack/echo/blob/99c395d846e079d4cffd29de9298d1dd829e8b04/echo.go#L322-L345) [echo.go#L657-L690](https://github.com/labstack/echo/blob/99c395d846e079d4cffd29de9298d1dd829e8b04/echo.go#L657-L690) [echo.go#L953-L967](https://github.com/labstack/echo/blob/99c395d846e079d4cffd29de9298d1dd829e8b04/echo.go#L953-L967) [middleware/static.go#L60-L80](https://github.com/labstack/echo/blob/99c395d846e079d4cffd29de9298d1dd829e8b04/middleware/static.go#L60-L80)
- JSON deserialization buffers the full body before decoding; the 64 KiB cap only controls pool retention. Request-size enforcement depends on separately installed BodyLimit or MaxBytesReader, while decompression has its own configurable expansion limit that can be disabled. — [json.go#L22-L24](https://github.com/labstack/echo/blob/99c395d846e079d4cffd29de9298d1dd829e8b04/json.go#L22-L24) [json.go#L43-L65](https://github.com/labstack/echo/blob/99c395d846e079d4cffd29de9298d1dd829e8b04/json.go#L43-L65) [middleware/body_limit.go#L63-L74](https://github.com/labstack/echo/blob/99c395d846e079d4cffd29de9298d1dd829e8b04/middleware/body_limit.go#L63-L74) [middleware/decompress.go#L24-L29](https://github.com/labstack/echo/blob/99c395d846e079d4cffd29de9298d1dd829e8b04/middleware/decompress.go#L24-L29)
- BodyDump's request cap changes downstream input: it reads only the prefix, drains the remainder, and replaces Request.Body with the captured prefix. TestBodyDump_RequestExceedsLimit explicitly expects this truncation; payload callbacks also receive raw content that application logging must handle appropriately. — [middleware/body_dump.go#L97-L113](https://github.com/labstack/echo/blob/99c395d846e079d4cffd29de9298d1dd829e8b04/middleware/body_dump.go#L97-L113) [middleware/body_dump.go#L136-L139](https://github.com/labstack/echo/blob/99c395d846e079d4cffd29de9298d1dd829e8b04/middleware/body_dump.go#L136-L139) [middleware/body_dump_test.go#L255-L284](https://github.com/labstack/echo/blob/99c395d846e079d4cffd29de9298d1dd829e8b04/middleware/body_dump_test.go#L255-L284)
- Forwarded identity and scheme depend on trust configuration: direct-peer IP is the default, but LegacyIPExtractor and LegacySchemeExtractor retain header-trusting behavior. Scheme extraction trusts private/loopback/link-local peers by default and requires trusted proxies to overwrite forwarding headers. — [ip.go#L113-L120](https://github.com/labstack/echo/blob/99c395d846e079d4cffd29de9298d1dd829e8b04/ip.go#L113-L120) [ip.go#L269-L279](https://github.com/labstack/echo/blob/99c395d846e079d4cffd29de9298d1dd829e8b04/ip.go#L269-L279) [scheme.go#L32-L57](https://github.com/labstack/echo/blob/99c395d846e079d4cffd29de9298d1dd829e8b04/scheme.go#L32-L57)
- BasicAuth and KeyAuth delegate secure credential comparison to application validators. KeyAuth can deliberately continue after rejected/missing credentials when ErrorHandler returns nil and ContinueOnIgnoredError is enabled, so downstream authorization must account for that configured public-access branch. — [middleware/basic_auth.go#L41-L46](https://github.com/labstack/echo/blob/99c395d846e079d4cffd29de9298d1dd829e8b04/middleware/basic_auth.go#L41-L46) [middleware/key_auth.go#L57-L70](https://github.com/labstack/echo/blob/99c395d846e079d4cffd29de9298d1dd829e8b04/middleware/key_auth.go#L57-L70) [middleware/key_auth.go#L187-L202](https://github.com/labstack/echo/blob/99c395d846e079d4cffd29de9298d1dd829e8b04/middleware/key_auth.go#L187-L202)
- Echo fields and DefaultRouter mutations are documented as unsafe during serving; concurrentRouter locks router operations only. serveHTTP returns Context to the pool after processing, and Reset clears its state, making escaped Context references and arbitrary runtime configuration changes important review boundaries. — [echo.go#L64-L68](https://github.com/labstack/echo/blob/99c395d846e079d4cffd29de9298d1dd829e8b04/echo.go#L64-L68) [router_concurrent.go#L21-L46](https://github.com/labstack/echo/blob/99c395d846e079d4cffd29de9298d1dd829e8b04/router_concurrent.go#L21-L46) [echo.go#L828-L846](https://github.com/labstack/echo/blob/99c395d846e079d4cffd29de9298d1dd829e8b04/echo.go#L828-L846) [context.go#L140-L156](https://github.com/labstack/echo/blob/99c395d846e079d4cffd29de9298d1dd829e8b04/context.go#L140-L156)
- Hypothesis: proxyRaw may retain hijacked connections when one io.Copy direction finishes but the other remains blocked, because it waits for both results before deferred connection closes run. The inspected WebSocket test verifies a normal echo exchange rather than this asymmetric-close scenario. (inferred) — [middleware/proxy.go#L145-L178](https://github.com/labstack/echo/blob/99c395d846e079d4cffd29de9298d1dd829e8b04/middleware/proxy.go#L145-L178) [middleware/proxy_test.go#L1081-L1118](https://github.com/labstack/echo/blob/99c395d846e079d4cffd29de9298d1dd829e8b04/middleware/proxy_test.go#L1081-L1118)
- Onboarding/tooling descriptions drift from implementation: CLAUDE.md lists separate method trees and bundled JWT middleware, README lists JWT externally, and Makefile uses an unversioned package prefix. These discrepancies can misdirect edits or checks; no Makefile failure was executed or confirmed. — [CLAUDE.md#L51-L59](https://github.com/labstack/echo/blob/99c395d846e079d4cffd29de9298d1dd829e8b04/CLAUDE.md#L51-L59) [router.go#L62-L75](https://github.com/labstack/echo/blob/99c395d846e079d4cffd29de9298d1dd829e8b04/router.go#L62-L75) [README.md#L121-L129](https://github.com/labstack/echo/blob/99c395d846e079d4cffd29de9298d1dd829e8b04/README.md#L121-L129) [Makefile#L1-L2](https://github.com/labstack/echo/blob/99c395d846e079d4cffd29de9298d1dd829e8b04/Makefile#L1-L2) [go.mod#L1-L3](https://github.com/labstack/echo/blob/99c395d846e079d4cffd29de9298d1dd829e8b04/go.mod#L1-L3)

Every claim links to its cited files at the pinned commit. Citations identify source evidence; they do not establish runtime correctness. Independent CodeVetter scan — not reviewed by upstream maintainers.

## Public product links

- [CodeVetter](https://codevetter.com/)
- [Download](https://codevetter.com/download)
- [Documentation](https://codevetter.com/docs/)
- [Source](https://github.com/Codevetter/codevetter)
