From 37a2b1267d8a61e489507e5ec67bca838b915957 Mon Sep 17 00:00:00 2001 From: taozhiyu <49258735+taozhiyu@users.noreply.github.com> Date: Sun, 10 Aug 2025 17:19:57 +0800 Subject: [PATCH 1/4] Add input validation to script API routes --- back/api/script.ts | 52 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/back/api/script.ts b/back/api/script.ts index 1bf85565..071026d1 100644 --- a/back/api/script.ts +++ b/back/api/script.ts @@ -24,7 +24,14 @@ const upload = multer({ storage: storage }); export default (app: Router) => { app.use('/scripts', route); - route.get('/', async (req: Request, res: Response, next: NextFunction) => { + route.get( + '/', + celebrate({ + query: Joi.object({ + path: Joi.string().optional().allow(''), + }), + }), + async (req: Request, res: Response, next: NextFunction) => { const logger: Logger = Container.get('logger'); try { let result: IFile[] = []; @@ -68,6 +75,12 @@ export default (app: Router) => { route.get( '/detail', + celebrate({ + query: Joi.object({ + path: Joi.string().optional().allow(''), + file: Joi.string().required(), + }), + }), async (req: Request, res: Response, next: NextFunction) => { try { const scriptService = Container.get(ScriptService); @@ -84,12 +97,20 @@ export default (app: Router) => { route.get( '/:file', + celebrate({ + params: Joi.object({ + file: Joi.string().required(), + }), + query: Joi.object({ + path: Joi.string().optional().allow(''), + }), + }), async (req: Request, res: Response, next: NextFunction) => { try { const scriptService = Container.get(ScriptService); const content = await scriptService.getFile( req.query.path as string, - req.params.file, + req.params?.file || '', ); res.send({ code: 200, data: content }); } catch (e) { @@ -101,6 +122,16 @@ export default (app: Router) => { route.post( '/', upload.single('file'), + celebrate({ + body: Joi.object({ + filename: Joi.string().required(), + file: Joi.string().optional().allow(''), + path: Joi.string().optional().allow(''), + content: Joi.string().optional().allow(''), + originFilename: Joi.string().optional().allow(''), + directory: Joi.string().optional().allow(''), + }), + }), async (req: Request, res: Response, next: NextFunction) => { try { let { filename, path, content, originFilename, directory } = @@ -201,7 +232,7 @@ export default (app: Router) => { celebrate({ body: Joi.object({ filename: Joi.string().required(), - path: Joi.string().allow(''), + path: Joi.string().optional().allow(''), type: Joi.string().optional(), }), }), @@ -211,6 +242,9 @@ export default (app: Router) => { filename: string; path: string; }; + if (!path) { + path = ''; + } const scriptService = Container.get(ScriptService); const filePath = scriptService.checkFilePath(path, filename); if (!filePath) { @@ -276,6 +310,9 @@ export default (app: Router) => { const logger: Logger = Container.get('logger'); try { let { filename, content, path } = req.body; + if (!path) { + path = ''; + } const { name, ext } = parse(filename); const filePath = join(config.scriptPath, path, `${name}.swap${ext}`); await writeFileWithLock(filePath, content || ''); @@ -301,6 +338,9 @@ export default (app: Router) => { async (req: Request, res: Response, next: NextFunction) => { try { let { filename, path, pid } = req.body; + if (!path) { + path = ''; + } const { name, ext } = parse(filename); const filePath = join(config.scriptPath, path, `${name}.swap${ext}`); const logPath = join(config.logPath, path, `${name}.swap`); @@ -328,12 +368,14 @@ export default (app: Router) => { }), async (req: Request, res: Response, next: NextFunction) => { try { - let { filename, path, type, newFilename } = req.body as { + let { filename, path, newFilename } = req.body as { filename: string; path: string; - type: string; newFilename: string; }; + if (!path) { + path = ''; + } const filePath = join(config.scriptPath, path, filename); const newPath = join(config.scriptPath, path, newFilename); await fs.rename(filePath, newPath); From 53a96462b2827ceb49e5b82f32658458bf90e184 Mon Sep 17 00:00:00 2001 From: taozhiyu <49258735+taozhiyu@users.noreply.github.com> Date: Sun, 10 Aug 2025 18:35:05 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=84=9A=E6=9C=AC=20API?= =?UTF-8?q?=20=E8=B7=AF=E7=94=B1=E7=9A=84=E9=94=99=E8=AF=AF=E5=A4=84?= =?UTF-8?q?=E7=90=86=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- back/api/script.ts | 82 +++++++++++++++++++++++----------------------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/back/api/script.ts b/back/api/script.ts index 071026d1..ea682c63 100644 --- a/back/api/script.ts +++ b/back/api/script.ts @@ -32,46 +32,46 @@ export default (app: Router) => { }), }), async (req: Request, res: Response, next: NextFunction) => { - const logger: Logger = Container.get('logger'); - try { - let result: IFile[] = []; - const blacklist = [ - 'node_modules', - '.git', - '.pnpm', - 'pnpm-lock.yaml', - 'yarn.lock', - 'package-lock.json', - ]; - if (req.query.path) { - result = await readDir( - req.query.path as string, - config.scriptPath, - blacklist, - ); - } else { - result = await readDirs( - config.scriptPath, - config.scriptPath, - blacklist, - (a, b) => { - if (a.type === b.type) { - return a.title.localeCompare(b.title); - } else { - return a.type === 'directory' ? -1 : 1; - } - }, - ); + const logger: Logger = Container.get('logger'); + try { + let result: IFile[] = []; + const blacklist = [ + 'node_modules', + '.git', + '.pnpm', + 'pnpm-lock.yaml', + 'yarn.lock', + 'package-lock.json', + ]; + if (req.query.path) { + result = await readDir( + req.query.path as string, + config.scriptPath, + blacklist, + ); + } else { + result = await readDirs( + config.scriptPath, + config.scriptPath, + blacklist, + (a, b) => { + if (a.type === b.type) { + return a.title.localeCompare(b.title); + } else { + return a.type === 'directory' ? -1 : 1; + } + }, + ); + } + res.send({ + code: 200, + data: result, + }); + } catch (e) { + logger.error('🔥 error: %o', e); + return next(e); } - res.send({ - code: 200, - data: result, - }); - } catch (e) { - logger.error('🔥 error: %o', e); - return next(e); - } - }); + }); route.get( '/detail', @@ -109,8 +109,8 @@ export default (app: Router) => { try { const scriptService = Container.get(ScriptService); const content = await scriptService.getFile( - req.query.path as string, - req.params?.file || '', + req.query?.path as string || '', + req.params.file, ); res.send({ code: 200, data: content }); } catch (e) { From 719a32c191b10b9e4e956daf9d6f8ee05f3e99b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B6=9B=E4=B9=8B=E9=9B=A8?= <49258735+taozhiyu@users.noreply.github.com> Date: Tue, 26 Aug 2025 23:00:07 +0800 Subject: [PATCH 3/4] Fix optional path compatibility checks --- back/api/script.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/back/api/script.ts b/back/api/script.ts index ea682c63..5e05e95d 100644 --- a/back/api/script.ts +++ b/back/api/script.ts @@ -85,7 +85,7 @@ export default (app: Router) => { try { const scriptService = Container.get(ScriptService); const content = await scriptService.getFile( - req.query.path as string, + req.query?.path as string || '', req.query.file as string, ); res.send({ code: 200, data: content }); From a41b79ebc5be624f1c76edeae9ff162640ee8114 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B6=9B=E4=B9=8B=E9=9B=A8?= <49258735+taozhiyu@users.noreply.github.com> Date: Sat, 6 Sep 2025 15:13:15 +0800 Subject: [PATCH 4/4] remove file --- back/api/script.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/back/api/script.ts b/back/api/script.ts index 5e05e95d..419ace4a 100644 --- a/back/api/script.ts +++ b/back/api/script.ts @@ -125,7 +125,6 @@ export default (app: Router) => { celebrate({ body: Joi.object({ filename: Joi.string().required(), - file: Joi.string().optional().allow(''), path: Joi.string().optional().allow(''), content: Joi.string().optional().allow(''), originFilename: Joi.string().optional().allow(''),