45 lines
1.6 KiB
TypeScript
45 lines
1.6 KiB
TypeScript
// @vitest-environment node
|
|||
|
|
import { readFileSync } from "node:fs";
|
||
|
|
import { expect, it } from "vitest";
|
||
|
|
import { verifyAudioAsset } from "../src/audio/integrity";
|
||
|
|
|
||
|
|
// 신뢰 기준은 JS 번들에 고정한 manifest다. 요청 파일이 자기 해시를 선언할 수 없다.
|
||
|
|
it("109개 승인 WAV만 SHA-256·bytes·형식 검증을 통과한다", async () => {
|
||
|
|
let count = 0;
|
||
|
|
for (const directory of ["audio", "ambience"]) {
|
||
|
|
const manifest = JSON.parse(
|
||
|
|
readFileSync(`public/${directory}/manifest.json`, "utf8"),
|
||
|
|
);
|
||
|
|
for (const entry of manifest.files) {
|
||
|
|
const bytes = Uint8Array.from(
|
||
|
|
readFileSync(`public/${directory}/${entry.file}`),
|
||
|
|
).buffer;
|
||
|
|
await expect(
|
||
|
|
verifyAudioAsset(`/${directory}/${entry.file}?v=0.2.5`, bytes),
|
||
|
|
).resolves.toBeUndefined();
|
||
|
|
count++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
expect(count).toBe(109);
|
||
|
|
});
|
||
|
|
|
||
|
|
it("변조·다른 파일·외부 URL·이전 캐시·축약 응답은 decode 전에 거부한다", async () => {
|
||
|
|
const bytes = Uint8Array.from(readFileSync("public/ambience/street.wav"));
|
||
|
|
for (const url of [
|
||
|
|
"/ambience/rain.wav?v=0.2.5",
|
||
|
|
"/ambience/street.wav?v=0.2.3",
|
||
|
|
"https://external.example/ambience/street.wav?v=0.2.5",
|
||
|
|
"/ambience/unknown.wav?v=0.2.5",
|
||
|
|
]) {
|
||
|
|
await expect(verifyAudioAsset(url, bytes.slice().buffer)).rejects.toThrow();
|
||
|
|
}
|
||
|
|
const corrupt = bytes.slice();
|
||
|
|
corrupt[100] = corrupt[100]! ^ 1;
|
||
|
|
await expect(
|
||
|
|
verifyAudioAsset("/ambience/street.wav?v=0.2.5", corrupt.buffer),
|
||
|
|
).rejects.toThrow();
|
||
|
|
await expect(
|
||
|
|
verifyAudioAsset("/ambience/street.wav?v=0.2.5", bytes.slice(0, -2).buffer),
|
||
|
|
).rejects.toThrow();
|
||
|
|
});
|