import { registerDecorator, type ValidationOptions, type ValidationArguments } from "class-validator"; import { findDisallowedInlineFunctionCalls } from "@b2bcall/telephony"; /** * Bloqueia `${nome(args)}` que não esteja no allowlist de APIs seguras * (`ALLOWED_INLINE_API_FUNCTIONS`) — achado real de segurança (secao 180): * o allowlist de `application` (set/export/playback/...) não impedia RCE * via `${system(...)}`/`${bg_system(...)}` embutido no `data` de uma * application já permitida (`mod_commands` está carregado no FreeSWITCH * desta implantação). `${variavel}` sem parênteses nunca é bloqueado. */ export function IsSafeDialplanData(validationOptions?: ValidationOptions) { return function (object: object, propertyName: string) { registerDecorator({ name: "isSafeDialplanData", target: object.constructor, propertyName, options: validationOptions, validator: { validate(value: unknown) { if (typeof value !== "string") return true; // @IsString() já cobre o tipo return findDisallowedInlineFunctionCalls(value).length === 0; }, defaultMessage(args: ValidationArguments) { const disallowed = findDisallowedInlineFunctionCalls(String(args.value)); return `data usa função não permitida: ${disallowed.join(", ")} (nunca system/bg_system/curl/db/lua/shell — RCE no FreeSWITCH)`; }, }, }); }; }