From b767d64288e71dce5c3cc256d430ed8fd6204a00 Mon Sep 17 00:00:00 2001 From: Innei Date: Wed, 16 Mar 2022 22:05:13 +0800 Subject: [PATCH] refactor: replace eval to validate valid function with ast parse --- package.json | 2 ++ pnpm-lock.yaml | 4 ++++ src/modules/serverless/serverless.service.ts | 24 ++++++++++++++------ src/modules/snippet/snippet.service.ts | 4 ++++ 4 files changed, 27 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index b24d9b09..482b713d 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "@algolia/client-search": "*", "@babel/core": "7.17.7", "@babel/plugin-transform-typescript": "7.16.8", + "@babel/types": "*", "@innei/class-validator-jsonschema": "3.1.1", "@nestjs/common": "8.4.1", "@nestjs/core": "8.4.1", @@ -131,6 +132,7 @@ "zx": "4.3.0" }, "devDependencies": { + "@babel__types@*": "link:@types/@babel__types@*", "@innei-util/eslint-config-ts": "0.5.0", "@innei-util/prettier": "0.4.1", "@nestjs/cli": "8.2.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index eebce0cd..ece151e6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,6 +9,8 @@ specifiers: '@algolia/client-search': '*' '@babel/core': 7.17.7 '@babel/plugin-transform-typescript': 7.16.8 + '@babel/types': '*' + '@babel__types@*': link:@types/@babel__types@* '@innei-util/eslint-config-ts': 0.5.0 '@innei-util/prettier': 0.4.1 '@innei/class-validator-jsonschema': 3.1.1 @@ -123,6 +125,7 @@ dependencies: '@algolia/client-search': 4.12.1 '@babel/core': 7.17.7 '@babel/plugin-transform-typescript': 7.16.8_@babel+core@7.17.7 + '@babel/types': 7.17.0 '@innei/class-validator-jsonschema': 3.1.1_279461c9b8c03a205cdbc4878bc60cf8 '@nestjs/common': 8.4.1_add13df2cdecb4b62cd3f7664ea82e18 '@nestjs/core': 8.4.1_b4e0a43386936f36c413921c49aca7e6 @@ -192,6 +195,7 @@ optionalDependencies: redis-memory-server: 0.5.0 devDependencies: + '@babel__types@*': link:@types/@babel__types@* '@innei-util/eslint-config-ts': 0.5.0_typescript@4.6.2 '@innei-util/prettier': 0.4.1 '@nestjs/cli': 8.2.3 diff --git a/src/modules/serverless/serverless.service.ts b/src/modules/serverless/serverless.service.ts index 4cf0a99c..13c1c1c5 100644 --- a/src/modules/serverless/serverless.service.ts +++ b/src/modules/serverless/serverless.service.ts @@ -1,7 +1,8 @@ import fs, { mkdir, stat } from 'fs/promises' import path from 'path' import { nextTick } from 'process' -import { transformAsync } from '@babel/core' +import * as t from '@babel/types' +import { parseAsync, transformAsync } from '@babel/core' import { Injectable, InternalServerErrorException, @@ -293,6 +294,7 @@ export class ServerlessService { const trustPackagePrefixes = ['@innei/', '@mx-space/', 'mx-function-'] if ( + isDev || allowedThirdPartLibs.includes(id as any) || trustPackagePrefixes.some((prefix) => id.startsWith(prefix)) ) { @@ -337,13 +339,21 @@ export class ServerlessService { async isValidServerlessFunction(raw: string) { try { // 验证 handler 是否存在并且是函数 - return safeEval(` - ${await this.convertTypescriptCode(raw)} - return typeof handler === 'function' - `) + const ast = (await parseAsync(raw, { + plugins: [require('@babel/plugin-transform-typescript')], + })) as t.File + + const { body } = ast.program as t.Program + + const hasEntryFunction = body.some( + (node) => t.isFunction(node) && node.id && node.id.name === 'handler', + ) + return hasEntryFunction } catch (e) { - console.error(e.message) - return false + if (isDev) { + console.error(e.message) + } + return e.message?.split('\n').at(0) } } } diff --git a/src/modules/snippet/snippet.service.ts b/src/modules/snippet/snippet.service.ts index 17d01d7b..86c83b6e 100644 --- a/src/modules/snippet/snippet.service.ts +++ b/src/modules/snippet/snippet.service.ts @@ -80,6 +80,10 @@ export class SnippetService { const isValid = await this.serverlessService.isValidServerlessFunction( model.raw, ) + // if isValid is string, eq error message + if (typeof isValid === 'string') { + throw new BadRequestException(isValid) + } if (!isValid) { throw new BadRequestException('serverless function is not valid') }