76 lines
2.6 KiB
TypeScript
76 lines
2.6 KiB
TypeScript
/**
|
|
* v0.8.2 P1-5: 敏感值脱敏单源工具测试
|
|
*
|
|
* 锁定契约:
|
|
* - 键名归一化匹配(api_key / authKey / Authorization 等形态均命中)
|
|
* - 长值保留后 4 位、短值完全掩码
|
|
* - 嵌套对象/数组递归;非敏感字段原样保留
|
|
* - 循环引用与深度上限防护(防御恶意构造的工具参数)
|
|
* - 原对象不被修改
|
|
*/
|
|
|
|
import { describe, it, expect } from 'vitest';
|
|
import { deepMaskSensitive, maskSensitiveValue } from '../mask';
|
|
|
|
describe('maskSensitiveValue', () => {
|
|
it('长值保留后 4 位', () => {
|
|
expect(maskSensitiveValue('sk-abcdefgh12345678')).toBe('***5678');
|
|
});
|
|
it('短值(≤4)完全掩码', () => {
|
|
expect(maskSensitiveValue('abc')).toBe('***');
|
|
expect(maskSensitiveValue('abcd')).toBe('***');
|
|
});
|
|
});
|
|
|
|
describe('deepMaskSensitive', () => {
|
|
it('顶层与嵌套的敏感键均被掩码', () => {
|
|
const input = {
|
|
url: 'https://api.example.com',
|
|
headers: {
|
|
Authorization: 'Bearer sk-abcdefgh12345678',
|
|
'content-type': 'application/json',
|
|
},
|
|
api_key: 'sk-abcdefgh12345678',
|
|
nested: { authToken: 'token-1234567890' },
|
|
};
|
|
const out = deepMaskSensitive(input) as typeof input;
|
|
expect(out.url).toBe('https://api.example.com');
|
|
expect(out['api_key']).toBe('***5678');
|
|
expect((out.headers as Record<string, string>).Authorization).toBe('***5678');
|
|
expect((out.headers as Record<string, string>)['content-type']).toBe('application/json');
|
|
expect((out.nested as { authToken: string }).authToken).toBe('***7890');
|
|
});
|
|
|
|
it('数组内对象同样脱敏', () => {
|
|
const out = deepMaskSensitive([{ secret: 'supersecret-value-42' }, { ok: 1 }]) as Array<
|
|
Record<string, unknown>
|
|
>;
|
|
expect(out[0].secret).toBe('***e-42');
|
|
expect(out[1].ok).toBe(1);
|
|
});
|
|
|
|
it('原对象不被修改', () => {
|
|
const input = { api_key: 'sk-abcdefgh12345678' };
|
|
deepMaskSensitive(input);
|
|
expect(input.api_key).toBe('sk-abcdefgh12345678');
|
|
});
|
|
|
|
it('循环引用与深度上限不抛错', () => {
|
|
const a: Record<string, unknown> = { name: 'a' };
|
|
a.self = a;
|
|
const out = deepMaskSensitive(a) as Record<string, unknown>;
|
|
expect(out.name).toBe('a');
|
|
expect(out.self).toBe('[circular]');
|
|
|
|
const deep: Record<string, unknown> = { v: 0 };
|
|
let cur = deep;
|
|
for (let i = 0; i < 20; i++) {
|
|
cur.next = { v: i + 1 };
|
|
cur = cur.next as Record<string, unknown>;
|
|
}
|
|
const deepOut = deepMaskSensitive(deep) as Record<string, unknown>;
|
|
expect(deepOut.v).toBe(0);
|
|
expect(JSON.stringify(deepOut)).toContain('depth-limit');
|
|
});
|
|
});
|