Skip to content

Commit

Permalink
merge: update from dev branch
Browse files Browse the repository at this point in the history
include PR #1
  • Loading branch information
alikia2x committed Jul 24, 2024
2 parents ab8c32f + 253742f commit e4677f4
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 28 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "aquavox",
"version": "1.12.12",
"version": "1.12.13",
"private": false,
"scripts": {
"dev": "vite dev",
Expand Down
23 changes: 23 additions & 0 deletions src/index.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { describe, it, expect } from 'vitest';
import formatDuration from '$lib/formatDuration';
import { safePath } from '$lib/server/safePath';

describe('formatDuration test', () => {
it('converts 120 seconds to "2:00"', () => {
Expand All @@ -21,4 +22,26 @@ describe('formatDuration test', () => {
it('converts 3601 seconds to "1:00:01"', () => {
expect(formatDuration(3601)).toBe('1:00:01');
});
});

describe('safePath test', () => {
const base = "data/subdir";
it('rejects empty string', () => {
expect(safePath('', { base })).toBe(null);
});
it('accepts a regular path', () => {
expect(safePath('subsubdir/file.txt', { base })).toBe('data/subdir/subsubdir/file.txt');
});
it('rejects path with ..', () => {
expect(safePath('../file.txt', { base })).toBe(null);
});
it('accepts path with .', () => {
expect(safePath('./file.txt', { base })).toBe('data/subdir/file.txt');
});
it('accepts path traversal within base', () => {
expect(safePath('subsubdir/../file.txt', { base })).toBe('data/subdir/file.txt');
});
it('rejects path with subdir if noSubDir is true', () => {
expect(safePath('subsubdir/file.txt', { base, noSubDir: true })).toBe(null);
});
});
8 changes: 7 additions & 1 deletion src/lib/server/database/loadData.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import fs from 'fs';
import { globalMemoryStorage, songData, songNameCache } from '$lib/server/cache.js';
import { getDirectoryHash } from '../dirHash';
import { safePath } from '../safePath';

const dataPath = './data/song/';

Expand All @@ -25,7 +26,12 @@ export async function loadData() {
songNameCache.flushAll();
for (const songID of songList) {
try {
const fileContentString = fs.readFileSync(dataPath + songID + '.json').toString();
const normPath = safePath(songID + '.json', { base: dataPath });
if (!normPath) {
console.error(`[load-song-data] Invalid path for song ID ${songID}`);
continue;
}
const fileContentString = fs.readFileSync(normPath).toString();
const data = JSON.parse(fileContentString);
songData.set(songID, data);
const metadata: MusicMetadata = data;
Expand Down
4 changes: 2 additions & 2 deletions src/lib/server/dirHash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export function getDirectoryHash(dir: string): string {

files.forEach(file => {
const filePath = path.join(currentDir, file);
const stats = fs.statSync(filePath);
const stats = fs.lstatSync(filePath);

if (stats.isDirectory()) {
traverseDirectory(filePath);
Expand All @@ -30,7 +30,7 @@ export function getDirectoryHash(dir: string): string {

// Create hash from file details
const hash = crypto.createHash('sha256');
hash.update(fileDetails.join('|'));
hash.update(fileDetails.join('\x00'));

return hash.digest('hex');
}
22 changes: 22 additions & 0 deletions src/lib/server/safePath.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import path from 'path';

export function safePath(pathIn: string, options: { base: string, noSubDir?: boolean }): string | null {
const base = options.base.endsWith('/') ? options.base : options.base + '/';
if (!pathIn.startsWith("./")) {
pathIn = "./" + pathIn;
}
pathIn = path.join(base, pathIn);
const normBase = path.normalize(base);
const normPath = path.normalize(pathIn);

if (normPath !== normBase && normPath.startsWith(normBase)) {
if (options.noSubDir) {
let rel = path.relative(normBase, normPath);
if (rel.indexOf(path.sep) !== -1) {
return null;
}
}
return normPath;
}
return null;
}
44 changes: 28 additions & 16 deletions src/routes/api/database/song/[id]/+server.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,43 @@
import { safePath } from '$lib/server/safePath';
import { getCurrentFormattedDateTime } from '$lib/songUpdateTime';
import { json, error } from '@sveltejs/kit';
import fs from 'fs';
import type { RequestHandler } from './$types';

export const GET: RequestHandler = async ({ params }) => {
const filePath = `./data/song/${params.id}.json`;
if (!fs.existsSync(filePath)) {
const filePath = safePath(`${params.id}.json`, { base: './data/song' });
if (!filePath) {
return error(404, {
message: "No correspoding song."
});
}
let data;
try { data = fs.readFileSync(filePath); } catch (e) {
return error(404, {
message: "No corresponding song."
})
});
}
const data = fs.readFileSync(filePath);
return json(JSON.parse(data.toString()));
return json(JSON.parse(data.toString()));
}

export const POST: RequestHandler = async ({ request, params }) => {
const timeStamp = new Date().getTime();
if (!fs.existsSync("./data/pending/")) {
fs.mkdirSync("./data/pending");
try {
if (!fs.existsSync("./data/pending/")) {
fs.mkdirSync("./data/pending", { mode: 0o755 });
}
const filePath = `./data/pending/${params.id}-${timeStamp}.json`;
const data: MusicMetadata = await request.json();
data.updateTime = getCurrentFormattedDateTime();
fs.writeFileSync(filePath, JSON.stringify(data, null, 4), { mode: 0o644 });
return json({
"message": "successfully created"
}, {
status: 201
});
} catch (e) {
return error(500, {
message: "Internal server error."
});
}
const filePath = `./data/pending/${params.id}-${timeStamp}.json`;
const data: MusicMetadata = await request.json();
data.updateTime = getCurrentFormattedDateTime();
fs.writeFileSync(filePath, JSON.stringify(data, null, 4));
return json({
"message": "successfully created"
}, {
status: 201
});
}
15 changes: 7 additions & 8 deletions src/routes/database/edit/[id]/+page.server.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
import type { PageServerLoad } from './$types';
import fs from 'fs';

import type { PageServerLoad } from './$types';
import { safePath } from '$lib/server/safePath';

export const load: PageServerLoad = ({ params }) => {
const filePath = `./data/song/${params.id}.json`;
if (!fs.existsSync(filePath)) {
const filePath = safePath(`${params.id}.json`, { base: './data/song' });
if (!filePath) {
return {
songData: null
}
};
}
const dataBuffer = fs.readFileSync(filePath);
try {
const dataBuffer = fs.readFileSync(filePath);
const data = JSON.parse(dataBuffer.toString());
return {
songData: data
};
}
catch {
} catch {
return {
songData: null
}
Expand Down

0 comments on commit e4677f4

Please sign in to comment.