Skip to content

App: allow native deep links from iOS widget#31242

Merged
andig merged 1 commit into
masterfrom
feat/app_navigate
Jun 26, 2026
Merged

App: allow native deep links from iOS widget#31242
andig merged 1 commit into
masterfrom
feat/app_navigate

Conversation

@naltatis

@naltatis naltatis commented Jun 26, 2026

Copy link
Copy Markdown
Member

Companion web change for evcc-io/app#212. Lets the native app drive web UI navigation (iOS widget deep links).

Native posts a JSON message over the webview bridge:

{ "type": "navigate", "path": "/forecast" }

@naltatis naltatis added enhancement New feature or request ux User experience/ interface labels Jun 26, 2026
@naltatis naltatis requested a review from Maschga June 26, 2026 13:59

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • Consider guarding receiveFromApp (and the message handler) against router being undefined or not yet initialized to avoid potential runtime errors if messages arrive before appDetection is called.
  • The message event handlers currently parse any incoming event data; you might want to add basic validation (e.g., checking event.source, message.type, message.path format) before calling router.push to reduce the risk of malformed or unwanted navigation requests.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Consider guarding `receiveFromApp` (and the message handler) against `router` being undefined or not yet initialized to avoid potential runtime errors if messages arrive before `appDetection` is called.
- The `message` event handlers currently parse any incoming event data; you might want to add basic validation (e.g., checking `event.source`, `message.type`, `message.path` format) before calling `router.push` to reduce the risk of malformed or unwanted navigation requests.

## Individual Comments

### Comment 1
<location path="assets/js/utils/native.ts" line_range="21-24" />
<code_context>
+  if (!isApp()) return;
+  document.querySelector("html")?.classList.add("app");
+
+  const handler = (event: Event) => {
+    try {
+      receiveFromApp(JSON.parse((event as MessageEvent).data));
+    } catch {
+      return;
+    }
+  };
+  window.addEventListener("message", handler); // iOS
+  document.addEventListener("message", handler); // Android
+}
</code_context>
<issue_to_address>
**🚨 suggestion (security):** Tighten message event handling with proper typing and origin/shape checks for security and robustness.

The handler assumes every `Event` is a `MessageEvent` and parses `event.data` as JSON without validating its type or origin, so unrelated messages (e.g. from other iframes/extensions) could influence navigation.

I recommend:
- Typing the handler as `MessageEvent` instead of casting.
- Guarding with `if (typeof event.data !== "string") return;` before `JSON.parse`.
- Optionally checking `event.origin` for `window` messages so only trusted senders can trigger navigation.

This preserves current behavior while making the handler safer against unexpected inputs.

```suggestion
export function appDetection(r: Router) {
  router = r;
  if (!isApp()) return;
  document.querySelector("html")?.classList.add("app");

  const handler = (event: MessageEvent) => {
    if (typeof event.data !== "string") {
      return;
    }

    let parsed: unknown;
    try {
      parsed = JSON.parse(event.data);
    } catch {
      return;
    }

    // Only accept messages with the expected shape
    if (!parsed || typeof parsed !== "object") {
      return;
    }

    const message = parsed as Partial<FromAppMessage>;

    if (message.type !== "navigate" || typeof message.path !== "string") {
      return;
    }

    // For window messages, only accept from our own origin (or "null" which is
    // typical for native webviews)
    const isWindowMessage = event.source === window;
    if (
      isWindowMessage &&
      event.origin !== window.location.origin &&
      event.origin !== "null"
    ) {
      return;
    }

    receiveFromApp(message as FromAppMessage);
  };

  window.addEventListener("message", handler); // iOS
  document.addEventListener("message", handler as unknown as EventListener); // Android
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread assets/js/utils/native.ts
@naltatis naltatis changed the title App: navigate web UI from native deep links App: allow native deep links from iOS widget Jun 26, 2026
@andig andig merged commit 51419e2 into master Jun 26, 2026
39 checks passed
@andig andig deleted the feat/app_navigate branch June 26, 2026 16:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request ux User experience/ interface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants