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:
1 parent
2ac7d3f64c
commit
eb7c44ac11
8 files changed
+26
-14
No files matched your search
@@ -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) {
|
||||
|
||||
@@ -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 }
|
||||
@@ -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' }))
|
||||
|
||||
@@ -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.')
|
||||
|
||||
@@ -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,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.'}`),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -2,7 +2,7 @@ import { test } from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import {
|
||||
fmtEuro, fmtDatum, fmtDatumZeit, fmtTagMonat, fmtStd, fmtMinuten, fmtBytes, legacyRequest, meldeZaehlerNeu, merkeHinweis, nimmHinweis,
|
||||
setzeChatVorgabe, nimmChatVorgabe, fmtUhrzeit, berlinTag, ersetze, wechselFunktionAus, zuAnsicht, meldeTitel, K, ABO_NICHT_GELADEN, navigiere, ladeAbo, safeUrl,
|
||||
setzeChatVorgabe, nimmChatVorgabe, fmtUhrzeit, berlinTag, ersetze, sichereAdresse, wechselFunktionAus, zuAnsicht, meldeTitel, K, ABO_NICHT_GELADEN, navigiere, ladeAbo, safeUrl,
|
||||
} from '../public/neu/ansicht-hilfen.js'
|
||||
import { mit, mitDom, abo, aboAntwort, me, projekt, rechnung, nachricht, angebot, ledger, anfrageGutschrift, VORSCHAU, VORSCHAU_SPEICHER, LIMITS, neueWurzel } from './support/knDaten.js'
|
||||
|
||||
@@ -97,3 +97,8 @@ test('mit(): unbekannte Pfade antworten 404, Funktionen liefern Status und Body,
|
||||
const c = await fetch('/api/z'); assert.equal(c.status, 404); assert.deepEqual(await c.json(), { error: 'unbekannt' })
|
||||
const ev = new CustomEvent('x', { detail: 1 }); assert.equal(ev.detail, 1); ev.target = 'frei'; assert.equal(ev.target, 'frei')
|
||||
}))
|
||||
|
||||
test('sichereAdresse: http(s) und Pfade auf derselben Herkunft, sonst leer', () => {
|
||||
assert.equal(sichereAdresse('https://a.de/x'), 'https://a.de/x'); assert.equal(sichereAdresse('/vorschau-hinweis.html'), '/vorschau-hinweis.html')
|
||||
for (const v of ['javascript:alert(1)', 'data:text/html,x', '//evil.example/x', '/\\evil.example/x', 'vorschau.html', '', null, undefined, 5]) assert.equal(sichereAdresse(v), '', String(v))
|
||||
})
|
||||
Reference in new issue
Block a user