Sanitize file names taken from server metadata (closes #9)
check / check (push) Successful in 30s

A file title or album name decrypted from server data could name a path
outside the chosen directory (`../../.ssh/authorized_keys`). One module,
src/filename.ts, now makes such names safe for `quak get`/`get-thumb`
without `--out`, downloadFile/downloadThumbnail without outPath, and the
backup and metadata backup trees. Originals-cache extensions are limited to
letters and digits. A user-supplied path is still used as is. decryptFile
reads a missing or non-string title as "" and rejects metadata that is not
a JSON object.

Model: opus-5-5
This commit is contained in:
2026-09-22 23:41:58 +00:00
parent fe952d3e62
commit 047f63c776
15 changed files with 381 additions and 44 deletions
+5 -3
View File
@@ -165,14 +165,15 @@ const buildMetaMock = async (): Promise<MetaMockState> => {
},
};
// Collection 2: "Work" with no magic metadata
// Collection 2: "../Work" with no magic metadata. The server chose a name
// that tries to climb out of the backup directory.
const ck2 = sodium.crypto_secretbox_keygen();
const { ciphertext: encCK2, nonce: ck2N } = encryptSecretbox(
ck2,
masterKey,
);
const { ciphertext: encCN2, nonce: cn2N } = encryptSecretbox(
new TextEncoder().encode("Work"),
new TextEncoder().encode("../Work"),
ck2,
);
const rawColl2 = {
@@ -496,7 +497,8 @@ describe("quak backup-metadata", () => {
await runBackup(outDir);
const collDirs = readdirSync(join(outDir, "collections"));
expect(collDirs.length).toBe(2);
// "../Work" is sanitized into one directory name.
expect(collDirs.sort()).toEqual(["10-Vacation", "20-__Work"]);
// Find the Vacation collection dir (prefixed with ID)
const vacDir = collDirs.find((d) => d.includes("Vacation"))!;