Skip to content

Commit c563348

Browse files
committed
Add useUrlHelper composable and use it for safe origin handling and comparison
1 parent 0d4cf33 commit c563348

3 files changed

Lines changed: 40 additions & 5 deletions

File tree

frontend/src/components/immersive-editor/HostedInstanceEditorWrapper.vue

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import { mapActions, mapState } from 'pinia'
2929
3030
import LoadingScreenWrapper from './LoadingScreenWrapper.vue'
3131
32+
import { useUrlHelper } from '@/composables/UrlHelper'
3233
import { useProductAssistantStore } from '@/stores/product-assistant.js'
3334
import { useThemeStore } from '@/stores/theme'
3435
import { isInstanceOnNR5Plus } from '@/utils/instanceVersion'
@@ -148,7 +149,9 @@ export default {
148149
}),
149150
// todo this event listener should be moved in the messaging.service.js
150151
eventListener (event) {
151-
if (event.origin === this.instance.url) {
152+
const { originsMatch } = useUrlHelper()
153+
154+
if (originsMatch(event.origin, this.instance.url)) {
152155
// Forward iframe activity to the parent page so PostHog's idle timer
153156
// is reset when the user is active inside the cross-origin iframe.
154157
window.dispatchEvent(new MouseEvent('mousemove'))
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
export function useUrlHelper () {
2+
/**
3+
* Safely extracts the bare origin (scheme://host:port) from a URL string.
4+
* Returns null if the URL cannot be parsed.
5+
*/
6+
const safeOrigin = (url: string): string | null => {
7+
try {
8+
return new URL(url).origin
9+
} catch {
10+
return null
11+
}
12+
}
13+
14+
/**
15+
* Compares two URLs (or origins) by their normalised origin.
16+
* Returns false if either URL cannot be parsed.
17+
*/
18+
const originsMatch = (a: string, b: string): boolean => {
19+
const originA = safeOrigin(a)
20+
const originB = safeOrigin(b)
21+
if (!originA || !originB) {
22+
return false
23+
}
24+
return originA === originB
25+
}
26+
27+
return {
28+
safeOrigin,
29+
originsMatch
30+
}
31+
}

frontend/src/stores/product-assistant.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { defineStore } from 'pinia'
22
import SemVer from 'semver'
33

4+
import { useUrlHelper } from '@/composables/UrlHelper'
45
import getAppOrchestrator from '@/services/app.orchestrator'
56
import { useContextStore } from '@/stores/context.js'
67

@@ -302,11 +303,11 @@ export const useProductAssistantStore = defineStore('product-assistant', {
302303
// instance.url / device.editor.url can carry an editor path (httpAdminRoot) or a
303304
// trailing slash, but a MessageEvent's origin is always a bare scheme://host:port.
304305
// Reduce each entry to its origin so the comparison in handleMessage matches.
306+
const { safeOrigin } = useUrlHelper()
305307
const addOrigin = (url) => {
306-
try {
307-
allowedOrigins.add(new URL(url).origin)
308-
} catch {
309-
// not a parseable URL - ignore
308+
const origin = safeOrigin(url)
309+
if (origin) {
310+
allowedOrigins.add(origin)
310311
}
311312
}
312313
if (instance?.url) {

0 commit comments

Comments
 (0)