From ed52f45ece315fe2e1d2a1386689adf3d3df7182 Mon Sep 17 00:00:00 2001 From: dsh <11198975+LucieFaire@users.noreply.github.com> Date: Wed, 3 Dec 2025 14:12:44 +0100 Subject: [PATCH 1/2] chore: log Blockaid scan responses Signed-off-by: dsh <11198975+LucieFaire@users.noreply.github.com> --- .../blockaid/blockaid-api.service.spec.ts | 42 ++++++++++++++++--- .../blockaid/blockaid-api.service.ts | 39 ++++++++++++++++- 2 files changed, 74 insertions(+), 7 deletions(-) diff --git a/src/modules/safe-shield/threat-analysis/blockaid/blockaid-api.service.spec.ts b/src/modules/safe-shield/threat-analysis/blockaid/blockaid-api.service.spec.ts index 10e4e2536f..90a0fc44b9 100644 --- a/src/modules/safe-shield/threat-analysis/blockaid/blockaid-api.service.spec.ts +++ b/src/modules/safe-shield/threat-analysis/blockaid/blockaid-api.service.spec.ts @@ -4,6 +4,7 @@ import type { TransactionScanResponse } from '@blockaid/client/resources/evm/evm import type { Address } from 'viem'; import { faker } from '@faker-js/faker'; import type Blockaid from '@blockaid/client'; +import type { ILoggingService } from '@/logging/logging.interface'; const createMockWithResponse = ( data: TransactionScanResponse, @@ -36,13 +37,17 @@ const mockBlockaidClient = { }, } as jest.MockedObjectDeep; +const mockLoggingService = { + debug: jest.fn(), +} as jest.MockedObjectDeep; + describe('BlockaidApi', () => { let service: BlockaidApi; beforeEach(() => { jest.resetAllMocks(); - service = new BlockaidApi(); + service = new BlockaidApi(mockLoggingService); // eslint-disable-next-line @typescript-eslint/no-explicit-any (service as any).blockaidClient = mockBlockaidClient; }); @@ -99,9 +104,14 @@ describe('BlockaidApi', () => { const request_id = faker.string.uuid(); const mockScanResponse: TransactionScanResponse = { - block: faker.string.numeric(), chain: `0x${chainId}`, - status: 'Success', + block: faker.string.numeric(), + validation: { + status: 'Success', + result_type: 'Benign', + description: 'No issues detected', + features: [], + }, } as TransactionScanResponse; mockBlockaidClient.evm.jsonRpc.scan.mockReturnValue( @@ -132,6 +142,20 @@ describe('BlockaidApi', () => { ...mockScanResponse, request_id, }); + expect(mockLoggingService.debug).toHaveBeenCalledWith({ + message: 'Blockaid scan response', + response: { + chain: mockScanResponse.chain, + request_id, + validation: { + status: mockScanResponse.validation?.status, + result_type: mockScanResponse.validation?.result_type, + description: mockScanResponse.validation?.description, + features: [], + }, + simulation: undefined, + }, + }); }); it('should call blockaid client without domain parameter/ with non_dapp', async () => { @@ -140,7 +164,6 @@ describe('BlockaidApi', () => { const mockScanResponse: TransactionScanResponse = { block: faker.string.numeric(), chain: `0x${chainId}`, - status: 'Success', } as TransactionScanResponse; mockBlockaidClient.evm.jsonRpc.scan.mockReturnValue( @@ -176,7 +199,6 @@ describe('BlockaidApi', () => { const mockScanResponse: TransactionScanResponse = { block: faker.string.numeric(), chain: `0x${chainId}`, - status: 'Success', } as TransactionScanResponse; mockBlockaidClient.evm.jsonRpc.scan.mockReturnValue( @@ -194,6 +216,16 @@ describe('BlockaidApi', () => { ...mockScanResponse, request_id: undefined, }); + + expect(mockLoggingService.debug).toHaveBeenCalledWith({ + message: 'Blockaid scan response', + response: { + chain: mockScanResponse.chain, + request_id: undefined, + validation: undefined, + simulation: undefined, + }, + }); }); it('should forward errors from blockaid client', async () => { diff --git a/src/modules/safe-shield/threat-analysis/blockaid/blockaid-api.service.ts b/src/modules/safe-shield/threat-analysis/blockaid/blockaid-api.service.ts index a36f2eba6e..402ce1aa18 100644 --- a/src/modules/safe-shield/threat-analysis/blockaid/blockaid-api.service.ts +++ b/src/modules/safe-shield/threat-analysis/blockaid/blockaid-api.service.ts @@ -6,14 +6,18 @@ import { ReportEvent } from '@/modules/safe-shield/entities/dtos/report-false-re import { BLOCKAID_REQUEST_ID_HEADER } from '@/modules/safe-shield/threat-analysis/blockaid/blockaid-api.constants'; import Blockaid from '@blockaid/client'; import { JsonRpcScanParams } from '@blockaid/client/resources/evm/json-rpc'; -import { Injectable } from '@nestjs/common'; +import { Inject, Injectable } from '@nestjs/common'; import { Address, numberToHex } from 'viem'; +import { ILoggingService, LoggingService } from '@/logging/logging.interface'; @Injectable() export class BlockaidApi implements IBlockaidApi { private readonly blockaidClient: Blockaid; - constructor() { + constructor( + @Inject(LoggingService) + private readonly loggingService: ILoggingService, + ) { this.blockaidClient = new Blockaid(); } @@ -54,6 +58,11 @@ export class BlockaidApi implements IBlockaidApi { const request_id = response.headers.get(BLOCKAID_REQUEST_ID_HEADER) ?? undefined; + this.loggingService.debug({ + message: 'Blockaid scan response', + response: this.sanitizeScanResponse({ ...data, request_id }), + }); + return { ...data, request_id }; } @@ -71,4 +80,30 @@ export class BlockaidApi implements IBlockaidApi { }, }); } + private sanitizeScanResponse( + response: TransactionScanResponseWithRequestId, + ): Record { + const { validation, simulation, chain, request_id } = response; + + return { + chain, + request_id, + validation: validation + ? { + status: validation.status, + result_type: validation.result_type, + description: validation.description, + features: validation.features?.map(({ type, feature_id }) => ({ + type, + feature_id, + })), + } + : undefined, + simulation: simulation + ? { + status: simulation.status, + } + : undefined, + }; + } } From 2d3f409c9921a5f4295ea70a5e3a2caa7750b40f Mon Sep 17 00:00:00 2001 From: dsh <11198975+LucieFaire@users.noreply.github.com> Date: Fri, 5 Dec 2025 13:55:15 +0100 Subject: [PATCH 2/2] use zod schema Signed-off-by: dsh <11198975+LucieFaire@users.noreply.github.com> --- .../blockaid/blockaid-api.service.spec.ts | 6 +-- .../blockaid/blockaid-api.service.ts | 39 +++++-------------- .../schemas/blockaid-scan-log.schema.ts | 28 +++++++++++++ 3 files changed, 41 insertions(+), 32 deletions(-) create mode 100644 src/modules/safe-shield/threat-analysis/blockaid/schemas/blockaid-scan-log.schema.ts diff --git a/src/modules/safe-shield/threat-analysis/blockaid/blockaid-api.service.spec.ts b/src/modules/safe-shield/threat-analysis/blockaid/blockaid-api.service.spec.ts index 90a0fc44b9..a77b755739 100644 --- a/src/modules/safe-shield/threat-analysis/blockaid/blockaid-api.service.spec.ts +++ b/src/modules/safe-shield/threat-analysis/blockaid/blockaid-api.service.spec.ts @@ -38,7 +38,7 @@ const mockBlockaidClient = { } as jest.MockedObjectDeep; const mockLoggingService = { - debug: jest.fn(), + info: jest.fn(), } as jest.MockedObjectDeep; describe('BlockaidApi', () => { @@ -142,7 +142,7 @@ describe('BlockaidApi', () => { ...mockScanResponse, request_id, }); - expect(mockLoggingService.debug).toHaveBeenCalledWith({ + expect(mockLoggingService.info).toHaveBeenCalledWith({ message: 'Blockaid scan response', response: { chain: mockScanResponse.chain, @@ -217,7 +217,7 @@ describe('BlockaidApi', () => { request_id: undefined, }); - expect(mockLoggingService.debug).toHaveBeenCalledWith({ + expect(mockLoggingService.info).toHaveBeenCalledWith({ message: 'Blockaid scan response', response: { chain: mockScanResponse.chain, diff --git a/src/modules/safe-shield/threat-analysis/blockaid/blockaid-api.service.ts b/src/modules/safe-shield/threat-analysis/blockaid/blockaid-api.service.ts index 402ce1aa18..da1515f777 100644 --- a/src/modules/safe-shield/threat-analysis/blockaid/blockaid-api.service.ts +++ b/src/modules/safe-shield/threat-analysis/blockaid/blockaid-api.service.ts @@ -9,6 +9,7 @@ import { JsonRpcScanParams } from '@blockaid/client/resources/evm/json-rpc'; import { Inject, Injectable } from '@nestjs/common'; import { Address, numberToHex } from 'viem'; import { ILoggingService, LoggingService } from '@/logging/logging.interface'; +import { BlockaidScanLogSchema } from '@/modules/safe-shield/threat-analysis/blockaid/schemas/blockaid-scan-log.schema'; @Injectable() export class BlockaidApi implements IBlockaidApi { @@ -58,11 +59,7 @@ export class BlockaidApi implements IBlockaidApi { const request_id = response.headers.get(BLOCKAID_REQUEST_ID_HEADER) ?? undefined; - this.loggingService.debug({ - message: 'Blockaid scan response', - response: this.sanitizeScanResponse({ ...data, request_id }), - }); - + this.logScanResponse({ ...data, request_id }); return { ...data, request_id }; } @@ -80,30 +77,14 @@ export class BlockaidApi implements IBlockaidApi { }, }); } - private sanitizeScanResponse( - response: TransactionScanResponseWithRequestId, - ): Record { - const { validation, simulation, chain, request_id } = response; - return { - chain, - request_id, - validation: validation - ? { - status: validation.status, - result_type: validation.result_type, - description: validation.description, - features: validation.features?.map(({ type, feature_id }) => ({ - type, - feature_id, - })), - } - : undefined, - simulation: simulation - ? { - status: simulation.status, - } - : undefined, - }; + private logScanResponse( + response: TransactionScanResponseWithRequestId, + ): void { + const logData = BlockaidScanLogSchema.parse({ ...response }); + this.loggingService.info({ + message: 'Blockaid scan response', + response: logData, + }); } } diff --git a/src/modules/safe-shield/threat-analysis/blockaid/schemas/blockaid-scan-log.schema.ts b/src/modules/safe-shield/threat-analysis/blockaid/schemas/blockaid-scan-log.schema.ts new file mode 100644 index 0000000000..a8440289bb --- /dev/null +++ b/src/modules/safe-shield/threat-analysis/blockaid/schemas/blockaid-scan-log.schema.ts @@ -0,0 +1,28 @@ +import { z } from 'zod'; + +export const BlockaidScanLogSchema = z.object({ + chain: z.string(), + request_id: z.string().optional(), + validation: z + .object({ + status: z.string(), + result_type: z.string(), + description: z.string().optional(), + features: z + .array( + z.object({ + type: z.string(), + feature_id: z.string(), + }), + ) + .optional(), + }) + .optional(), + simulation: z + .object({ + status: z.string(), + }) + .optional(), +}); + +export type BlockaidScanLog = z.infer;