Kundenansicht neu P2: Adressen auf derselben Herkunft zulassen (Vorschau, Zahlungsseiten)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Sonnet 5.5 committed 2026-10-16 17:24:27 +00:00
1 parent 2ac7d3f64c
commit eb7c44ac11
8 files changed
+26 -14

No files matched your search

+2 -2
View File
@@ -5,7 +5,7 @@
import { api } from './api.js'
import { h, leeren } from './dom.js'
import { K, ladeAbo, safeUrl, meldeTitel, fmtEuro, fmtDatum, ABO_NICHT_GELADEN } from './ansicht-hilfen.js'
import { K, ladeAbo, sichereAdresse, meldeTitel, fmtEuro, fmtDatum, ABO_NICHT_GELADEN } from './ansicht-hilfen.js'
const laeufe = new WeakMap() // Wurzel → Nummer des jüngsten Aufrufs
const BEDINGUNGEN = '/abo-bedingungen.html'
@@ -101,7 +101,7 @@ function bestellung(daten, productKey, viewAs) {
method: 'POST',
body: { productKey, acceptTerms, startChoice, termsVersion: daten.termsVersion, offerId: offer?.id || '', lateStartAt: daten.lateStartAt || '' },
})
const ziel = safeUrl(url)
const ziel = sichereAdresse(url)
if (!ziel) throw new Error('Die Zahlungsseite konnte nicht geöffnet werden. Bitte später noch einmal versuchen.')
window.location.href = ziel
} catch (f) {
+7
View File
@@ -181,4 +181,11 @@ export function wechselFunktionAus(sub) {
return { key: f.key, projectId: f.projectId }
}
/** Adressen aus Daten (previewUrl, portalUrl, paymentUrl): http(s) wie safeUrl, dazu Pfade auf derselben Herkunft ("/vorschau-hinweis.html").
* Nicht zugelassen: javascript:, data:, "//host" und "/\\host" (Browser lesen den Rückstrich wie einen Schrägstrich). Sonst ''. */
export function sichereAdresse(url) {
const text = typeof url === 'string' ? url : ''
return safeUrl(text) || (/^\/(?![/\\])/.test(text) ? text : '')
}
export { safeUrl }
+2 -2
View File
@@ -3,7 +3,7 @@
import { api } from './api.js'
import { h, leeren } from './dom.js'
import { K, ersetze, zuAnsicht, meldeTitel, fmtDatumZeit, safeUrl } from './ansicht-hilfen.js'
import { K, ersetze, zuAnsicht, meldeTitel, fmtDatumZeit, sichereAdresse } from './ansicht-hilfen.js'
import { baueAktivitaet, baueProjektDetails, ladeProjektDetails } from './website-details.js'
import { ladeOffeneRechnungen, rechnungOffenZeile } from './rechnung-offen.js'
import { starteInstallQuelle, installAngebot, baueInstallHinweis, istAusgeblendet, merkeAusgeblendet } from './install-hinweis.js'
@@ -47,7 +47,7 @@ const fakt = (titel, wert) => h('div', null, h('dt', null, titel), h('dd', null,
function projektZeile(projekt) {
const bereit = vorschauBereit(projekt)
const url = safeUrl(projekt.previewUrl)
const url = sichereAdresse(projekt.previewUrl)
const status = projektStatus(projekt)
const rahmen = bereit && url
? h('div', { class: 'kn-vorschau', 'data-vorschau': true }, h('iframe', { src: url, loading: 'lazy', scrolling: 'no', tabindex: '-1', 'aria-hidden': 'true', title: 'Vorschau deiner Website' }))
+2 -2
View File
@@ -5,7 +5,7 @@
import { api } from './api.js'
import { h, leeren } from './dom.js'
import { K, ladeAbo, wechselFunktionAus, safeUrl, merkeHinweis, navigiere, meldeTitel, meldeZaehlerNeu, zuAnsicht, fmtEuro, fmtDatum, berlinTag, ABO_NICHT_GELADEN } from './ansicht-hilfen.js'
import { K, ladeAbo, wechselFunktionAus, sichereAdresse, merkeHinweis, navigiere, meldeTitel, meldeZaehlerNeu, zuAnsicht, fmtEuro, fmtDatum, berlinTag, ABO_NICHT_GELADEN } from './ansicht-hilfen.js'
const SCHLUESSEL = 'kn-wechsel-funktion'
const BEDINGUNGEN = '/abo-bedingungen.html'
@@ -141,7 +141,7 @@ export async function zeigeWechsel(wurzel, { route, me, nav, daten } = {}) {
const res = await api('/api/abo/switch', { method: 'POST', body })
if (gesendet) vergissFunktion()
if (res.paymentUrl) {
const ziel = safeUrl(res.paymentUrl)
const ziel = sichereAdresse(res.paymentUrl)
if (!ziel) {
lauf.busy = false
zeigeFehler('Die Zahlungsseite konnte nicht geöffnet werden. Der Wechsel wartet auf die Zahlung; Näheres steht unter Verträge.')
+2 -2
View File
@@ -3,7 +3,7 @@
import { api } from './api.js'
import { h } from './dom.js'
import { fmtEuro, safeUrl, zuAnsicht } from './ansicht-hilfen.js'
import { fmtEuro, sichereAdresse, zuAnsicht } from './ansicht-hilfen.js'
/** Rechnungen mit offenem Betrag, in der Reihenfolge der Antwort von GET /api/invoices. */
export function offeneRechnungen(antwort) {
@@ -25,7 +25,7 @@ export function rechnungOffenZeile(offen) {
if (!Array.isArray(offen) || !offen.length) return null
const summe = Math.round(offen.reduce((s, r) => s + (Number(r.balance) || 0), 0) * 100) / 100
const einzel = offen.length === 1 ? offen[0] : null
const url = einzel ? safeUrl(einzel.portalUrl) : ''
const url = einzel ? sichereAdresse(einzel.portalUrl) : ''
const link = url
? h('a', { href: url, target: '_blank', rel: 'noopener noreferrer' }, 'Zahlen')
: h('a', zuAnsicht('invoices'), 'Zahlen')
+3 -3
View File
@@ -3,7 +3,7 @@
import { api } from './api.js'
import { h, leeren } from './dom.js'
import { K, zuAnsicht, fmtEuro, fmtDatum, fmtTagMonat, safeUrl } from './ansicht-hilfen.js'
import { K, zuAnsicht, fmtEuro, fmtDatum, fmtTagMonat, sichereAdresse } from './ansicht-hilfen.js'
const laeufe = new WeakMap() // Reiter-Inhalt → Nummer des jüngsten Aufrufs
const TEXT_UST = 'Gemäß § 19 UStG ohne Umsatzsteuer.'
@@ -61,7 +61,7 @@ function zahlungsKnopf(viewAs) {
fehler.hidden = true
try {
const { url } = await api('/api/abo/portal', { method: 'POST', body: {} })
const ziel = safeUrl(url)
const ziel = sichereAdresse(url)
if (!ziel) throw new Error('Die Zahlungsseite konnte nicht geöffnet werden. Bitte versuche es später noch einmal.')
window.location.href = ziel
} catch (f) {
@@ -77,7 +77,7 @@ function wechselKarte(daten, sub) {
if (sub.productKey !== 'hosting') return null
const wartung = produkt(daten, 'wartung')
if (sub.switchPending) {
const url = safeUrl(sub.switchPending.paymentUrl)
const url = sichereAdresse(sub.switchPending.paymentUrl)
return h('section', { class: 'kn-sektion kn-wechsel' },
h('h3', null, 'Dein Wechsel wartet auf die Zahlung'),
h('p', { class: 'kn-hinweis' }, `Dein Wechsel zum Wartungs-Abo ist erst abgeschlossen, wenn die Zahlung bestätigt ist.${url ? '' : ' Schreib uns gern im Chat, wenn du Hilfe brauchst.'}`),
+2 -2
View File
@@ -3,7 +3,7 @@
import { api } from './api.js'
import { h, leeren } from './dom.js'
import { K, legacyRequest, safeUrl, fmtEuro, fmtDatum } from './ansicht-hilfen.js'
import { K, legacyRequest, sichereAdresse, fmtEuro, fmtDatum } from './ansicht-hilfen.js'
import { initAuftraege } from '../auftrag-portal.js'
const TEXT_LEER = 'Es liegen noch keine Rechnungen vor.'
@@ -14,7 +14,7 @@ const pille = (r) => (r.overdue ? K.pille.rot : r.paid ? K.pille.gruen : r.cance
function rechnungsZeile(r) {
const offen = Number(r.balance) > 0
const url = safeUrl(r.portalUrl)
const url = sichereAdresse(r.portalUrl)
const links = [
url ? h('a', { class: 'kn-zeilenlink', href: url, target: '_blank', rel: 'noopener noreferrer' }, offen ? 'Zahlen' : 'Ansehen') : null,
r.hasPdf !== false ? h('a', { class: 'kn-zeilenlink', href: `/api/invoices/${encodeURIComponent(r.id)}/pdf` }, 'PDF') : null,