mirror of
https://github.com/djdevin/recflare.git
synced 2026-10-26 15:38:52 -07:00
[img] hardening
This commit is contained in:
@@ -482,7 +482,7 @@ export const avatarRoutes = new Hono<App>({ strict: false })
|
||||
'`avatar-item/<date>/<id>-thumb.png` and `avatar-item/<date>/<id>-design.png`; those ' +
|
||||
'keys are the `ThumbnailImageFilename` / `DesignFilename` on the row. Each file is ' +
|
||||
'identified by its bytes — the client posts them under `.bin` names — and must be a ' +
|
||||
'PNG, JPEG, GIF, WebP or BMP; the key’s extension and the stored content-type come ' +
|
||||
'PNG or JPEG; the key’s extension and the stored content-type come ' +
|
||||
'from that signature, and anything else is refused with a 400 before either file is ' +
|
||||
'written.\n\n' +
|
||||
'`Price` is capped at 1000 tokens (`MAX_PLAYER_ITEM_PRICE`); one above it, or below ' +
|
||||
|
||||
@@ -220,10 +220,9 @@ export const imageRoutes = new Hono<App>({ strict: false })
|
||||
'`savedImageType` of 4 (profile thumbnail) additionally becomes the account’s ' +
|
||||
'avatar, persisted on the account row.\n\n' +
|
||||
'The file is identified by its bytes, not by the part’s filename or content-type: ' +
|
||||
'it must be a PNG, JPEG, GIF, WebP or BMP, and the stored key’s extension and ' +
|
||||
'it must be a PNG or JPEG, and the stored key’s extension and ' +
|
||||
'content-type come from that signature. Anything else is refused with a 400 — the ' +
|
||||
'`img` worker serves the object back under its stored type, so an HTML body under ' +
|
||||
'an image name would otherwise be a page on the image origin.',
|
||||
'`img` worker serves only those two formats, so it would never display.',
|
||||
security: AUTHED,
|
||||
requestBody: form(UploadImageRequest, 'The image file plus its metadata'),
|
||||
responses: {
|
||||
|
||||
@@ -5017,14 +5017,10 @@ describe('custom avatar items', () => {
|
||||
|
||||
// Both uploads landed in the image bucket under those keys.
|
||||
const thumb = await env.IMAGES.get(body.Value.ThumbnailImageFilename as string)
|
||||
expect(new Uint8Array((await thumb!.arrayBuffer()) as ArrayBuffer)).toEqual(
|
||||
new Uint8Array([1, 2, 3])
|
||||
)
|
||||
expect(new Uint8Array((await thumb!.arrayBuffer()) as ArrayBuffer)).toEqual(PNG_BYTES)
|
||||
expect(thumb!.httpMetadata?.contentType).toBe('image/png')
|
||||
const design = await env.IMAGES.get(body.Value.DesignFilename as string)
|
||||
expect(new Uint8Array((await design!.arrayBuffer()) as ArrayBuffer)).toEqual(
|
||||
new Uint8Array([4, 5, 6])
|
||||
)
|
||||
expect(new Uint8Array((await design!.arrayBuffer()) as ArrayBuffer)).toEqual(PNG_BYTES)
|
||||
|
||||
// The row is the record as JSON, with the queried fields generated off it.
|
||||
const row = await env.DB.prepare(
|
||||
@@ -5384,14 +5380,14 @@ describe('custom avatar items', () => {
|
||||
expect(badThumbnail.status).toBe(400)
|
||||
expect(await badThumbnail.json()).toMatchObject({
|
||||
Success: false,
|
||||
Error: 'thumbnailImage is not a PNG, JPEG, GIF, WebP or BMP image',
|
||||
Error: 'thumbnailImage is not a PNG or JPEG image',
|
||||
})
|
||||
|
||||
const badDesign = await upload(PNG_BYTES, html)
|
||||
expect(badDesign.status).toBe(400)
|
||||
expect(await badDesign.json()).toMatchObject({
|
||||
Success: false,
|
||||
Error: 'design is not a PNG, JPEG, GIF, WebP or BMP image',
|
||||
Error: 'design is not a PNG or JPEG image',
|
||||
})
|
||||
|
||||
const row = await env.DB.prepare(
|
||||
@@ -6974,7 +6970,7 @@ describe('images', () => {
|
||||
body: fd,
|
||||
})
|
||||
expect(res.status).toBe(400)
|
||||
expect(await res.json()).toEqual({ error: 'image is not a PNG, JPEG, GIF, WebP or BMP image' })
|
||||
expect(await res.json()).toEqual({ error: 'image is not a PNG or JPEG image' })
|
||||
expect((await env.IMAGES.list()).objects).toHaveLength(objectsBefore)
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user