import test from 'node:test'; import assert from 'node:assert/strict'; import { IngressError, inspectImageBytes, MAX_EDGE, MAX_PIXELS } from '../src/image-ingress.js'; // Every fixture built here is synthetic container bytes assembled in code. No photograph of any // person or specimen is used, and nothing is fetched. const PNG_SIGNATURE = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]); function pngChunk(type, data = Buffer.alloc(0)) { const length = Buffer.alloc(4); length.writeUInt32BE(data.length); const body = Buffer.concat([Buffer.from(type, 'ascii'), data]); // The parser walks chunk lengths to find the end of the container; the CRC is not read, so a // synthetic zero CRC is enough to be structurally complete. return Buffer.concat([length, body, Buffer.alloc(4)]); } function png({ width = 1200, height = 900, extraChunks = [], tail = Buffer.alloc(0) } = {}) { const ihdr = Buffer.alloc(13); ihdr.writeUInt32BE(width, 0); ihdr.writeUInt32BE(height, 4); ihdr[8] = 8; // bit depth ihdr[9] = 2; // truecolour return Buffer.concat([ PNG_SIGNATURE, pngChunk('IHDR', ihdr), ...extraChunks, pngChunk('IDAT', Buffer.from([0x78, 0x9c, 0x01])), pngChunk('IEND'), tail, ]); } function jpeg({ width = 1200, height = 900, segments = [], tail = Buffer.alloc(0) } = {}) { const sof = Buffer.alloc(17); sof.writeUInt16BE(0xffc0, 0); sof.writeUInt16BE(15, 2); // payload length sof[4] = 8; // precision sof.writeUInt16BE(height, 5); sof.writeUInt16BE(width, 7); sof[9] = 3; // component count for (let i = 0; i < 3; i += 1) { sof[10 + i * 3] = i + 1; sof[11 + i * 3] = 0x11; sof[12 + i * 3] = 0; } return Buffer.concat([ Buffer.from([0xff, 0xd8]), ...segments, sof, Buffer.from([0xff, 0xd9]), tail, ]); } function exifSegment() { const payload = Buffer.concat([ Buffer.from('Exif', 'ascii'), Buffer.from([0x00, 0x00]), Buffer.from('GPSLatitude=0.000', 'ascii'), ]); const header = Buffer.alloc(4); header.writeUInt16BE(0xffe1, 0); header.writeUInt16BE(payload.length + 2, 2); return Buffer.concat([header, payload]); } function webp({ width = 1200, height = 900 } = {}) { // VP8X canvas payload: flags(1) + reserved(3) + width-1(3, LE) + height-1(3, LE). const payload = Buffer.alloc(10); payload[0] = 0; payload.writeUIntLE(width - 1, 4, 3); payload.writeUIntLE(height - 1, 7, 3); const chunk = Buffer.alloc(8); chunk.write('VP8X', 0, 'ascii'); chunk.writeUInt32LE(payload.length, 4); const body = Buffer.concat([Buffer.from('WEBP', 'ascii'), chunk, payload]); const riff = Buffer.alloc(8); riff.write('RIFF', 0, 'ascii'); riff.writeUInt32LE(body.length, 4); return Buffer.concat([riff, body]); } const expectRefusal = (bytes, declaredMime, pattern) => { assert.throws( () => inspectImageBytes(bytes, { declaredMime }), error => { assert.ok(error instanceof IngressError, `expected an IngressError, got ${error?.name}`); assert.match(error.message, pattern); return true; }, ); }; test('refuses a file whose bytes are not a JPEG, PNG, or WebP', () => { expectRefusal(Buffer.from('', 'utf8'), 'image/jpeg', /not a JPEG, PNG, or WebP photo/i); }); test('refuses a PNG that claims to be a JPEG', () => { expectRefusal(png(), 'image/jpeg', /not the format it claims/i); }); test('refuses a JPEG that claims to be a PNG', () => { expectRefusal(jpeg(), 'image/png', /not the format it claims/i); }); test('refuses a truncated container instead of forwarding a partial image', () => { expectRefusal(PNG_SIGNATURE.subarray(0, 8), 'image/png', /incomplete or damaged/i); expectRefusal(Buffer.from([0xff, 0xd8, 0xff, 0xe0]), 'image/jpeg', /incomplete or damaged/i); }); test('refuses a PNG with bytes appended after its end marker', () => { expectRefusal(png({ tail: Buffer.from('PK\u0003\u0004zip-appended', 'binary') }), 'image/png', /extra data after the image/i); }); test('refuses a JPEG with bytes appended after its end marker', () => { expectRefusal(jpeg({ tail: Buffer.from('', 'utf8') }), 'image/jpeg', /extra data after the image/i); }); test('refuses a JPEG that still carries an EXIF segment', () => { expectRefusal(jpeg({ segments: [exifSegment()] }), 'image/jpeg', /embedded metadata/i); }); test('refuses a PNG that carries an eXIf chunk', () => { const exifChunk = pngChunk('eXIf', Buffer.from('MM\u0000*metadata', 'binary')); expectRefusal(png({ extraChunks: [exifChunk] }), 'image/png', /embedded metadata/i); }); test('accepts a clean JPEG and reports its real dimensions', () => { const intake = inspectImageBytes(jpeg({ width: 1200, height: 900 }), { declaredMime: 'image/jpeg' }); assert.equal(intake.mime, 'image/jpeg'); assert.equal(intake.width, 1200); assert.equal(intake.height, 900); assert.equal(intake.bytes, jpeg({ width: 1200, height: 900 }).length); }); test('accepts a clean PNG and reports its real dimensions', () => { const intake = inspectImageBytes(png({ width: 800, height: 600 }), { declaredMime: 'image/png' }); assert.equal(intake.mime, 'image/png'); assert.equal(intake.width, 800); assert.equal(intake.height, 600); }); test('accepts a clean WebP and reports its canvas dimensions', () => { const intake = inspectImageBytes(webp({ width: 640, height: 480 }), { declaredMime: 'image/webp' }); assert.equal(intake.mime, 'image/webp'); assert.equal(intake.width, 640); assert.equal(intake.height, 480); }); test(`refuses an edge longer than ${MAX_EDGE} pixels even when the file is small`, () => { expectRefusal(png({ width: MAX_EDGE + 1, height: 200 }), 'image/png', /too large in pixels/i); }); test(`refuses an image above ${MAX_PIXELS} pixels when each edge fits`, () => { const edge = 4096; expectRefusal(jpeg({ width: edge, height: Math.ceil(MAX_PIXELS / edge) + 1 }), 'image/jpeg', /too large in pixels/i); }); test('never repeats image bytes back to the caller in a refusal message', () => { const marker = 'SECRETPIXELMARKER'; try { inspectImageBytes(Buffer.from(`not an image ${marker}`, 'utf8'), { declaredMime: 'image/jpeg' }); assert.fail('expected a refusal'); } catch (error) { assert.ok(error instanceof IngressError); assert.doesNotMatch(error.message, new RegExp(marker)); assert.doesNotMatch(error.message, /[A-Za-z0-9+/=]{40,}/); } }); test('refuses an empty body with a bounded message', () => { expectRefusal(Buffer.alloc(0), 'image/jpeg', /empty/i); });