Build a secret's link from the site's URL, not the browser's
Reported from production: clicking a secret link gave an IIS 404, and the link read /s/<token> where the site lives at /shopdb/s/<token>. The link was assembled from window.location.origin, which is scheme and host only. site_base_url already exists for this and says so in its own description - "used for QR codes and any absolute link the app emits" - with getSiteBaseUrl() as the helper the QR pages use. This reached past all of it. Both halves were wrong, and the second is worse because it fails silently: MOUNT PATH. A site served under /shopdb or /ops carries that in front of every route. Leaving it off is the reported 404. HOST. Whoever creates a secret may have reached the app by an IP, a short hostname, or a name that resolves in one VLAN, and the recipient opens the link from somewhere else entirely. The configured site URL is the one address known to work for both of them. The shape of this failure is what makes it worth spelling out: the secret was created SUCCESSFULLY and the sender was handed a link that could never work, so the failure landed on the recipient, the sender had no reason to doubt it, and the secret expired unread. A 500 would have been kinder. Falls back to the browsing origin when a site has not set site_base_url, which is what every other absolute link here does. BASE_URL is injected at build time, so the subpath half of this cannot show up in dev and only appears on a subpath deployment.
This commit is contained in:
@@ -91,6 +91,8 @@
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import { toolsApi } from '../api'
|
||||
import { withBase } from '@/utils/basePath'
|
||||
import { getSiteBaseUrl } from '@/utils/siteSettings'
|
||||
import {
|
||||
sealSecret, refuseReason, pasteCarriesFiles, cryptoAvailable, MAX_PLAINTEXT,
|
||||
} from '../securesend'
|
||||
@@ -137,9 +139,24 @@ async function createLink() {
|
||||
views: views.value,
|
||||
})
|
||||
const token = response.data.data.token
|
||||
|
||||
// THE SITE'S CONFIGURED URL, not the one this browser happens to be using.
|
||||
// site_base_url exists for exactly this - "used for QR codes and any
|
||||
// absolute link the app emits" - because somebody creating a secret may
|
||||
// have reached the app by an IP, a short hostname, or a name that only
|
||||
// resolves inside one VLAN, and the recipient has to open the link from
|
||||
// wherever they are. It falls back to the browsing origin when a site has
|
||||
// not set one.
|
||||
//
|
||||
// withBase for the path: a site served under /shopdb or /ops carries that
|
||||
// in front of every route, and leaving it off produced a 404 from IIS for a
|
||||
// secret that existed - with the sender shown a link that could never work.
|
||||
const origin = (await getSiteBaseUrl()).replace(/\/+$/, '')
|
||||
const path = withBase(`/s/${token}`)
|
||||
|
||||
// The key rides in the FRAGMENT. Browsers never send it, so it reaches the
|
||||
// recipient and nowhere else.
|
||||
link.value = `${window.location.origin}/s/${token}#${sealed.fragmentKey}`
|
||||
link.value = `${origin}${path}#${sealed.fragmentKey}`
|
||||
// Drop the plaintext as soon as it is sealed: no reason to keep it in memory
|
||||
// behind the link that replaced it.
|
||||
plaintext.value = ''
|
||||
|
||||
Reference in New Issue
Block a user