mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-05-04 22:51:08 +00:00
add check for userPromptSubmit
This commit is contained in:
parent
cf0b67ef8e
commit
0c6b16c695
4 changed files with 151 additions and 4 deletions
|
|
@ -6,7 +6,10 @@
|
|||
|
||||
import type React from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import { escapeAnsiCtrlCodes } from '../utils/textUtils.js';
|
||||
import {
|
||||
escapeAnsiCtrlCodes,
|
||||
sanitizeSensitiveText,
|
||||
} from '../utils/textUtils.js';
|
||||
import type { HistoryItem } from '../types.js';
|
||||
import {
|
||||
UserMessage,
|
||||
|
|
@ -233,7 +236,7 @@ const HistoryItemDisplayComponent: React.FC<HistoryItemDisplayProps> = ({
|
|||
)}
|
||||
{itemForDisplay.type === 'user_prompt_submit_blocked' && (
|
||||
<ErrorMessage
|
||||
text={`UserPromptSubmit operation blocked by hook:\n${itemForDisplay.reason}\n\nOriginal prompt: ${itemForDisplay.originalPrompt}`}
|
||||
text={`UserPromptSubmit operation blocked by hook:\n${itemForDisplay.reason}\n\nOriginal prompt: ${sanitizeSensitiveText(itemForDisplay.originalPrompt)}`}
|
||||
/>
|
||||
)}
|
||||
{itemForDisplay.type === 'stop_hook_loop' && (
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import type {
|
|||
ToolCallConfirmationDetails,
|
||||
ToolEditConfirmationDetails,
|
||||
} from '@qwen-code/qwen-code-core';
|
||||
import { escapeAnsiCtrlCodes } from './textUtils.js';
|
||||
import { escapeAnsiCtrlCodes, sanitizeSensitiveText } from './textUtils.js';
|
||||
|
||||
describe('textUtils', () => {
|
||||
describe('escapeAnsiCtrlCodes', () => {
|
||||
|
|
@ -167,4 +167,71 @@ describe('textUtils', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('sanitizeSensitiveText', () => {
|
||||
it('should return text unchanged if no sensitive patterns', () => {
|
||||
const text = 'Hello, this is a normal prompt';
|
||||
expect(sanitizeSensitiveText(text)).toBe(text);
|
||||
});
|
||||
|
||||
it('should redact OpenAI-style API keys', () => {
|
||||
const text = 'Use API key sk-1234567890abcdefghijklmnopqrstuv for access';
|
||||
expect(sanitizeSensitiveText(text)).toBe(
|
||||
'Use API key sk-***REDACTED*** for access',
|
||||
);
|
||||
});
|
||||
|
||||
it('should redact api_key assignments', () => {
|
||||
const text = 'api_key=supersecretkey123456789012';
|
||||
expect(sanitizeSensitiveText(text)).toBe('api_key=***REDACTED***');
|
||||
});
|
||||
|
||||
it('should redact Bearer tokens', () => {
|
||||
const text = 'Authorization: Bearer abc123token456xyz';
|
||||
expect(sanitizeSensitiveText(text)).toBe(
|
||||
'Authorization: Bearer ***REDACTED***',
|
||||
);
|
||||
});
|
||||
|
||||
it('should redact password assignments', () => {
|
||||
const text = 'password=mysecretpassword123';
|
||||
expect(sanitizeSensitiveText(text)).toBe('password=***REDACTED***');
|
||||
});
|
||||
|
||||
it('should redact AWS access keys', () => {
|
||||
const text = 'AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE';
|
||||
expect(sanitizeSensitiveText(text)).toBe(
|
||||
'AWS_ACCESS_KEY_ID=***REDACTED***',
|
||||
);
|
||||
});
|
||||
|
||||
it('should truncate long text', () => {
|
||||
const text = 'a'.repeat(300);
|
||||
const result = sanitizeSensitiveText(text, 200);
|
||||
expect(result.length).toBe(200);
|
||||
expect(result.endsWith('...')).toBe(true);
|
||||
});
|
||||
|
||||
it('should handle custom max length', () => {
|
||||
const text =
|
||||
'This is a test prompt with sk-1234567890abcdefghijklmnopqrstuv';
|
||||
const result = sanitizeSensitiveText(text, 20);
|
||||
expect(result.length).toBe(20);
|
||||
expect(result).toBe('This is a test pr...');
|
||||
});
|
||||
|
||||
it('should handle empty string', () => {
|
||||
expect(sanitizeSensitiveText('')).toBe('');
|
||||
});
|
||||
|
||||
it('should redact multiple sensitive patterns', () => {
|
||||
const text =
|
||||
'api_key=secretkey12345678901234 and password=mypass123 and sk-test123456789012345678901';
|
||||
const result = sanitizeSensitiveText(text);
|
||||
expect(result).toContain('***REDACTED***');
|
||||
expect(result).not.toContain('secretkey12345678901234');
|
||||
expect(result).not.toContain('mypass123');
|
||||
expect(result).not.toContain('sk-test123456789012345678901');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -214,3 +214,77 @@ export function escapeAnsiCtrlCodes<T>(obj: T): T {
|
|||
|
||||
return newObj !== null ? newObj : obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Patterns that may indicate sensitive information like API keys, tokens, passwords.
|
||||
*/
|
||||
const SENSITIVE_PATTERNS: Array<{ pattern: RegExp; replacement: string }> = [
|
||||
// API keys with common prefixes
|
||||
{
|
||||
pattern: /(sk-[a-zA-Z0-9]{20,})/g,
|
||||
replacement: 'sk-***REDACTED***',
|
||||
},
|
||||
{
|
||||
pattern: /(api[_-]?key[_-]?[=:]\s*)[a-zA-Z0-9_-]{20,}/gi,
|
||||
replacement: '$1***REDACTED***',
|
||||
},
|
||||
// Bearer tokens
|
||||
{
|
||||
pattern: /(Bearer\s+)[a-zA-Z0-9._-]+/gi,
|
||||
replacement: '$1***REDACTED***',
|
||||
},
|
||||
// Generic tokens
|
||||
{
|
||||
pattern: /(token[_-]?[=:]\s*)[a-zA-Z0-9._-]{10,}/gi,
|
||||
replacement: '$1***REDACTED***',
|
||||
},
|
||||
// Passwords in connection strings or assignments
|
||||
{
|
||||
pattern: /(password[_-]?[=:]\s*)[^\s]+/gi,
|
||||
replacement: '$1***REDACTED***',
|
||||
},
|
||||
{
|
||||
pattern: /(pwd[_-]?[=:]\s*)[^\s]+/gi,
|
||||
replacement: '$1***REDACTED***',
|
||||
},
|
||||
// AWS keys
|
||||
{
|
||||
pattern: /(AKIA[A-Z0-9]{16})/g,
|
||||
replacement: '***REDACTED***',
|
||||
},
|
||||
// Generic secret patterns
|
||||
{
|
||||
pattern: /(secret[_-]?[=:]\s*)[a-zA-Z0-9._-]{10,}/gi,
|
||||
replacement: '$1***REDACTED***',
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Sanitizes text by redacting potentially sensitive information like API keys,
|
||||
* tokens, and passwords. Also truncates long text to a maximum length.
|
||||
*
|
||||
* @param text The text to sanitize
|
||||
* @param maxLength Maximum length of the output text (default: 200)
|
||||
* @returns Sanitized and truncated text
|
||||
*/
|
||||
export function sanitizeSensitiveText(
|
||||
text: string,
|
||||
maxLength: number = 200,
|
||||
): string {
|
||||
let result = text;
|
||||
|
||||
// Apply each sensitive pattern replacement
|
||||
for (const { pattern, replacement } of SENSITIVE_PATTERNS) {
|
||||
result = result.replace(pattern, replacement);
|
||||
}
|
||||
|
||||
// Truncate if too long
|
||||
if (result.length > maxLength) {
|
||||
if (maxLength <= 3) {
|
||||
return result.slice(0, maxLength);
|
||||
}
|
||||
return result.slice(0, maxLength - 3) + '...';
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue