Sanitize file names taken from server metadata (closes #9)
check / check (push) Successful in 37s
check / check (push) Successful in 37s
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 was merged in pull request #78.
This commit is contained in:
+8
-11
@@ -40,8 +40,9 @@ import {
|
||||
symlinkSync,
|
||||
writeFileSync,
|
||||
} from "node:fs";
|
||||
import { basename, dirname, extname, join, relative } from "node:path";
|
||||
import { basename, dirname, join, relative } from "node:path";
|
||||
|
||||
import { safeExtension, sanitizeFileName } from "./filename.js";
|
||||
import type { Collection, EnteFile } from "./model/types.js";
|
||||
|
||||
export type ProgressCallback = (message: string) => void;
|
||||
@@ -108,16 +109,11 @@ interface FailureEntry {
|
||||
|
||||
const LEDGER_VERSION = 1;
|
||||
|
||||
const sanitizePath = (name: string): string =>
|
||||
name.replace(/[/\\:*?"<>|]/g, "_").replace(/^\.+/, "_");
|
||||
|
||||
// The originals/ filename for a file: `<id><ext>`, the extension taken from the
|
||||
// title (or `.bin`). Matches the content cache's own naming so a present check
|
||||
// lines up with what a fetch would write.
|
||||
const originalName = (file: EnteFile): string => {
|
||||
const ext = extname(file.metadata.title || "") || ".bin";
|
||||
return `${file.id}${ext}`;
|
||||
};
|
||||
const originalName = (file: EnteFile): string =>
|
||||
`${file.id}${safeExtension(file.metadata.title)}`;
|
||||
|
||||
// A regular file with content is treated as complete. A zero-byte file is not:
|
||||
// it is the shape an aborted write leaves and must be re-fetched.
|
||||
@@ -370,7 +366,7 @@ export const runBackup = async (
|
||||
|
||||
// Then the per-collection symlink trees and JSON.
|
||||
for (const c of collections) {
|
||||
const colDirName = sanitizePath(c.name || `collection-${c.id}`);
|
||||
const colDirName = sanitizeFileName(c.name, `collection-${c.id}`);
|
||||
const colDir = join(collectionsDir, colDirName);
|
||||
mkdirSync(colDir, { recursive: true });
|
||||
|
||||
@@ -381,8 +377,9 @@ export const runBackup = async (
|
||||
if (!includeOriginals) continue;
|
||||
const orig = join(originalsDir, originalName(file));
|
||||
if (!isPresent(orig)) continue;
|
||||
const linkName = sanitizePath(
|
||||
file.metadata.title || `file-${file.id}`,
|
||||
const linkName = sanitizeFileName(
|
||||
file.metadata.title,
|
||||
`file-${file.id}`,
|
||||
);
|
||||
const linkPath = join(colDir, linkName);
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user