Preserve known legacy failures by reviewed identity, probe material delivery before and after releases, and retain originals and downloads.
200 lines
12 KiB
JavaScript
200 lines
12 KiB
JavaScript
import assert from 'node:assert/strict'
|
|
import { createHash } from 'node:crypto'
|
|
import test from 'node:test'
|
|
import { createRequire } from 'node:module'
|
|
import { checkMaterialPreviewHealth, createSharpPreviewDecoder, directFetch, isPublicAddress, LIMITS, registeredSource, validateManifest } from './check-material-preview-health.mjs'
|
|
|
|
const sharp = createRequire(import.meta.url)('sharp')
|
|
const fixture = () => sharp({ create: { width: 3, height: 2, channels: 3, background: '#345678' } })
|
|
const PNG = await fixture().png().toBuffer()
|
|
const JPEG = await fixture().jpeg().toBuffer()
|
|
const WEBP = await fixture().webp().toBuffer()
|
|
const source = 'https://s3.jiansun.vip/gongxiangpan/public-display/example.png'
|
|
const manifest = (url = source) => ({ version: 1, reviewed: true, materials: [{ materialId: 'material-a', registeredSource: url }] })
|
|
const response = (body, type, status = 200, extra = {}) => new Response(body, { status, headers: { 'content-type': type, ...extra } })
|
|
|
|
test('complete failure bodies retain a digest for exact incident baselines without retaining raw contents', async () => {
|
|
const body = '<html>Faithfully yours, frp.</html>'
|
|
const report = await checkMaterialPreviewHealth(manifest(), { fetchImpl: async () => response(body, 'text/html', 404) })
|
|
assert.equal(report.results[0].ok, false)
|
|
assert.equal(report.results[0].bodySha256, createHash('sha256').update(body).digest('hex'))
|
|
assert.equal(JSON.stringify(report).includes(body), false)
|
|
})
|
|
|
|
test('normal PNG/JPEG/WebP use one actual uncached GET with no redirects or credentials', async () => {
|
|
for (const [body, mime, format] of [[PNG, 'image/png', 'png'], [JPEG, 'image/jpeg', 'jpeg'], [WEBP, 'image/webp', 'webp']]) {
|
|
const calls = []
|
|
const report = await checkMaterialPreviewHealth(manifest(), { fetchImpl: async (url, options) => {
|
|
calls.push({ url, options })
|
|
return response(body, `${mime}; charset=binary`)
|
|
} })
|
|
assert.equal(report.passed, true)
|
|
assert.equal(report.readOnly, true)
|
|
assert.equal(report.retries, 0)
|
|
assert.equal(calls.length, 1)
|
|
assert.equal(calls[0].url, source)
|
|
assert.equal(calls[0].options.method, 'GET')
|
|
assert.equal(calls[0].options.cache, 'no-store')
|
|
assert.equal(calls[0].options.redirect, 'manual')
|
|
assert.match(calls[0].options.headers['Cache-Control'], /no-store/)
|
|
assert.equal(calls[0].options.headers.Authorization, undefined)
|
|
assert.equal(report.results[0].classification, 'healthy')
|
|
assert.equal(report.results[0].format, format)
|
|
assert.deepEqual(report.results[0].decoded, { width: 3, height: 2 })
|
|
assert.equal(report.results[0].bytes, body.length)
|
|
assert.match(report.results[0].bodySha256, /^[a-f0-9]{64}$/)
|
|
}
|
|
})
|
|
|
|
test('correct MIME and magic bytes cannot make truncated image headers healthy', async () => {
|
|
for (const [body, mime] of [
|
|
[PNG.subarray(0, 16), 'image/png'],
|
|
[JPEG.subarray(0, 16), 'image/jpeg'],
|
|
[WEBP.subarray(0, 20), 'image/webp'],
|
|
]) {
|
|
const report = await checkMaterialPreviewHealth(manifest(), { fetchImpl: async () => response(body, mime) })
|
|
assert.equal(report.passed, false)
|
|
assert.equal(report.results[0].code, 'image_decode_failed')
|
|
}
|
|
})
|
|
|
|
test('decoder is lazy, injectable, and a missing runtime decoder fails closed', async () => {
|
|
let requires = 0
|
|
const unavailable = createSharpPreviewDecoder({ requireImpl() { requires++; throw new Error('sensitive runtime path') } })
|
|
assert.equal(requires, 0)
|
|
const report = await checkMaterialPreviewHealth(manifest(), { decodeImage: unavailable, fetchImpl: async () => response(PNG, 'image/png') })
|
|
assert.equal(requires, 1)
|
|
assert.equal(report.results[0].classification, 'decoder_unavailable')
|
|
assert.equal(report.results[0].code, 'image_decoder_unavailable')
|
|
assert.ok(!JSON.stringify(report).includes('sensitive runtime path'))
|
|
const invalid = await checkMaterialPreviewHealth(manifest(), { decodeImage: async () => ({ format: 'png', width: 0, height: 2 }), fetchImpl: async () => response(PNG, 'image/png') })
|
|
assert.equal(invalid.results[0].code, 'image_decode_result_invalid')
|
|
})
|
|
|
|
test('pixel bombs cannot pass the native decoder budget', async () => {
|
|
const large = await sharp({ create: { width: 5001, height: 5000, channels: 3, background: '#ffffff' } }).png().toBuffer()
|
|
const report = await checkMaterialPreviewHealth(manifest(), { fetchImpl: async () => response(large, 'image/png') })
|
|
assert.equal(report.passed, false)
|
|
assert.equal(report.results[0].code, 'image_decode_failed')
|
|
})
|
|
|
|
test('FRP default 404 is distinguished from a missing S3 object and never retried', async () => {
|
|
let calls = 0
|
|
const report = await checkMaterialPreviewHealth(manifest('https://s3.1080p.hk/jsxiangche/a.png'), { fetchImpl: async () => {
|
|
calls++
|
|
return response('<html><center><h1>404 Not Found</h1></center><hr><center>frp</center></html>', 'text/html', 404)
|
|
} })
|
|
assert.equal(calls, 1)
|
|
assert.equal(report.passed, false)
|
|
assert.equal(report.results[0].classification, 'routing_failure')
|
|
assert.equal(report.results[0].code, 'frp_default_404')
|
|
const missing = await checkMaterialPreviewHealth(manifest(), { fetchImpl: async () => response('<Error><Code>NoSuchKey</Code></Error>', 'application/xml', 404) })
|
|
assert.equal(missing.results[0].classification, 'missing_object')
|
|
})
|
|
|
|
test('real FRP Not Found page is identified by HTTP status and explicit marker without digits in its body', async () => {
|
|
const html = '<!DOCTYPE html><html><head><title>Not Found</title></head><body><h1>Not Found</h1><p>powered by frp</p><p>Faithfully yours, frp</p></body></html>'
|
|
assert.equal(html.includes('404'), false)
|
|
let calls = 0
|
|
const report = await checkMaterialPreviewHealth(manifest(), { fetchImpl: async () => { calls++; return response(html, 'text/html', 404) } })
|
|
assert.equal(calls, 1)
|
|
assert.equal(report.results[0].classification, 'routing_failure')
|
|
assert.equal(report.results[0].code, 'frp_default_404')
|
|
const fakeSuccess = await checkMaterialPreviewHealth(manifest(), { fetchImpl: async () => response(html, 'text/html', 200) })
|
|
assert.equal(fakeSuccess.results[0].code, 'html_instead_of_image')
|
|
})
|
|
|
|
test('HTML disguised as successful image content is an entrance routing failure', async () => {
|
|
for (const type of ['text/html', 'image/png']) {
|
|
const report = await checkMaterialPreviewHealth(manifest(), { fetchImpl: async () => response('<!DOCTYPE html><html>wrong gateway</html>', type) })
|
|
assert.equal(report.results[0].classification, 'routing_failure')
|
|
assert.equal(report.results[0].code, 'html_instead_of_image')
|
|
}
|
|
})
|
|
|
|
test('declared and streaming byte limits are enforced without consuming an unbounded body', async () => {
|
|
let read = 0
|
|
let cancelled = false
|
|
const declared = await checkMaterialPreviewHealth(manifest(), { maxBytes: 16, fetchImpl: async () => response(new ReadableStream({
|
|
pull(controller) { read++; controller.enqueue(new Uint8Array(32)) },
|
|
cancel() { cancelled = true },
|
|
}), 'image/png', 200, { 'content-length': '999999999' }) })
|
|
assert.equal(declared.results[0].code, 'declared_body_limit')
|
|
assert.equal(declared.results[0].bytes, 0)
|
|
assert.ok(read <= 1, 'the declared limit is checked before reading')
|
|
assert.equal(cancelled, true, 'rejected response bodies are cancelled even before acquiring a reader')
|
|
const streamed = await checkMaterialPreviewHealth(manifest(), { maxBytes: 16, fetchImpl: async () => response(new ReadableStream({
|
|
start(controller) { controller.enqueue(new Uint8Array(12)); controller.enqueue(new Uint8Array(12)); controller.close() },
|
|
}), 'image/png') })
|
|
assert.equal(streamed.results[0].classification, 'too_large')
|
|
assert.equal(streamed.results[0].code, 'stream_body_limit')
|
|
})
|
|
|
|
test('redirects never follow a second host, including a permitted destination', async () => {
|
|
for (const target of ['http://127.0.0.1/private', 'https://s3.jiansun.vip/gongxiangpan/b.png']) {
|
|
const calls = []
|
|
const report = await checkMaterialPreviewHealth(manifest(), { fetchImpl: async (url, options) => {
|
|
calls.push({ url, options })
|
|
return response('', 'text/html', 302, { location: target })
|
|
} })
|
|
assert.equal(calls.length, 1)
|
|
assert.equal(calls[0].options.redirect, 'manual')
|
|
assert.equal(report.results[0].classification, 'redirect_blocked')
|
|
}
|
|
const changed = response(PNG, 'image/png')
|
|
Object.defineProperty(changed, 'url', { value: 'https://unregistered.example/p.png' })
|
|
const report = await checkMaterialPreviewHealth(manifest(), { fetchImpl: async () => changed })
|
|
assert.equal(report.results[0].code, 'response_url_changed')
|
|
})
|
|
|
|
test('unreviewed, duplicate and mixed invalid manifests fail before any fetch', async () => {
|
|
let calls = 0
|
|
const fetchImpl = async () => { calls++; return response(PNG, 'image/png') }
|
|
for (const value of [
|
|
{ ...manifest(), reviewed: false },
|
|
{ ...manifest(), materials: [...manifest().materials, ...manifest().materials] },
|
|
{ ...manifest(), materials: [...manifest().materials, { materialId: 'b', registeredSource: 'http://169.254.169.254/latest/meta-data' }] },
|
|
]) await assert.rejects(checkMaterialPreviewHealth(value, { fetchImpl }))
|
|
assert.equal(calls, 0)
|
|
})
|
|
|
|
test('only exact registered hosts and approved schemes/ports are accepted', () => {
|
|
for (const url of [
|
|
'http://127.0.0.1/private', 'http://192.168.58.20:9000/object.png', 'https://s3.jiansun.vip.evil.example/a.png',
|
|
'https://sub.s3.jiansun.vip/a.png', 'https://s3.jiansun.vip:444/a.png', 'https://user:pass@s3.jiansun.vip/a.png',
|
|
'http://s3.jiansun.vip/a.png', 'file:///C:/Windows/a.png', 'https://s3.jiansun.vip/', 'https://s3.jiansun.vip/a.png#secret',
|
|
]) assert.throws(() => registeredSource(url), { classification: 'source_rejected' })
|
|
assert.equal(registeredSource('http://s3.1080p.hk/jsxiangche/a.png').protocol, 'http:')
|
|
assert.equal(registeredSource('https://s3.yydd.ai/gongxiangpan/a.png').hostname, 's3.yydd.ai')
|
|
assert.equal(validateManifest(manifest()).length, 1)
|
|
assert.throws(() => directFetch('https://evil.example/a.png', { method: 'GET', cache: 'no-store', redirect: 'manual' }), { classification: 'source_rejected' })
|
|
})
|
|
|
|
test('DNS guard rejects local, private, mapped and special-purpose addresses', () => {
|
|
for (const ip of ['0.0.0.0', '10.0.0.1', '100.64.0.1', '127.0.0.1', '169.254.169.254', '172.16.0.1', '192.168.1.1', '198.18.0.1', '224.0.0.1', '::1', '::ffff:127.0.0.1', 'fe80::1', 'fc00::1', '2001:db8::1', '2002:7f00:1::1']) {
|
|
assert.equal(isPublicAddress(ip), false, ip)
|
|
}
|
|
for (const ip of ['218.93.9.38', '156.233.232.106', '2606:4700::1111']) assert.equal(isPublicAddress(ip), true, ip)
|
|
})
|
|
|
|
test('timeouts and network errors remain single-attempt failures and hide signed URL secrets', async () => {
|
|
let calls = 0
|
|
const report = await checkMaterialPreviewHealth(manifest(), { timeoutMs: 5, fetchImpl: () => { calls++; return new Promise(() => {}) } })
|
|
assert.equal(report.results[0].classification, 'timeout')
|
|
assert.equal(calls, 1)
|
|
const secret = 'never-print-this-signature'
|
|
const failure = await checkMaterialPreviewHealth(manifest(`${source}?X-Amz-Signature=${secret}`), { fetchImpl: async () => { throw new Error(secret) } })
|
|
assert.equal(failure.results[0].classification, 'network_failure')
|
|
assert.equal(failure.results[0].source.queryRedacted, true)
|
|
assert.ok(!JSON.stringify(failure).includes(secret))
|
|
await assert.rejects(checkMaterialPreviewHealth(manifest(), { timeoutMs: LIMITS.timeoutMs + 1 }))
|
|
})
|
|
|
|
test('image content type and magic must agree; JSON and empty images cannot pass', async () => {
|
|
for (const [body, type] of [['{}', 'application/json'], ['', 'image/png'], [JPEG, 'image/png']]) {
|
|
const report = await checkMaterialPreviewHealth(manifest(), { fetchImpl: async () => response(body, type) })
|
|
assert.equal(report.passed, false)
|
|
assert.equal(report.results[0].classification, 'unexpected_content')
|
|
}
|
|
})
|