Files
yaklang-chrome-extension/src/features/browser-crypto/model.test.ts
T

70 lines
3.0 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import type { BrowserRecordingEvent } from '@/types/models';
import {
cryptoDeepCaptureMatcher,
cryptoEventLabel,
isForwardCryptoEvent,
isReverseCryptoEvent,
normalizeBrowserRecordingCrypto,
} from './model';
function cryptoEvent(operation: string): BrowserRecordingEvent {
return {
id: 'crypto-1', sequence: 1, timestamp: 1, recordingId: 'recording-1', traceId: 'trace-1',
kind: 'crypto', operation, inputs: [], outputs: [], sensitiveCaptured: false,
wrapperHandleId: 'wrapper-jsencrypt-encrypt',
scriptUrl: 'https://example.test/app.js',
crypto: {
adapterId: 'jsencrypt', providerKind: 'library', family: 'asymmetric', operation,
algorithm: 'RSA', padding: 'PKCS1-v1_5', outputEncoding: 'base64',
state: { model: 'receiver', phase: 'one-shot' },
key: { kind: 'public', bits: 1024, fingerprint: 'v2:key' },
},
};
}
describe('browser crypto model', () => {
it('normalizes bounded JSEncrypt metadata without carrying key material', () => {
expect(normalizeBrowserRecordingCrypto({
adapterId: 'jsencrypt', providerKind: 'library', family: 'asymmetric', operation: 'encrypt', algorithm: 'RSA',
padding: 'PKCS1-v1_5', inputEncoding: 'utf8', outputEncoding: 'base64',
state: { model: 'receiver', phase: 'one-shot', internalState: 'must-not-survive' },
key: { kind: 'public', bits: 1024, fingerprint: 'v2:key', pem: 'must-not-survive' },
publicKey: 'must-not-survive',
})).toEqual({
adapterId: 'jsencrypt', providerKind: 'library', family: 'asymmetric', operation: 'encrypt', algorithm: 'RSA',
padding: 'PKCS1-v1_5', inputEncoding: 'utf8', outputEncoding: 'base64',
state: { model: 'receiver', phase: 'one-shot' },
key: { kind: 'public', bits: 1024, fingerprint: 'v2:key' },
});
});
it('accepts bounded adapter IDs while rejecting malformed adapter metadata', () => {
expect(normalizeBrowserRecordingCrypto({
adapterId: 'vendor-suite.v2', providerKind: 'library', family: 'asymmetric', operation: 'encrypt',
})?.adapterId).toBe('vendor-suite.v2');
expect(normalizeBrowserRecordingCrypto({
adapterId: '<img onerror=1>', providerKind: 'library', family: 'asymmetric', operation: 'encrypt',
})).toBeUndefined();
});
it('classifies forward and reverse RSA calls', () => {
expect(isForwardCryptoEvent(cryptoEvent('encrypt'))).toBe(true);
expect(isForwardCryptoEvent(cryptoEvent('decrypt'))).toBe(false);
expect(isReverseCryptoEvent(cryptoEvent('decrypt'))).toBe(true);
expect(isReverseCryptoEvent(cryptoEvent('verify'))).toBe(false);
});
it('uses adapter-aware labels and exact wrapper handles for deep capture', () => {
const event = cryptoEvent('encrypt');
expect(cryptoEventLabel(event)).toBe('JSEncrypt RSA');
expect(cryptoDeepCaptureMatcher(event)).toEqual({
kind: 'crypto',
adapterId: 'jsencrypt',
operation: 'encrypt',
wrapperHandleId: 'wrapper-jsencrypt-encrypt',
scriptUrl: 'https://example.test/app.js',
});
});
});