REMOVE_BASE_PATH strips every leading repetition of the base path, not just one
LowPublished Sep 25, 2026
## Summary `AWS_LWA_REMOVE_BASE_PATH` is documented as "the base path to be removed from request path" — singular. In affected versions the implementation removed **every** leading repetition of it, because it used `str::trim_start_matches` rather than `str::strip_prefix`. The match was also not anchored to a path-segment boundary, so a partial-segment prefix was stripped too. `src/lib.rs`, as of v1.0.1: ```rust let path = event.raw_http_path().to_string(); // caller-controlled request path let mut path = path.as_str(); ... if let Some(base_path) = self.base_path.as_deref() { let stripped = path.trim_start_matches(base_path); // removes ALL leading repetitions if stripped.len() != path.len() { /* debug log */ } path = stripped; } ``` `path` comes from `event.raw_http_path()`, so it is under the caller's control. The debug log compared only `stripped.len() != path.len()`, so a multi-strip was indistinguishable from a single strip in the logs. ## Behaviour With `AWS_LWA_REMOVE_BASE_PATH=/api`: | request path | affected versions | fixed (v1.1.0) | |---|---|---| | `/api/orders` | `/orders` | `/orders` | | `/api/api/admin` | **`/admin`** | `/api/admin` | | `/api/a...
Affected versions
| Package | Affected | Fixed in |
|---|---|---|
| aws-lambda-web-adapter Product | < 1.1.0 | 1.1.0 |
Details and references
## Summary `AWS_LWA_REMOVE_BASE_PATH` is documented as "the base path to be removed from request path" — singular. In affected versions the implementation removed **every** leading repetition of it, because it used `str::trim_start_matches` rather than `str::strip_prefix`. The match was also not anchored to a path-segment boundary, so a partial-segment prefix was stripped too. `src/lib.rs`, as of v1.0.1: ```rust let path = event.raw_http_path().to_string(); // caller-controlled request path let mut path = path.as_str(); ... if let Some(base_path) = self.base_path.as_deref() { let stripped = path.trim_start_matches(base_path); // removes ALL leading repetitions if stripped.len() != path.len() { /* debug log */ } path = stripped; } ``` `path` comes from `event.raw_http_path()`, so it is under the caller's control. The debug log compared only `stripped.len() != path.len()`, so a multi-strip was indistinguishable from a single strip in the logs. ## Behaviour With `AWS_LWA_REMOVE_BASE_PATH=/api`: | request path | affected versions | fixed (v1.1.0) | |---|---|---| | `/api/orders` | `/orders` | `/orders` | | `/api/api/admin` | **`/admin`** | `/api/admin` | | `/api/api/api/internal` | **`/internal`** | `/api/api/internal` | | `/apiorder` | **`order`** | `/apiorder` | ## Impact The common deployment shape is an edge (API Gateway / ALB / CloudFront) that exposes only a prefix — `/api/*` — and forwards to a Lambda running the adapter, which strips that prefix before handing the request to the app. Under that shape an operator may assume the app's routes outside the prefix are unreachable from the internet. A request for `/api/api/admin` satisfies the edge's `/api/*` rule, and the adapter then removed the prefix twice, so the application received `/admin`. With the documented single-strip behaviour it would have received `/api/admin`. Whether that reaches anything sensitive depends entirely on the application's routing, so this is not a universal-impact issue and no bypass exists inside the adapter itself. The defect is that the adapter did not do what its own documentation said, in a direction that only ever exposes *more* of the backend than intended, driven by the request path. Applications should not rely on an edge prefix rule as their only control over which routes are reachable. ## Patches Fixed in **v1.1.0**. The adapter now strips exactly one leading occurrence, only on a path-segment boundary, and normalizes a configured trailing slash first: ```rust if let Some(base_path) = self.base_path.as_deref() { let base_path = base_path.strip_suffix('/').unwrap_or(base_path); if let Some(rest) = path.strip_prefix(base_path) { if rest.is_empty() || rest.starts_with('/') { path = if rest.is_empty() { "/" } else { rest }; } } } ``` **Operators upgrading from 1.0.1 or earlier should verify their routes** if they relied on the old repeated or partial-segment stripping behaviour. This is a deliberate behaviour change and is also noted in the v1.1.0 release notes. ## Workarounds On affected versions, make the edge reject request paths that repeat the base path segment, or unset `AWS_LWA_REMOVE_BASE_PATH` and let the application serve its routes under the full prefix. ## Verification The reported behaviour was measured against the two Rust standard-library functions directly, not against a deployed Lambda + API Gateway stack. The fix is covered by a regression test in `src/lib.rs` (`test_base_path_strip_single_and_segment_aware`) pinning single-occurrence, segment-aware, exact-match and trailing-slash cases.
- Severity from
- GitHub (reviewed advisory)
- Weakness
- CWE-436
More AWS advisories
All AWS| Date | Advisory | Severity | Fixed in |
|---|---|---|---|
| Sep 24 | Type confusion in AWS pgcollection allows remote code execution | High8.8 | No fix yet |
| Sep 22 | Kiro IDE Insufficient File Write Restrictions to Execution-Sensitive Paths | High8.8 | No fix yet |
| Sep 22 | Amazon Aurora PostgreSQL a fully managed relational database engine that's compatible... | High8.0 | No fix yet |
| Sep 22 | HTTPS Fallback to HTTP in Graph Explorer | Medium5.9 | No fix yet |
| Sep 22 | Code Injection via Improper Triple-Quote Escaping in AgentCore CLI Bedrock Agent Import | Critical9.0 | No fix yet |
| Sep 22 | Issue with HTTP/2 multi-frame request body inspection in AWS WAF | Critical9.8 | No fix yet |