Normalize only loopback authorities in embedded app runtime URLs
This commit is contained in:
@@ -862,3 +862,21 @@ User reports an HTTPS-only embedded app gate while tab mode works. Added task17,
|
|||||||
with exact node/app clarification pending. No authentication bypass or live
|
with exact node/app clarification pending. No authentication bypass or live
|
||||||
nginx modification. The private operator report is updated, regenerated and
|
nginx modification. The private operator report is updated, regenerated and
|
||||||
checked at390/1440px; SCP path remains unchanged.
|
checked at390/1440px; SCP path remains unchanged.
|
||||||
|
|
||||||
|
### HTTPS iframe investigation: bounded findings
|
||||||
|
|
||||||
|
A real HTTPS parent on dev with an existing session successfully loaded File
|
||||||
|
Browser in an iframe (200, no gate). Certificate trust was ignored only inside
|
||||||
|
the disposable diagnostic context; this does not qualify normal browser trust.
|
||||||
|
Yaya's File Browser HTTPS port resets the connection in both curl and browser.
|
||||||
|
The exact operator URL/app is still needed to reproduce the reported gate.
|
||||||
|
Initial intercepted-parent probes were blocked by Chromium local-network access
|
||||||
|
checks; the corrected probe used the actual parent origin. Logs:
|
||||||
|
`/tmp/archy-https-frame-probe-2.log` and `...-3.log`. No gate/TLS settings changed.
|
||||||
|
|
||||||
|
Separate regression tests reproduced embedded runtime-URL handling errors:
|
||||||
|
127.0.0.1/IPv6 loopback were not translated to the dashboard host, while a loose
|
||||||
|
localhost replacement could alter external hostnames or paths. Exact authority
|
||||||
|
matching fixes these errors. The 22 focused launch/stable-URL/loader tests pass
|
||||||
|
after two new failures were reproduced. This is not claimed as the live gate
|
||||||
|
incident's cause. Production UI build passes (`/tmp/archy-https-runtime-ui-build.log`). This change is not yet deployed.
|
||||||
|
|||||||
@@ -107,6 +107,22 @@ describe('appSessionConfig', () => {
|
|||||||
expect(resolveAppUrl('filebrowser', undefined, 'http://localhost:8083')).toBe('http://192.0.2.10:8083')
|
expect(resolveAppUrl('filebrowser', undefined, 'http://localhost:8083')).toBe('http://192.0.2.10:8083')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('keeps loopback runtime addresses on the dashboard host for embedded apps', () => {
|
||||||
|
stubLocation({ hostname: 'node.example', protocol: 'https:' })
|
||||||
|
for (const host of ['localhost', '127.0.0.1', '[::1]']) {
|
||||||
|
expect(resolveAppUrl('filebrowser', undefined, `http://${host}:8083/files?view=grid#recent`))
|
||||||
|
.toBe('https://node.example:8083/files?view=grid#recent')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it('does not replace localhost text inside an external hostname or path', () => {
|
||||||
|
stubLocation({ hostname: 'node.example', protocol: 'http:' })
|
||||||
|
expect(resolveAppUrl('filebrowser', undefined, 'http://localhost.example:8083/localhost'))
|
||||||
|
.toBe('http://localhost.example:8083/localhost')
|
||||||
|
expect(resolveAppUrl('filebrowser', undefined, 'http://media.example:8083/localhost'))
|
||||||
|
.toBe('http://media.example:8083/localhost')
|
||||||
|
})
|
||||||
|
|
||||||
// The direct-port launch path (new-tab apps on desktop, the companion's
|
// The direct-port launch path (new-tab apps on desktop, the companion's
|
||||||
// native WebView on phones) used to hardcode http:// — so a node reached
|
// native WebView on phones) used to hardcode http:// — so a node reached
|
||||||
// over HTTPS opened Vaultwarden and friends in cleartext. It must follow
|
// over HTTPS opened Vaultwarden and friends in cleartext. It must follow
|
||||||
|
|||||||
@@ -172,7 +172,12 @@ export function resolveAppUrl(id: string, routeQueryPath?: string, runtimeUrl?:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (runtimeUrl && id !== 'netbird') {
|
if (runtimeUrl && id !== 'netbird') {
|
||||||
let base = runtimeUrl.replace(/localhost/i, window.location.hostname)
|
// Only rewrite a loopback authority. A substring replacement also changes
|
||||||
|
// external hostnames and paths, while leaving 127.0.0.1 pointed at the viewer.
|
||||||
|
let base = runtimeUrl.replace(
|
||||||
|
/^(https?:\/\/)(localhost|127\.0\.0\.1|\[::1\])(?=[:/?#]|$)/i,
|
||||||
|
(_match, scheme: string) => `${scheme}${window.location.hostname}`,
|
||||||
|
)
|
||||||
// The backend reports runtime URLs as http:// because that is how the app
|
// The backend reports runtime URLs as http:// because that is how the app
|
||||||
// binds locally. On an HTTPS dashboard that is mixed content and the
|
// binds locally. On an HTTPS dashboard that is mixed content and the
|
||||||
// frame is blocked outright — but ONLY upgrade when the gate fronts the
|
// frame is blocked outright — but ONLY upgrade when the gate fronts the
|
||||||
|
|||||||
Reference in New Issue
Block a user