Analyse: Eingaben und Liste robuster
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
f4c8b291d7
commit
fc937b0d1e
3 files changed
+34
-4
No files matched your search
@@ -70,7 +70,15 @@ router.get('/', requireSession, async (req, res) => {
|
||||
await runActivation({ customerId }).catch((err) => console.error('[funktionen] Freischalten fehlgeschlagen:', err.message))
|
||||
await backfillInvoiceLinks(customerId)
|
||||
const projects = await listCustomerFeatures(customerId)
|
||||
for (const p of projects) p.analyse = await analyseView(customerId, p.projectId)
|
||||
// Ein Fehler bei einem Projekt (z. B. Abo-Pruefung) darf nicht die ganze Liste kosten
|
||||
await Promise.all(
|
||||
projects.map(async (p) => {
|
||||
p.analyse = await analyseView(customerId, p.projectId).catch((err) => {
|
||||
console.error('[funktionen] Analyse-Zustand fehlgeschlagen:', err.message)
|
||||
return { state: 'unknown' }
|
||||
})
|
||||
}),
|
||||
)
|
||||
res.json({
|
||||
projects,
|
||||
setupEstimate: config.funktionen.setupEstimate,
|
||||
@@ -217,7 +225,7 @@ router.get('/analyse/stats', analyseGuard, async (req, res) => {
|
||||
router.post('/analyse/heatmap-link', analyseGuard, async (req, res) => {
|
||||
try {
|
||||
const { projectId, path, vp } = req.body || {}
|
||||
if (typeof path !== 'string' || !path.startsWith('/') || path.length > 300 || /[?#]/.test(path)) {
|
||||
if (typeof path !== 'string' || !path.startsWith('/') || path.length > 300 || /[?#\\\s\u0000-\u001f\u007f]/.test(path)) {
|
||||
return res.status(400).json({ error: 'Ungültiger Pfad' })
|
||||
}
|
||||
if (!['m', 't', 'd'].includes(vp)) return res.status(400).json({ error: 'Ungültige Ansicht' })
|
||||
@@ -226,6 +234,7 @@ router.post('/analyse/heatmap-link', analyseGuard, async (req, res) => {
|
||||
return res.status(409).json({ error: 'Die Website-Analyse ist für dieses Projekt nicht aktiv.' })
|
||||
}
|
||||
const domains = analyseDomains(project)
|
||||
if (!domains.length) return res.status(409).json({ error: 'Keine gültige Domain' })
|
||||
const host = project.liveDomain ? domains[domains.length - 1] : domains[0]
|
||||
const token = analyseDeps.client.heatToken({ siteKey: doc.siteKey, path, vp })
|
||||
res.json({ url: `https://${host}${path}?__wk_heat=${encodeURIComponent(token)}` })
|
||||
|
||||
@@ -56,7 +56,9 @@ export async function analyseEligibility(customerId, projectId) {
|
||||
return { ok: true, project }
|
||||
}
|
||||
|
||||
/** `<subdomain>.project.webklar.com` plus Live-Domain (klein, ohne Schema/Pfad), ohne Doppelte. */
|
||||
const HOSTNAME_RE = /^(?=.{4,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z][a-z0-9-]{0,61}[a-z0-9]$/
|
||||
|
||||
/** `<subdomain>.project.webklar.com` plus Live-Domain (klein, ohne Schema/Pfad), ohne Doppelte; ungueltige Hosts werden uebersprungen. */
|
||||
export function analyseDomains(project) {
|
||||
const domains = []
|
||||
if (project.subdomain) domains.push(`${String(project.subdomain).trim().toLowerCase()}.project.webklar.com`)
|
||||
@@ -66,7 +68,7 @@ export function analyseDomains(project) {
|
||||
.replace(/^[a-z]+:\/\//, '')
|
||||
.replace(/[/?#].*$/, '')
|
||||
if (live) domains.push(live)
|
||||
return [...new Set(domains)]
|
||||
return [...new Set(domains)].filter((d) => HOSTNAME_RE.test(d))
|
||||
}
|
||||
|
||||
async function customerOf(customerId) {
|
||||
|
||||
@@ -218,3 +218,22 @@ test('Fix b: Abschalten durch Mitarbeiter meldet Sammeldienst active:false', asy
|
||||
an.analyseDeps.client.putSite = real
|
||||
assert.equal(again.state, 'disabled')
|
||||
})
|
||||
|
||||
test('Härtung: ungültige Hosts, Pfadzeichen, Fehler bei einem Projekt', async () => {
|
||||
// analyseDomains: ungueltige Hosts werden uebersprungen
|
||||
assert.deepEqual(an.analyseDomains({ subdomain: 'cafe', liveDomain: 'https://Cafe.DE/x' }), ['cafe.project.webklar.com', 'cafe.de'])
|
||||
assert.deepEqual(an.analyseDomains({ subdomain: 'cafe', liveDomain: 'evil.de@x y' }), ['cafe.project.webklar.com'])
|
||||
assert.deepEqual(an.analyseDomains({ subdomain: 'ca fe', liveDomain: 'a_b.de' }), [])
|
||||
assert.deepEqual(an.analyseDomains({ subdomain: 'cafe', liveDomain: 'localhost' }), ['cafe.project.webklar.com'])
|
||||
// Heatmap-Pfad: Steuerzeichen, Backslash, Leerraum => 400; dekodierte Form bleibt erlaubt
|
||||
await post(`${A}/activate`, { projectId: 'p1', consent: consent() }, { cookie })
|
||||
const link = (path) => post(`${A}/heatmap-link`, { projectId: 'p1', path, vp: 'd' }, { cookie })
|
||||
for (const path of ['/a\\b', '/a b', '/a\nb', '/a\u0000b', '/a\tb', '/a\u007fb']) assert.equal((await link(path)).status, 400, JSON.stringify(path))
|
||||
assert.equal((await link('/über-uns')).status, 200)
|
||||
// keine gueltige Domain => 409
|
||||
aw.seed('websiteProjects', { $id: 'p7', customerId: 'c1', projectName: 'Kaputt', subdomain: 'ka putt', liveDomain: 'x_y', isPublic: true })
|
||||
await post(`${A}/activate`, { projectId: 'p7', consent: consent() }, { cookie })
|
||||
const bad = await post(`${A}/heatmap-link`, { projectId: 'p7', path: '/', vp: 'd' }, { cookie })
|
||||
assert.equal(bad.status, 409)
|
||||
assert.equal(bad.body.error, 'Keine gültige Domain')
|
||||
})
|
||||
Reference in new issue
Block a user