- Speed calculation: raised minimum elapsed floor to 0.5s preventing unrealistic spikes - Reconnect: exponential backoff with consecutive counter, clock regression protection - Download engine: retry byte tracking (itemContributedBytes), mkdir before createWriteStream, content-length validation - Fire-and-forget promises: all void promises now have .catch() error handlers - Session recovery: normalize stale active statuses to queued on crash recovery, clear speedBps - Storage: config backup (.bak) before overwrite, EXDEV cross-device rename fallback with type guard - IPC security: input validation on all string/array IPC handlers, CSP headers in production - Main process: clipboard memory limit (50KB), installer timing increased to 800ms - Debrid: attribute-order-independent meta tag regex for Rapidgator filename extraction - Constants: named constants for magic numbers (MAX_MANIFEST_FILE_BYTES, MAX_LINK_ARTIFACT_BYTES, etc.) - Extractor/integrity: use shared constants, document password visibility and TOCTOU limitations - Tests: 103 tests total (55 new), covering utils, storage, integrity, cleanup, extractor, debrid, update Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
74 lines
2.8 KiB
TypeScript
74 lines
2.8 KiB
TypeScript
import fs from "node:fs";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import { afterEach, describe, expect, it } from "vitest";
|
|
import { parseHashLine, readHashManifest, validateFileAgainstManifest } from "../src/main/integrity";
|
|
|
|
const tempDirs: string[] = [];
|
|
|
|
afterEach(() => {
|
|
for (const dir of tempDirs.splice(0)) {
|
|
fs.rmSync(dir, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
describe("integrity", () => {
|
|
it("parses md5 and sfv lines", () => {
|
|
const md = parseHashLine("d41d8cd98f00b204e9800998ecf8427e sample.bin");
|
|
expect(md?.algorithm).toBe("md5");
|
|
const sfv = parseHashLine("sample.bin 1A2B3C4D");
|
|
expect(sfv?.algorithm).toBe("crc32");
|
|
});
|
|
|
|
it("validates file against md5 manifest", async () => {
|
|
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "rd-int-"));
|
|
tempDirs.push(dir);
|
|
const filePath = path.join(dir, "movie.bin");
|
|
fs.writeFileSync(filePath, Buffer.from("hello"));
|
|
fs.writeFileSync(path.join(dir, "hash.md5"), "5d41402abc4b2a76b9719d911017c592 movie.bin\n");
|
|
const result = await validateFileAgainstManifest(filePath, dir);
|
|
expect(result.ok).toBe(true);
|
|
});
|
|
|
|
it("skips manifest files larger than 5MB", () => {
|
|
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "rd-int-"));
|
|
tempDirs.push(dir);
|
|
|
|
// Create a .md5 manifest that exceeds the 5MB limit
|
|
const largeContent = "d41d8cd98f00b204e9800998ecf8427e sample.bin\n".repeat(200000);
|
|
const manifestPath = path.join(dir, "hashes.md5");
|
|
fs.writeFileSync(manifestPath, largeContent, "utf8");
|
|
|
|
// Verify the file is actually > 5MB
|
|
const stat = fs.statSync(manifestPath);
|
|
expect(stat.size).toBeGreaterThan(5 * 1024 * 1024);
|
|
|
|
// readHashManifest should skip the oversized file
|
|
const manifest = readHashManifest(dir);
|
|
expect(manifest.size).toBe(0);
|
|
});
|
|
|
|
it("does not parse SHA256 (64-char hex) as valid hash", () => {
|
|
// SHA256 is 64 chars - parseHashLine only supports 32 (MD5) and 40 (SHA1)
|
|
const sha256Line = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 emptyfile.bin";
|
|
const result = parseHashLine(sha256Line);
|
|
// 64-char hex should not match the MD5 (32) or SHA1 (40) pattern
|
|
expect(result).toBeNull();
|
|
});
|
|
|
|
it("parses SHA1 hash lines correctly", () => {
|
|
const sha1Line = "da39a3ee5e6b4b0d3255bfef95601890afd80709 emptyfile.bin";
|
|
const result = parseHashLine(sha1Line);
|
|
expect(result).not.toBeNull();
|
|
expect(result?.algorithm).toBe("sha1");
|
|
expect(result?.digest).toBe("da39a3ee5e6b4b0d3255bfef95601890afd80709");
|
|
expect(result?.fileName).toBe("emptyfile.bin");
|
|
});
|
|
|
|
it("ignores comment lines in hash manifests", () => {
|
|
expect(parseHashLine("; This is a comment")).toBeNull();
|
|
expect(parseHashLine("")).toBeNull();
|
|
expect(parseHashLine(" ")).toBeNull();
|
|
});
|
|
});
|