test(tess): cover streamed private key redaction
All checks were successful
ci/woodpecker/pr/ci Pipeline was successful
All checks were successful
ci/woodpecker/pr/ci Pipeline was successful
This commit is contained in:
@@ -94,6 +94,31 @@ describe('ChatGateway redaction boundary', (): void => {
|
||||
expect(JSON.stringify(client.emit.mock.calls)).not.toContain('canaryvalue123');
|
||||
});
|
||||
|
||||
it('holds a streamed private key until it can be redacted', (): void => {
|
||||
const { gateway } = buildGateway();
|
||||
const client = {
|
||||
connected: true,
|
||||
id: 'client-1',
|
||||
data: { user: { id: 'user-1' } },
|
||||
emit: vi.fn(),
|
||||
};
|
||||
|
||||
gateway.relayEvent(client, CONVERSATION_ID, {
|
||||
type: 'message_update',
|
||||
assistantMessageEvent: { type: 'text_delta', delta: '-----BEGIN PRIVATE KEY-----\ncanary' },
|
||||
});
|
||||
gateway.relayEvent(client, CONVERSATION_ID, {
|
||||
type: 'message_update',
|
||||
assistantMessageEvent: { type: 'text_delta', delta: '\n-----END PRIVATE KEY-----' },
|
||||
});
|
||||
|
||||
expect(client.emit).toHaveBeenCalledWith('agent:text', {
|
||||
conversationId: CONVERSATION_ID,
|
||||
text: '[REDACTED_SECRET]',
|
||||
});
|
||||
expect(JSON.stringify(client.emit.mock.calls)).not.toContain('canary');
|
||||
});
|
||||
|
||||
it('drops an oversized unterminated stream fragment rather than retaining it', (): void => {
|
||||
const { gateway } = buildGateway();
|
||||
const client = {
|
||||
|
||||
@@ -858,6 +858,17 @@ export class ChatGateway implements OnGatewayInit, OnGatewayConnection, OnGatewa
|
||||
);
|
||||
}
|
||||
|
||||
const privateKeyStart = content.lastIndexOf('-----BEGIN');
|
||||
if (privateKeyStart >= 0) {
|
||||
const privateKey = content.slice(privateKeyStart);
|
||||
if (/-----END(?: [A-Z]+)* KEY-----/.test(privateKey)) {
|
||||
// Release the complete block in one pass so the full-block classifier can redact it.
|
||||
retainedFrom = content.length;
|
||||
} else {
|
||||
retainedFrom = Math.min(retainedFrom, privateKeyStart);
|
||||
}
|
||||
}
|
||||
|
||||
return retainedFrom;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ const SECRET_PATTERNS: RegExp[] = [
|
||||
/\b(?:authorization\s*:\s*)?bearer\s+[A-Za-z0-9._~+/-]+=*/gi,
|
||||
/\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/g,
|
||||
/\b(?:AKIA|ASIA)[A-Z0-9]{16}\b/g,
|
||||
/-----BEGIN(?: [A-Z]+)* PRIVATE KEY-----[\s\S]*?-----END(?: [A-Z]+)* PRIVATE KEY-----/g,
|
||||
/-----BEGIN(?: [A-Z]+)* KEY-----[\s\S]*?-----END(?: [A-Z]+)* KEY-----/g,
|
||||
/https?:\/\/[^\s?#]+[^\s]*[?&](?:token|key|secret|signature|sig)=[^\s&#]+/gi,
|
||||
];
|
||||
const PII_PATTERNS: RegExp[] = [
|
||||
|
||||
Reference in New Issue
Block a user