ed081ec477
Plan 3 Task 4:media-writer(attachment 行三级对拍 + 内容行引用提取双事件消费) + magic-bytes 手写签名表(零新增依赖)。 - 三级对拍(前置事实 7):①uploads 相对路径清单精确命中 ②侧车 source_url→ file_path→清单实体(精确 miss 按文件名候选,多候选歧义不自动选全列 issue) ③missing 只进报告不插行(sha256 NOT NULL 库层防线) - 魔数校验:命中实体读存储副本头部实际字节——SVG 检出 status=quarantined 不进 产物语义(引用不建 usage);未知签名 unsupported;扩展名声明 MIME 与检测不符 → issue(按检测登记) - usage 两阶段时序(WXR 文档序 items 先于 attachment):内容行提取引用 + 修订 锚点(依赖 content-writer 先处理同事件——Task 6 编排顺序契约),finish 统一 对 media_assets 反查(id→source_id / URL→source_url→public_path),顺序无关 - 悬空引用计数 mediaDanglingRefs 不静默(mini 203/204 钉死) - counts 六键 media 前缀独占(勿复用 'skipped' 的键契约注记) - 幂等:(site,source_url) 定向 onConflictDoNothing + usage 复合主键——重跑零增行 308 测试(296+6 单测+6 集成);typecheck/lint 三绿。 Co-Authored-By: Claude Code <noreply@anthropic.com>
67 lines
3.0 KiB
TypeScript
67 lines
3.0 KiB
TypeScript
// magic-bytes 单测(Plan 3 Task 4 Step 1):六类签名各正例 + 伪签名 null 例 +
|
||
// SVG 大小写不敏感 + 窗口边界(1KB 外不检出)+ 二进制签名优先于文本特征。
|
||
// 期望值全部手写字面量(与实现零共享——对拍而非引用)。
|
||
import { describe, expect, it } from 'vitest'
|
||
import { detectMediaType } from '../../apps/api/src/modules/import/magic-bytes.js'
|
||
|
||
const PNG_SIG = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a])
|
||
|
||
describe('detectMediaType 手写签名表', () => {
|
||
it('六类二进制签名正例:PNG/JPEG/GIF87a/GIF89a/WEBP/PDF', () => {
|
||
expect(detectMediaType(Buffer.concat([PNG_SIG, Buffer.from('rest-of-png')]))).toStrictEqual({
|
||
type: 'image/png',
|
||
svg: false,
|
||
})
|
||
expect(detectMediaType(Buffer.from([0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10]))).toStrictEqual({
|
||
type: 'image/jpeg',
|
||
svg: false,
|
||
})
|
||
expect(detectMediaType(Buffer.from('GIF87a-more-gif'))).toStrictEqual({ type: 'image/gif', svg: false })
|
||
expect(detectMediaType(Buffer.from('GIF89a-more-gif'))).toStrictEqual({ type: 'image/gif', svg: false })
|
||
expect(
|
||
detectMediaType(Buffer.concat([Buffer.from('RIFF'), Buffer.from([0x24, 0x00, 0x00, 0x00]), Buffer.from('WEBPVP8 ')])),
|
||
).toStrictEqual({ type: 'image/webp', svg: false })
|
||
expect(detectMediaType(Buffer.from('%PDF-1.7\n%fake-payload'))).toStrictEqual({
|
||
type: 'application/pdf',
|
||
svg: false,
|
||
})
|
||
})
|
||
|
||
it('SVG 文本特征:大小写不敏感(<svg / <SVG / <Svg,含 xml 声明前导),svg=true', () => {
|
||
expect(detectMediaType(Buffer.from('<svg xmlns="http://www.w3.org/2000/svg"/>'))).toStrictEqual({
|
||
type: 'image/svg+xml',
|
||
svg: true,
|
||
})
|
||
expect(detectMediaType(Buffer.from('<?xml version="1.0"?><SVG onload="x()">'))).toStrictEqual({
|
||
type: 'image/svg+xml',
|
||
svg: true,
|
||
})
|
||
expect(detectMediaType(Buffer.from('<!-- c --><Svg width="1">'))).toStrictEqual({
|
||
type: 'image/svg+xml',
|
||
svg: true,
|
||
})
|
||
})
|
||
|
||
it('伪签名 / 纯文本 / 空缓冲 → null(未知签名不猜格式)', () => {
|
||
expect(detectMediaType(Buffer.from('hello plain text note'))).toBe(null)
|
||
expect(detectMediaType(Buffer.from([0x00, 0x01, 0x02, 0x03]))).toBe(null)
|
||
expect(detectMediaType(Buffer.alloc(0))).toBe(null)
|
||
})
|
||
|
||
it('RIFF 非 WEBP(WAV)不误报 webp → null', () => {
|
||
expect(
|
||
detectMediaType(Buffer.concat([Buffer.from('RIFF'), Buffer.from([0x24, 0x00, 0x00, 0x00]), Buffer.from('WAVEfmt ')])),
|
||
).toBe(null)
|
||
})
|
||
|
||
it('二进制签名优先:PNG 头 + 尾部 <svg 片段仍判 PNG(文本窗口只对非二进制生效)', () => {
|
||
const sneaky = Buffer.concat([PNG_SIG, Buffer.from('<svg onload="alert(1)"> trailing')])
|
||
expect(detectMediaType(sneaky)).toStrictEqual({ type: 'image/png', svg: false })
|
||
})
|
||
|
||
it('SVG 窗口 1KB 钉死:<svg 出现在 1KB 之后不检出 → null', () => {
|
||
const beyond = Buffer.concat([Buffer.alloc(1200, 0x20), Buffer.from('<svg>')])
|
||
expect(detectMediaType(beyond)).toBe(null)
|
||
})
|
||
})
|