Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
168 changes: 168 additions & 0 deletions src/utils/security/__tests__/phishingProtection.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,174 @@ describe('PhishingProtection', () => {
expect((PhishingProtection as any).levenshteinDistance('kitten', 'sitting')).toBe(3);
expect((PhishingProtection as any).levenshteinDistance('', 'abc')).toBe(3);
});

it('should handle two empty strings', () => {
expect((PhishingProtection as any).levenshteinDistance('', '')).toBe(0);
});

it('should handle one empty string', () => {
expect((PhishingProtection as any).levenshteinDistance('abc', '')).toBe(3);
});
});

describe('decodeTransactionData', () => {
it('should decode standard transaction data into method selector and params', () => {
const data = '0xa9059cbb00000000000000000000000012345678901234567890123456789012345678900000000000000000000000000000000000000000000000000000000000000001';
const result = (PhishingProtection as any).decodeTransactionData(data);

expect(result).toBeDefined();
expect(result.methodSelector).toBe('0xa9059cbb');
expect(result.params).toBe(data.slice(10));
expect(result.decoded).toBe(false);
});

it('should handle data with only method selector and no params', () => {
const data = '0xa9059cbb';
const result = (PhishingProtection as any).decodeTransactionData(data);

expect(result.methodSelector).toBe('0xa9059cbb');
expect(result.params).toBe('');
});

it('should handle empty data gracefully', () => {
const data = '';
const result = (PhishingProtection as any).decodeTransactionData(data);

expect(result.methodSelector).toBe('');
expect(result.params).toBe('');
});

it('should handle approvals (0x095ea7b3) correctly', () => {
const data = '0x095ea7b300000000000000000000000012345678901234567890123456789012345678900000000000000000000000000000000000000000000000000000000000000001';
const result = (PhishingProtection as any).decodeTransactionData(data);

expect(result.methodSelector).toBe('0x095ea7b3');
expect(result.params.length).toBeGreaterThan(0);
});

it('should handle withdraw method signature', () => {
const data = '0x2e1a7d4d0000000000000000000000000000000000000000000000000000000000000010';
const result = (PhishingProtection as any).decodeTransactionData(data);

expect(result.methodSelector).toBe('0x2e1a7d4d');
});
});

describe('analyzeTransactionParameters', () => {
it('should return no warnings for normal decoded data', () => {
const decodedData = {
methodSelector: '0xa9059cbb',
params: '0000000000000000000000001234567890123456789012345678901234567890',
decoded: false,
};
const result = (PhishingProtection as any).analyzeTransactionParameters(decodedData);

expect(result.isMalicious).toBe(false);
expect(result.warnings).toHaveLength(0);
});

it('should flag unusually large parameter data as malicious', () => {
const largeParams = '0'.repeat(1001);
const decodedData = {
methodSelector: '0xa9059cbb',
params: largeParams,
decoded: false,
};
const result = (PhishingProtection as any).analyzeTransactionParameters(decodedData);

expect(result.isMalicious).toBe(true);
expect(result.warnings).toContain('Unusually large parameter data');
});

it('should handle null or undefined decoded data', () => {
const resultNull = (PhishingProtection as any).analyzeTransactionParameters(null);
expect(resultNull.isMalicious).toBe(false);
expect(resultNull.warnings).toHaveLength(0);

const resultUndefined = (PhishingProtection as any).analyzeTransactionParameters(undefined);
expect(resultUndefined.isMalicious).toBe(false);
expect(resultUndefined.warnings).toHaveLength(0);
});

it('should handle decoded data without params property', () => {
const decodedData = {
methodSelector: '0xa9059cbb',
decoded: false,
};
const result = (PhishingProtection as any).analyzeTransactionParameters(decodedData);

expect(result.isMalicious).toBe(false);
expect(result.warnings).toHaveLength(0);
});

it('should handle decoded data with empty params', () => {
const decodedData = {
methodSelector: '0xa9059cbb',
params: '',
decoded: false,
};
const result = (PhishingProtection as any).analyzeTransactionParameters(decodedData);

expect(result.isMalicious).toBe(false);
expect(result.warnings).toHaveLength(0);
});

it('should correctly bound false positives at exactly 1000 chars (boundary)', () => {
const exactlyAtBoundary = '0'.repeat(1000);
const decodedData = {
methodSelector: '0xa9059cbb',
params: exactlyAtBoundary,
decoded: false,
};
const result = (PhishingProtection as any).analyzeTransactionParameters(decodedData);

// 1000 chars is NOT > 1000, so should not be flagged
expect(result.isMalicious).toBe(false);
});
});

describe('decodeTransactionData integration via validateTransactionData', () => {
it('should decode and analyze transaction data when validating', () => {
const largeParams = '0'.repeat(1001);
const largeData = `0xa9059cbb${largeParams}`;

const result = PhishingProtection.validateTransactionData(
'0x742d35Cc6634C0532925a3b8D4C9db96C4b4Db45',
largeData
);

// transfer function is not automatically malicious, but large params are
expect(result.isMalicious).toBe(true);
expect(result.warnings).toContain('Suspicious function call detected');
expect(result.warnings).toContain('Unusually large parameter data');
expect(result.decodedData).toBeDefined();
expect(result.decodedData.methodSelector).toBe('0xa9059cbb');
});

it('should not mark transfer as malicious without large params', () => {
const result = PhishingProtection.validateTransactionData(
'0x742d35Cc6634C0532925a3b8D4C9db96C4b4Db45',
'0xa9059cbb00000000000000000000000012345678901234567890123456789012345678900000000000000000000000000000000000000000000000000000000000000001'
);

// transfer (0xa9059cbb) is suspicious but not automatically malicious
expect(result.isValid).toBe(true);
expect(result.warnings).toContain('Suspicious function call detected');
expect(result.decodedData).toBeDefined();
});

it('should handle invalid hex data without crashing', () => {
// The try/catch in validateTransactionData catches the decode error
const result = PhishingProtection.validateTransactionData(
'0x742d35Cc6634C0532925a3b8D4C9db96C4b4Db45',
'not-hex-data'
);

expect(result.isValid).toBe(true);
expect(result.isMalicious).toBe(false);
// The decode happens inside the try block; if it throws, it should be caught
// and warnings will include 'Unable to decode transaction data'
});
});
});
});