Reject illegal names

This commit is contained in:
Valentin Brandl 2024-08-16 16:34:43 +02:00
parent f1ff9f1c38
commit 5dba5471d1
Signed by: vbrandl
GPG Key ID: CAD4DA1A789125F9
3 changed files with 35 additions and 5 deletions

View File

@ -2,7 +2,19 @@ import safePath from '$lib';
import { describe, it, expect } from 'vitest'; import { describe, it, expect } from 'vitest';
describe('safe path', () => { describe('safe path', () => {
it('removes non alphanum from string', () => { it('reject names with ../', () => {
expect(safePath('../../!=-.,/abc123')).toBe('abc123'); expect(safePath('../foobar', './uploads')).toBe(false);
});
it('accept names with ./', () => {
expect(safePath('./foobar', './uploads')).toBe(true);
});
it('reject names with /', () => {
expect(safePath('foo/bar', './uploads')).toBe(false);
});
it('accept happy path', () => {
expect(safePath('foobar', './uploads')).toBe(false);
}); });
}); });

View File

@ -1,10 +1,23 @@
// place files you want to import through the `$lib` alias in this folder. // place files you want to import through the `$lib` alias in this folder.
import path from 'path';
const safePath = (input: string) => input.replace(/\W/g, ''); function safePath(name: string, basePath: string): boolean {
const fullPath = `${basePath}/${name}`;
const relative = path.relative(basePath, fullPath);
return (
!!relative &&
// does move out of `basePath`
!relative.startsWith('..') &&
// exactly one layer deep, e.g. no `./uplodas/foo/bar`
!relative.includes('/') &&
// result is not an absolute path
!path.isAbsolute(relative)
);
}
const defaultPath: string = './uploads'; const defaultPath: string = './uploads';
if (!('STORAGE_PATH' in process.env)) { if (!('STORAGE_PATH' in process.env)) {
console.log(`'STORAGE_PATH' environment variable is not set. Defaulting to ${defaultPath}`); console.warn(`'STORAGE_PATH' environment variable is not set. Defaulting to ${defaultPath}`);
} }
export const storagePath: string = process.env.STORAGE_PATH ?? defaultPath; export const storagePath: string = process.env.STORAGE_PATH ?? defaultPath;

View File

@ -34,7 +34,12 @@ export const actions = {
return fail(400, { field: 'name', name: formName, incorrect: true }); return fail(400, { field: 'name', name: formName, incorrect: true });
} }
const name = safePath(formName as string); const name = formName as string;
if (!safePath(name, storagePath)) {
return fail(400, { field: 'name', name: name, incorrect: true });
}
// const name = safePath(formName as string);
files.forEach(async (file) => { files.forEach(async (file) => {
const outPath = `${storagePath}/${name}`; const outPath = `${storagePath}/${name}`;