Compare commits
10 Commits
Author | SHA1 | Date | |
---|---|---|---|
4b8ecc65c3
|
|||
3696bcade2
|
|||
dea401fec0
|
|||
66494c6760
|
|||
05320916b2
|
|||
e6857f5b5d
|
|||
5dba5471d1
|
|||
f1ff9f1c38
|
|||
7386a29ec5
|
|||
88b158b7b4
|
@ -6,6 +6,9 @@ on:
|
|||||||
- 'v*'
|
- 'v*'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
lints:
|
||||||
|
uses: ./.gitea/workflows/lints.yml
|
||||||
|
|
||||||
tests:
|
tests:
|
||||||
uses: ./.gitea/workflows/node.yml
|
uses: ./.gitea/workflows/node.yml
|
||||||
|
|
||||||
|
6
flake.lock
generated
6
flake.lock
generated
@ -20,11 +20,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1720957393,
|
"lastModified": 1723637854,
|
||||||
"narHash": "sha256-oedh2RwpjEa+TNxhg5Je9Ch6d3W1NKi7DbRO1ziHemA=",
|
"narHash": "sha256-med8+5DSWa2UnOqtdICndjDAEjxr5D7zaIiK4pn0Q7c=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "693bc46d169f5af9c992095736e82c3488bf7dbb",
|
"rev": "c3aa7b8938b17aebd2deecf7be0636000d62a2b9",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
4
package-lock.json
generated
4
package-lock.json
generated
@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "fotochallenge",
|
"name": "fotochallenge",
|
||||||
"version": "0.0.1",
|
"version": "0.0.5",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "fotochallenge",
|
"name": "fotochallenge",
|
||||||
"version": "0.0.1",
|
"version": "0.0.5",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@sveltejs/adapter-auto": "^3.0.0",
|
"@sveltejs/adapter-auto": "^3.0.0",
|
||||||
"@sveltejs/adapter-node": "^5.2.0",
|
"@sveltejs/adapter-node": "^5.2.0",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "fotochallenge",
|
"name": "fotochallenge",
|
||||||
"version": "0.0.1",
|
"version": "0.0.5",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite dev",
|
"dev": "vite dev",
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<title>Gabi und Hannes Fotochallenge</title>
|
||||||
%sveltekit.head%
|
%sveltekit.head%
|
||||||
</head>
|
</head>
|
||||||
<body data-sveltekit-preload-data="hover">
|
<body data-sveltekit-preload-data="hover">
|
||||||
|
@ -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('./uplodas', '../foobar')).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('accept names with ./', () => {
|
||||||
|
expect(safePath('./uplodas', './foobar')).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reject names with /', () => {
|
||||||
|
expect(safePath('./uplodas', 'foo/bar')).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('accept happy path', () => {
|
||||||
|
expect(safePath('./uplodas', 'foobar')).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -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(basePath: string, name: 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;
|
||||||
|
|
||||||
|
@ -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(storagePath, name)) {
|
||||||
|
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}`;
|
||||||
|
Reference in New Issue
Block a user