Assessor_2.0/src/domain/artifact-path.ts

19 lines
1011 B
TypeScript

const windowsReservedName = /^(?:con|prn|aux|nul|clock\$|com[1-9]|lpt[1-9])(?:\..*)?$/i;
const unsafeUnicode = /[\u0000-\u001f\u007f\u202a-\u202e\u2066-\u2069\u2044\u2215\u29f8]/;
export function normalizeArtifactRelativePath(value: string): string | null {
if (!value || value.length > 4096 || value !== value.normalize('NFC') || unsafeUnicode.test(value)) return null;
if (value.startsWith('/') || value.startsWith('\\') || /^[a-z]:/i.test(value)) return null;
const normalized = value.replace(/\\/g, '/');
const segments = normalized.split('/');
if (segments.some(segment => !segment || segment === '.' || segment === '..' || segment.length > 255
|| segment !== segment.trim() || segment.endsWith('.') || segment.includes(':')
|| windowsReservedName.test(segment))) return null;
return segments.join('/');
}
export function artifactPathCollisionKey(value: string): string | null {
const normalized = normalizeArtifactRelativePath(value);
return normalized?.toLowerCase() ?? null;
}