[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

Copyright © Eclipse Foundation AISBL. All rights reserved.     Privacy Policy | Terms of Use | Copyright Agent