/
s.homyakov
/
share-md
Обзор
Документация
Войти
/
s.homyakov
/
share-md
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/path-validation.ts
68 строк
2 KB
Sergey Khomyakov
Sanitize repository history
07 май 2026, 13:03
07 май 2026, 13:03
067d512
Код
Авторство
О чём код?
import fs from "node:fs"; import path from "node:path"; const MAX_PATH_LENGTH = 3000; export class InvalidNotePathError extends Error { public readonly statusCode = 400; constructor(message: string) { super(message); this.name = "InvalidNotePathError"; } } export function resolveVaultNotePath(vaultRoot: string, rawPath: string): string { if (!rawPath || typeof rawPath !== "string" || !rawPath.trim()) { throw new InvalidNotePathError("notePath is required"); } if (rawPath.includes("\0")) { throw new InvalidNotePathError("Invalid note path"); } if (rawPath.length > MAX_PATH_LENGTH) { throw new InvalidNotePathError("notePath is too long"); } let normalized = rawPath.trim().replace(/\\/g, "/"); try { normalized = decodeURIComponent(normalized); normalized = normalized.replace(/\\/g, "/"); } catch { // leave raw input if malformed percent encoding is provided intentionally. } if (/[A-Za-z]:[\\/]/.test(normalized)) { throw new InvalidNotePathError("Windows absolute paths are not allowed"); } if (path.posix.isAbsolute(normalized)) { throw new InvalidNotePathError("Absolute paths are not allowed"); } const safeNormalized = path.posix.normalize(normalized); const segments = safeNormalized.split("/").filter(Boolean); if (segments.some((segment) => segment === "..")) { throw new InvalidNotePathError("Path traversal is not allowed"); } const absoluteCandidate = path.resolve(vaultRoot, safeNormalized); const vaultReal = fs.realpathSync(vaultRoot); const noteReal = fs.realpathSync(absoluteCandidate); if (!noteReal.startsWith(`${vaultReal}${path.sep}`)) { throw new InvalidNotePathError("Note must be inside vault root"); } if (!noteReal.toLowerCase().endsWith(".md")) { throw new InvalidNotePathError("Only .md notes are allowed"); } const stat = fs.statSync(noteReal); if (!stat.isFile()) { throw new InvalidNotePathError("Note path must point to a file"); } return noteReal; }