Buchungskalender: öffentliche API gegen manipulierte IDs und Lookups absichern
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
035967f8eb
commit
0c5ca76a4d
3 files changed
+73
-7
No files matched your search
@@ -114,9 +114,14 @@ test('Belegung: fasst angrenzende Bereiche zusammen, ohne Quelle, auf das Fenste
|
||||
})
|
||||
|
||||
test('Belegung: ueber 18 Monate hinaus, kaputte Daten, fremde oder inaktive Unterkunft', async () => {
|
||||
assert.equal((await send('GET', `${BASE}/belegung?unterkunft=u1&von=2026-11-01&bis=2028-05-02`)).status, 400)
|
||||
aw.seed('ukSperren', { $id: 'sz', unterkunftId: 'u1', featureId: FEATURE, von: '2028-04-28', bis: '2028-06-10', quelle: 'manuell' })
|
||||
const geklemmt = await send('GET', `${BASE}/belegung?unterkunft=u1&von=2028-04-01&bis=2030-01-01`)
|
||||
assert.equal(geklemmt.status, 200)
|
||||
assert.equal(geklemmt.body.bisMax, '2028-05-01')
|
||||
assert.deepEqual(geklemmt.body.belegt, [{ von: '2028-04-28', bis: '2028-05-01' }])
|
||||
assert.equal((await send('GET', `${BASE}/belegung?unterkunft=u1&von=2026-11-01&bis=2028-05-02`)).status, 200)
|
||||
assert.equal((await send('GET', `${BASE}/belegung?unterkunft=u1&von=2026-11-05&bis=2026-11-04`)).status, 400)
|
||||
assert.equal((await send('GET', `${BASE}/belegung?unterkunft=u1&von=2026-11-01&bis=2028-05-01`)).status, 200)
|
||||
assert.equal((await send('GET', `${BASE}/belegung?unterkunft=u1&von=2026-11-05&bis=2026-11-05`)).status, 400)
|
||||
assert.equal((await send('GET', `${BASE}/belegung?unterkunft=u1&von=morgen&bis=2026-11-05`)).status, 400)
|
||||
assert.equal((await send('GET', `${BASE}/belegung?von=2026-11-01&bis=2026-11-05`)).status, 400)
|
||||
assert.equal((await send('GET', `${BASE}/belegung?unterkunft=u3&von=2026-11-01&bis=2026-11-05`)).status, 404)
|
||||
@@ -200,3 +205,58 @@ test('CORS: Preflight erlaubt/fremd, POST von fremder oder fehlender Origin -> 4
|
||||
assert.equal(unbekannt.status, 204)
|
||||
assert.equal(unbekannt.headers.get('access-control-allow-origin'), '*')
|
||||
})
|
||||
|
||||
test('Fix: manipulierte Unterkunfts-IDs -> 400 (Belegung, Anfrage); Speicher fragt nicht nach', async () => {
|
||||
for (const id of ['../x', 'a?b', 'a%23b', 'x'.repeat(37)]) {
|
||||
const r = await send('GET', `${BASE}/belegung?unterkunft=${encodeURIComponent(id)}&von=2026-11-01&bis=2026-11-05`)
|
||||
assert.equal(r.status, 400, id)
|
||||
assert.ok(r.body.felder.unterkunft)
|
||||
const p = await post(anfrage({ unterkunft: id }))
|
||||
assert.equal(p.status, 400, id)
|
||||
assert.ok(p.body.felder.unterkunft)
|
||||
}
|
||||
assert.equal(aw.docs('ukAnfragen').size, 0)
|
||||
const sp = await import('../server/services/unterkunft/speicher.js')
|
||||
const echtesFetch = globalThis.fetch
|
||||
let aufrufe = 0
|
||||
globalThis.fetch = (...a) => (aufrufe++, echtesFetch(...a))
|
||||
try {
|
||||
for (const id of ['../x', 'a?b', '', 'x'.repeat(37), 5, null]) assert.equal(await sp.ladeUnterkunft(id), null)
|
||||
assert.equal(await sp.ladeAnfrage('../../x'), null)
|
||||
} finally {
|
||||
globalThis.fetch = echtesFetch
|
||||
}
|
||||
assert.equal(aufrufe, 0)
|
||||
})
|
||||
|
||||
test('Fix: POST prueft die IP-Grenze vor dem siteKey-Lookup', async () => {
|
||||
const echtesFetch = globalThis.fetch
|
||||
let aufrufe = 0
|
||||
globalThis.fetch = (u, ...a) => (String(u?.url ?? u).startsWith(aw.url) && aufrufe++, echtesFetch(u, ...a))
|
||||
try {
|
||||
const stati = []
|
||||
for (let i = 0; i < 125; i++) {
|
||||
const key = ('a'.repeat(15) + String.fromCharCode(97 + (i % 26))).slice(0, 16)
|
||||
stati.push((await send('POST', `/api/public/unterkunft/v1/${key}/anfrage`, { body: anfrage(), headers: { origin: ORIGIN } })).status)
|
||||
}
|
||||
assert.ok(aufrufe > 0, 'unter der Grenze wird nachgeschlagen')
|
||||
assert.equal(stati[119], 200)
|
||||
assert.deepEqual(stati.slice(120), [429, 429, 429, 429, 429])
|
||||
const nachher = aufrufe
|
||||
await send('POST', `${BASE}/anfrage`, { body: anfrage(), headers: { origin: ORIGIN } })
|
||||
assert.equal(aufrufe, nachher, 'ueber der IP-Grenze keine Lookups mehr')
|
||||
} finally {
|
||||
globalThis.fetch = echtesFetch
|
||||
}
|
||||
})
|
||||
|
||||
test('Fix: Pilotmodus mit fremdem Besitzer -> {aktiv:false}; zu grosser Rumpf -> 413', async () => {
|
||||
config.unterkunft.mode = 'pilot'
|
||||
config.unterkunft.pilotEmails = ['andere@example.com']
|
||||
K.leereCache()
|
||||
assert.deepEqual((await send('GET', BASE)).body, { aktiv: false })
|
||||
config.unterkunft.pilotEmails = ['mimi@example.com']
|
||||
assert.equal((await send('GET', BASE)).body.aktiv, true)
|
||||
const gross = await post(anfrage({ nachricht: 'x'.repeat(20000) }))
|
||||
assert.equal(gross.status, 413)
|
||||
})
|
||||
Reference in new issue
Block a user