From ec713a73a1dab767a60f7745a60e32908e06eb6d Mon Sep 17 00:00:00 2001 From: GabuTheDev <107962621+GabuTheDev@users.noreply.github.com> Date: Sat, 1 Feb 2025 14:51:03 +0200 Subject: [PATCH 1/8] feat(tosu/utils): Implement path sanitizer function. --- packages/tosu/src/utils/converters.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/tosu/src/utils/converters.ts b/packages/tosu/src/utils/converters.ts index ebd8e081..10d65416 100644 --- a/packages/tosu/src/utils/converters.ts +++ b/packages/tosu/src/utils/converters.ts @@ -76,3 +76,19 @@ export const numberFromDecimal = ( return value; }; + +/** + * Joins multiple paths into a single path, replacing invalid Windows path characters in the process + * + * @param {...string[]} paths Paths to join + * @returns {string} Joined path + */ +export const cleanPath = (...paths: string[]): string => { + paths.map((path) => + Buffer.from(path.trim()) + .toString('utf8') + .replace(/[<>:"|?*]/g, '') + ); + + return paths.join(...paths); +}; From 73ec0ff32b7cd0d3ed09d3d584d0c28c3b2c984b Mon Sep 17 00:00:00 2001 From: GabuTheDev <107962621+GabuTheDev@users.noreply.github.com> Date: Sat, 1 Feb 2025 14:52:08 +0200 Subject: [PATCH 2/8] feat: Sanitize paths in client instances. --- packages/tosu/src/instances/lazerInstance.ts | 3 ++- packages/tosu/src/instances/osuInstance.ts | 12 +++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/tosu/src/instances/lazerInstance.ts b/packages/tosu/src/instances/lazerInstance.ts index 23c6e91f..fbc3f44f 100644 --- a/packages/tosu/src/instances/lazerInstance.ts +++ b/packages/tosu/src/instances/lazerInstance.ts @@ -10,6 +10,7 @@ import { import { LazerMemory } from '@/memory/lazer'; import { Gameplay } from '@/states/gameplay'; import { Global } from '@/states/global'; +import { cleanPath } from '@/utils/converters'; import { AbstractInstance } from '.'; @@ -58,7 +59,7 @@ export class LazerInstance extends AbstractInstance { if (!global.gameFolder) { global.setGameFolder(this.path); - global.setSongsFolder(global.memorySongsFolder); + global.setSongsFolder(cleanPath(global.memorySongsFolder)); } // update important data before doing rest diff --git a/packages/tosu/src/instances/osuInstance.ts b/packages/tosu/src/instances/osuInstance.ts index 771baf13..5bd3ba19 100644 --- a/packages/tosu/src/instances/osuInstance.ts +++ b/packages/tosu/src/instances/osuInstance.ts @@ -7,12 +7,12 @@ import { wLogger } from '@tosu/common'; import fs from 'fs'; -import path from 'path'; import { AbstractInstance } from '@/instances/index'; import { StableMemory } from '@/memory/stable'; import { Gameplay } from '@/states/gameplay'; import { Global } from '@/states/global'; +import { cleanPath } from '@/utils/converters'; export class OsuInstance extends AbstractInstance { gameOverlayAllowed = true; @@ -61,14 +61,16 @@ export class OsuInstance extends AbstractInstance { } if (!global.gameFolder) { - global.setGameFolder(this.path); + global.setGameFolder(cleanPath(this.path)); // condition when user have different BeatmapDirectory in osu! config - if (fs.existsSync(global.memorySongsFolder)) { - global.setSongsFolder(global.memorySongsFolder); + if (fs.existsSync(cleanPath(global.memorySongsFolder))) { + global.setSongsFolder( + cleanPath(global.memorySongsFolder) + ); } else { global.setSongsFolder( - path.join(this.path, global.memorySongsFolder) + cleanPath(this.path, global.memorySongsFolder) ); } } From 9b07ac14a00a27a7ba422856ac93c14a059e24f6 Mon Sep 17 00:00:00 2001 From: GabuTheDev <107962621+GabuTheDev@users.noreply.github.com> Date: Sat, 1 Feb 2025 14:52:39 +0200 Subject: [PATCH 3/8] feat: Sanitize paths in beatmap states. --- packages/tosu/src/states/beatmap.ts | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/packages/tosu/src/states/beatmap.ts b/packages/tosu/src/states/beatmap.ts index 8778c3d6..a053871e 100644 --- a/packages/tosu/src/states/beatmap.ts +++ b/packages/tosu/src/states/beatmap.ts @@ -3,12 +3,11 @@ import { ClientType, config, wLogger } from '@tosu/common'; import fs from 'fs'; import { Beatmap as ParsedBeatmap, TimingPoint } from 'osu-classes'; import { BeatmapDecoder } from 'osu-parsers'; -import path from 'path'; import { BeatmapStrains } from '@/api/types/v1'; import { AbstractInstance } from '@/instances'; import { AbstractState } from '@/states'; -import { fixDecimals } from '@/utils/converters'; +import { cleanPath, fixDecimals } from '@/utils/converters'; import { removeDebuffMods } from '@/utils/osuMods'; import { CalculateMods, ModsLazer } from '@/utils/osuMods.types'; @@ -316,9 +315,9 @@ export class BeatmapPP extends AbstractState { `beatmapPP updateMapMetadata`, `Skip osu! music theme file`, { - SongsFolder: global.songsFolder, - Folder: menu.folder, - Path: menu.filename + SongsFolder: cleanPath(global.songsFolder), + Folder: cleanPath(menu.folder), + Path: cleanPath(menu.filename) } ); return; @@ -331,18 +330,18 @@ export class BeatmapPP extends AbstractState { `beatmapPP updateMapMetadata`, `Skip new map creation`, { - SongsFolder: global.songsFolder, - Folder: menu.folder, - Path: menu.filename + SongsFolder: cleanPath(global.songsFolder), + Folder: cleanPath(menu.folder), + Path: cleanPath(menu.filename) } ); return; } - const mapPath = path.join( - global.songsFolder.trim(), - menu.folder.trim(), - menu.filename.trim() + const mapPath = cleanPath( + global.songsFolder, + menu.folder, + menu.filename ); try { From 172216d15afc160c0441da6cc529a49755abba34 Mon Sep 17 00:00:00 2001 From: GabuTheDev <107962621+GabuTheDev@users.noreply.github.com> Date: Sat, 1 Feb 2025 15:26:33 +0200 Subject: [PATCH 4/8] fix(tosu/utils): Save array mapping to the original variable. --- packages/tosu/src/utils/converters.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/tosu/src/utils/converters.ts b/packages/tosu/src/utils/converters.ts index 10d65416..abc40de6 100644 --- a/packages/tosu/src/utils/converters.ts +++ b/packages/tosu/src/utils/converters.ts @@ -84,7 +84,7 @@ export const numberFromDecimal = ( * @returns {string} Joined path */ export const cleanPath = (...paths: string[]): string => { - paths.map((path) => + paths = paths.map((path) => Buffer.from(path.trim()) .toString('utf8') .replace(/[<>:"|?*]/g, '') From f67dc2099f6dee51c1eb2100ab2ae65944cf201e Mon Sep 17 00:00:00 2001 From: GabuTheDev <107962621+GabuTheDev@users.noreply.github.com> Date: Sat, 1 Feb 2025 15:27:53 +0200 Subject: [PATCH 5/8] fix(tosu/utils): Introduce platform-based invalid char removal. --- packages/tosu/src/utils/converters.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/tosu/src/utils/converters.ts b/packages/tosu/src/utils/converters.ts index abc40de6..6e1adcfa 100644 --- a/packages/tosu/src/utils/converters.ts +++ b/packages/tosu/src/utils/converters.ts @@ -87,7 +87,7 @@ export const cleanPath = (...paths: string[]): string => { paths = paths.map((path) => Buffer.from(path.trim()) .toString('utf8') - .replace(/[<>:"|?*]/g, '') + .replace(process.platform === 'win32' ? /[<>:"|?*]/g : /\//g, '') ); return paths.join(...paths); From 1117807fb8a005cb3bd9fff51d9a3a4832fbc88e Mon Sep 17 00:00:00 2001 From: GabuTheDev <107962621+GabuTheDev@users.noreply.github.com> Date: Sat, 1 Feb 2025 15:30:37 +0200 Subject: [PATCH 6/8] fix(tosu/utils): Remove os platform mention from function description. --- packages/tosu/src/utils/converters.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/tosu/src/utils/converters.ts b/packages/tosu/src/utils/converters.ts index 6e1adcfa..393edfc2 100644 --- a/packages/tosu/src/utils/converters.ts +++ b/packages/tosu/src/utils/converters.ts @@ -78,7 +78,7 @@ export const numberFromDecimal = ( }; /** - * Joins multiple paths into a single path, replacing invalid Windows path characters in the process + * Joins multiple paths into a single path, replacing invalid path characters in the process. * * @param {...string[]} paths Paths to join * @returns {string} Joined path From 8d82da2e0b59b7e3e45e4a70cbbda9471d00a663 Mon Sep 17 00:00:00 2001 From: Gabriel Constantin <107962621+GabuTheDev@users.noreply.github.com> Date: Tue, 4 Feb 2025 19:32:04 +0200 Subject: [PATCH 7/8] chore: Remove redundant path processing. --- packages/tosu/src/states/beatmap.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/tosu/src/states/beatmap.ts b/packages/tosu/src/states/beatmap.ts index a053871e..bab753ef 100644 --- a/packages/tosu/src/states/beatmap.ts +++ b/packages/tosu/src/states/beatmap.ts @@ -315,9 +315,9 @@ export class BeatmapPP extends AbstractState { `beatmapPP updateMapMetadata`, `Skip osu! music theme file`, { - SongsFolder: cleanPath(global.songsFolder), - Folder: cleanPath(menu.folder), - Path: cleanPath(menu.filename) + SongsFolder: global.songsFolder, + Folder: menu.folder, + Path: menu.filename } ); return; @@ -330,9 +330,9 @@ export class BeatmapPP extends AbstractState { `beatmapPP updateMapMetadata`, `Skip new map creation`, { - SongsFolder: cleanPath(global.songsFolder), - Folder: cleanPath(menu.folder), - Path: cleanPath(menu.filename) + SongsFolder: global.songsFolder, + Folder: menu.folder, + Path: menu.filename } ); return; From 5cc517ad58b353653207210daea1488089f67642 Mon Sep 17 00:00:00 2001 From: Gabriel Constantin <107962621+GabuTheDev@users.noreply.github.com> Date: Tue, 4 Feb 2025 19:35:55 +0200 Subject: [PATCH 8/8] feat: Sanitize path at initialization for single responsibility. --- packages/tosu/src/instances/index.ts | 3 ++- packages/tosu/src/instances/lazerInstance.ts | 3 +-- packages/tosu/src/instances/osuInstance.ts | 8 +++----- packages/tosu/src/states/beatmap.ts | 7 ++++--- packages/tosu/src/states/global.ts | 5 +++-- packages/tosu/src/states/menu.ts | 9 +++++---- 6 files changed, 18 insertions(+), 17 deletions(-) diff --git a/packages/tosu/src/instances/index.ts b/packages/tosu/src/instances/index.ts index 4cb43153..d13c6fb6 100644 --- a/packages/tosu/src/instances/index.ts +++ b/packages/tosu/src/instances/index.ts @@ -18,6 +18,7 @@ import { ResultScreen } from '@/states/resultScreen'; import { Settings } from '@/states/settings'; import { TourneyManager } from '@/states/tourney'; import { User } from '@/states/user'; +import { cleanPath } from '@/utils/converters'; export interface DataRepoList { settings: Settings; @@ -63,7 +64,7 @@ export abstract class AbstractInstance { this.pid = pid; this.process = new Process(this.pid, bitness); - this.path = this.process.path; + this.path = cleanPath(this.process.path); this.bitness = bitness; this.client = diff --git a/packages/tosu/src/instances/lazerInstance.ts b/packages/tosu/src/instances/lazerInstance.ts index fbc3f44f..23c6e91f 100644 --- a/packages/tosu/src/instances/lazerInstance.ts +++ b/packages/tosu/src/instances/lazerInstance.ts @@ -10,7 +10,6 @@ import { import { LazerMemory } from '@/memory/lazer'; import { Gameplay } from '@/states/gameplay'; import { Global } from '@/states/global'; -import { cleanPath } from '@/utils/converters'; import { AbstractInstance } from '.'; @@ -59,7 +58,7 @@ export class LazerInstance extends AbstractInstance { if (!global.gameFolder) { global.setGameFolder(this.path); - global.setSongsFolder(cleanPath(global.memorySongsFolder)); + global.setSongsFolder(global.memorySongsFolder); } // update important data before doing rest diff --git a/packages/tosu/src/instances/osuInstance.ts b/packages/tosu/src/instances/osuInstance.ts index 5bd3ba19..a42575e1 100644 --- a/packages/tosu/src/instances/osuInstance.ts +++ b/packages/tosu/src/instances/osuInstance.ts @@ -61,13 +61,11 @@ export class OsuInstance extends AbstractInstance { } if (!global.gameFolder) { - global.setGameFolder(cleanPath(this.path)); + global.setGameFolder(this.path); // condition when user have different BeatmapDirectory in osu! config - if (fs.existsSync(cleanPath(global.memorySongsFolder))) { - global.setSongsFolder( - cleanPath(global.memorySongsFolder) - ); + if (fs.existsSync(global.memorySongsFolder)) { + global.setSongsFolder(global.memorySongsFolder); } else { global.setSongsFolder( cleanPath(this.path, global.memorySongsFolder) diff --git a/packages/tosu/src/states/beatmap.ts b/packages/tosu/src/states/beatmap.ts index bab753ef..1d98a0e9 100644 --- a/packages/tosu/src/states/beatmap.ts +++ b/packages/tosu/src/states/beatmap.ts @@ -466,12 +466,13 @@ export class BeatmapPP extends AbstractState { const { bpm, bpmMin, bpmMax } = this.lazerBeatmap; if ( - this.lazerBeatmap.events.backgroundPath !== + cleanPath(this.lazerBeatmap.events.backgroundPath || '') !== menu.backgroundFilename && !lazerByPass ) { - menu.backgroundFilename = - this.lazerBeatmap.events.backgroundPath || ''; + menu.backgroundFilename = cleanPath( + this.lazerBeatmap.events.backgroundPath || '' + ); } this.previewtime = this.lazerBeatmap.general.previewTime; diff --git a/packages/tosu/src/states/global.ts b/packages/tosu/src/states/global.ts index 55d6c664..de4cef6e 100644 --- a/packages/tosu/src/states/global.ts +++ b/packages/tosu/src/states/global.ts @@ -1,6 +1,7 @@ import { ClientType, wLogger } from '@tosu/common'; import { AbstractState } from '@/states'; +import { cleanPath } from '@/utils/converters'; import { defaultCalculatedMods } from '@/utils/osuMods'; import { CalculateMods } from '@/utils/osuMods.types'; @@ -60,8 +61,8 @@ export class Global extends AbstractState { this.gameTime = result.gameTime; this.menuMods = result.menuMods; - this.skinFolder = result.skinFolder; - this.memorySongsFolder = result.memorySongsFolder; + this.skinFolder = cleanPath(result.skinFolder); + this.memorySongsFolder = cleanPath(result.memorySongsFolder); this.resetReportCount('global updateState'); } catch (exc) { diff --git a/packages/tosu/src/states/menu.ts b/packages/tosu/src/states/menu.ts index 08b509e9..4cee548f 100644 --- a/packages/tosu/src/states/menu.ts +++ b/packages/tosu/src/states/menu.ts @@ -1,6 +1,7 @@ import { ClientType, wLogger } from '@tosu/common'; import { AbstractState } from '@/states'; +import { cleanPath } from '@/utils/converters'; export class Menu extends AbstractState { gamemode: number; @@ -75,7 +76,7 @@ export class Menu extends AbstractState { // MD5 hasn't changed in over NEW_MAP_COMMIT_DELAY, commit to new map this.checksum = result.checksum; - this.filename = result.filename; + this.filename = cleanPath(result.filename); this.plays = result.plays; this.artist = result.artist; @@ -87,9 +88,9 @@ export class Menu extends AbstractState { this.cs = result.cs; this.hp = result.hp; this.od = result.od; - this.audioFilename = result.audioFilename; - this.backgroundFilename = result.backgroundFilename; - this.folder = result.folder; + this.audioFilename = cleanPath(result.audioFilename); + this.backgroundFilename = cleanPath(result.backgroundFilename); + this.folder = cleanPath(result.folder); this.creator = result.creator; this.difficulty = result.difficulty; this.mapID = result.mapID;