1// @effect-diagnostics nodeBuiltinImport:off
2import * as NodePath from "node:path";
3
4export function normalizeAttachmentRelativePath(rawRelativePath: string): string | null {
5 const normalized = NodePath.normalize(rawRelativePath).replace(/^[/\\]+/, "");
6 if (normalized.length === 0 || normalized.startsWith("..") || normalized.includes("\0")) {
7 return null;
8 }
9 return normalized.replace(/\\/g, "/");
10}
11
12export function resolveAttachmentRelativePath(input: {
13 readonly attachmentsDir: string;
14 readonly relativePath: string;
15}): string | null {
16 const normalizedRelativePath = normalizeAttachmentRelativePath(input.relativePath);
17 if (!normalizedRelativePath) {
18 return null;
19 }
20
21 const attachmentsRoot = NodePath.resolve(input.attachmentsDir);
22 const filePath = NodePath.resolve(NodePath.join(attachmentsRoot, normalizedRelativePath));
23 if (!filePath.startsWith(`${attachmentsRoot}${NodePath.sep}`)) {
24 return null;
25 }
26 return filePath;
27}