@defai.digital/ax-cli 3.5.2 → 3.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.ax-cli/memory.json +8 -8
- package/README.md +27 -1
- package/dist/agent/chat-history-manager.d.ts +56 -0
- package/dist/agent/chat-history-manager.js +150 -0
- package/dist/agent/chat-history-manager.js.map +1 -0
- package/dist/agent/llm-agent.js +1 -1
- package/dist/agent/llm-agent.js.map +1 -1
- package/dist/agent/tool-manager.d.ts +39 -0
- package/dist/agent/tool-manager.js +76 -0
- package/dist/agent/tool-manager.js.map +1 -0
- package/dist/analyzers/ast/index.d.ts +9 -0
- package/dist/analyzers/ast/index.js +10 -0
- package/dist/analyzers/ast/index.js.map +1 -0
- package/dist/analyzers/ast/node-helpers.d.ts +81 -0
- package/dist/analyzers/ast/node-helpers.js +128 -0
- package/dist/analyzers/ast/node-helpers.js.map +1 -0
- package/dist/analyzers/ast/traverser.d.ts +67 -0
- package/dist/analyzers/ast/traverser.js +156 -0
- package/dist/analyzers/ast/traverser.js.map +1 -0
- package/dist/analyzers/best-practices/index.d.ts +10 -0
- package/dist/analyzers/best-practices/index.js +11 -0
- package/dist/analyzers/best-practices/index.js.map +1 -0
- package/dist/commands/setup.js +13 -5
- package/dist/commands/setup.js.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/llm/client.d.ts +1 -0
- package/dist/llm/client.js +44 -0
- package/dist/llm/client.js.map +1 -1
- package/dist/mcp/ssrf-protection.d.ts +86 -0
- package/dist/mcp/ssrf-protection.js +313 -0
- package/dist/mcp/ssrf-protection.js.map +1 -0
- package/dist/mcp/validation.d.ts +4 -0
- package/dist/mcp/validation.js +122 -11
- package/dist/mcp/validation.js.map +1 -1
- package/dist/schemas/settings-schemas.d.ts +30 -0
- package/dist/schemas/settings-schemas.js +30 -0
- package/dist/schemas/settings-schemas.js.map +1 -1
- package/dist/tools/bash.d.ts +3 -2
- package/dist/tools/bash.js +31 -2
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/search.d.ts +1 -1
- package/dist/tools/search.js +121 -128
- package/dist/tools/search.js.map +1 -1
- package/dist/tools/text-editor.js +52 -15
- package/dist/tools/text-editor.js.map +1 -1
- package/dist/ui/components/status-bar.js +2 -2
- package/dist/ui/components/status-bar.js.map +1 -1
- package/dist/ui/components/toast-notification.js +0 -1
- package/dist/ui/components/toast-notification.js.map +1 -1
- package/dist/utils/audit-logger.d.ts +247 -0
- package/dist/utils/audit-logger.js +374 -0
- package/dist/utils/audit-logger.js.map +1 -0
- package/dist/utils/command-security.d.ts +85 -0
- package/dist/utils/command-security.js +200 -0
- package/dist/utils/command-security.js.map +1 -0
- package/dist/utils/encryption.d.ts +78 -0
- package/dist/utils/encryption.js +216 -0
- package/dist/utils/encryption.js.map +1 -0
- package/dist/utils/error-sanitizer.d.ts +119 -0
- package/dist/utils/error-sanitizer.js +253 -0
- package/dist/utils/error-sanitizer.js.map +1 -0
- package/dist/utils/input-sanitizer.d.ts +210 -0
- package/dist/utils/input-sanitizer.js +362 -0
- package/dist/utils/input-sanitizer.js.map +1 -0
- package/dist/utils/json-utils.d.ts +13 -0
- package/dist/utils/json-utils.js +55 -1
- package/dist/utils/json-utils.js.map +1 -1
- package/dist/utils/parallel-analyzer.js +29 -12
- package/dist/utils/parallel-analyzer.js.map +1 -1
- package/dist/utils/path-security.d.ts +90 -0
- package/dist/utils/path-security.js +328 -0
- package/dist/utils/path-security.js.map +1 -0
- package/dist/utils/process-pool.d.ts +105 -0
- package/dist/utils/process-pool.js +326 -0
- package/dist/utils/process-pool.js.map +1 -0
- package/dist/utils/rate-limiter.d.ts +207 -0
- package/dist/utils/rate-limiter.js +303 -0
- package/dist/utils/rate-limiter.js.map +1 -0
- package/dist/utils/settings-manager.js +83 -4
- package/dist/utils/settings-manager.js.map +1 -1
- package/eslint.config.js +3 -0
- package/package.json +1 -1
- package/.ax-cli/checkpoints/2025-11-20/checkpoint-11e9e0ba-c39d-4fd2-aa77-bc818811c921.json +0 -69
- package/.ax-cli/checkpoints/2025-11-20/checkpoint-2b260b98-b418-4c7c-9694-e2b94967e662.json +0 -24
- package/.ax-cli/checkpoints/2025-11-20/checkpoint-7e03601e-e8ab-4cd7-9841-a74b66adf78f.json +0 -69
- package/.ax-cli/checkpoints/2025-11-20/checkpoint-7f9c6562-771f-4fd0-adcf-9e7e9ac34ae8.json +0 -44
- package/.ax-cli/checkpoints/2025-11-20/checkpoint-e1ebe666-4c3a-4367-ba5c-27fe512a9c70.json +0 -24
- package/.ax-cli/checkpoints/2025-11-21/checkpoint-15743e7d-430c-4d76-b6fc-955d7a5c250c.json +0 -44
- package/.ax-cli/checkpoints/2025-11-21/checkpoint-25cf7679-0b3f-4988-83d7-704548fbba91.json +0 -69
- package/.ax-cli/checkpoints/2025-11-21/checkpoint-54aedbac-6db0-464e-8ebb-dbb3979e6dca.json +0 -24
- package/.ax-cli/checkpoints/2025-11-21/checkpoint-7658aed8-fe5d-4222-903f-1a7c63717ea7.json +0 -24
- package/.ax-cli/checkpoints/2025-11-21/checkpoint-c9c13497-40dc-4294-a327-6a5fc854eaa1.json +0 -69
- package/ax.config.json +0 -333
- package/dist/hooks/use-chat-reducer.d.ts +0 -61
- package/dist/hooks/use-chat-reducer.js +0 -118
- package/dist/hooks/use-chat-reducer.js.map +0 -1
- package/dist/hooks/use-enhanced-input.d.ts +0 -40
- package/dist/hooks/use-enhanced-input.js +0 -249
- package/dist/hooks/use-enhanced-input.js.map +0 -1
- package/dist/hooks/use-input-handler.d.ts +0 -46
- package/dist/hooks/use-input-handler.js +0 -1430
- package/dist/hooks/use-input-handler.js.map +0 -1
- package/dist/hooks/use-input-history.d.ts +0 -9
- package/dist/hooks/use-input-history.js +0 -112
- package/dist/hooks/use-input-history.js.map +0 -1
- package/dist/utils/paste-collapse.d.ts +0 -46
- package/dist/utils/paste-collapse.js +0 -77
- package/dist/utils/paste-collapse.js.map +0 -1
- package/packages/schemas/dist/index.d.ts +0 -14
- package/packages/schemas/dist/index.d.ts.map +0 -1
- package/packages/schemas/dist/index.js +0 -19
- package/packages/schemas/dist/index.js.map +0 -1
- package/packages/schemas/dist/public/core/brand-types.d.ts +0 -308
- package/packages/schemas/dist/public/core/brand-types.d.ts.map +0 -1
- package/packages/schemas/dist/public/core/brand-types.js +0 -243
- package/packages/schemas/dist/public/core/brand-types.js.map +0 -1
- package/packages/schemas/dist/public/core/enums.d.ts +0 -227
- package/packages/schemas/dist/public/core/enums.d.ts.map +0 -1
- package/packages/schemas/dist/public/core/enums.js +0 -222
- package/packages/schemas/dist/public/core/enums.js.map +0 -1
- package/packages/schemas/dist/public/core/id-types.d.ts +0 -286
- package/packages/schemas/dist/public/core/id-types.d.ts.map +0 -1
- package/packages/schemas/dist/public/core/id-types.js +0 -136
- package/packages/schemas/dist/public/core/id-types.js.map +0 -1
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API Key Encryption Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides secure encryption/decryption for API keys stored in configuration
|
|
5
|
+
* files (REQ-SEC-003).
|
|
6
|
+
*
|
|
7
|
+
* Uses Node.js crypto module with:
|
|
8
|
+
* - AES-256-GCM for encryption (authenticated encryption)
|
|
9
|
+
* - PBKDF2 for key derivation from machine-specific identifier
|
|
10
|
+
* - Random IV for each encryption
|
|
11
|
+
* - Authentication tag verification
|
|
12
|
+
*
|
|
13
|
+
* @module encryption
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Encrypted value format
|
|
17
|
+
*/
|
|
18
|
+
export interface EncryptedValue {
|
|
19
|
+
encrypted: string;
|
|
20
|
+
iv: string;
|
|
21
|
+
tag: string;
|
|
22
|
+
version: number;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Encrypt a string value (typically an API key).
|
|
26
|
+
*
|
|
27
|
+
* @param plaintext - The value to encrypt
|
|
28
|
+
* @returns Encrypted value object with iv, tag, and encrypted data
|
|
29
|
+
*/
|
|
30
|
+
export declare function encrypt(plaintext: string): EncryptedValue;
|
|
31
|
+
/**
|
|
32
|
+
* Decrypt an encrypted value.
|
|
33
|
+
*
|
|
34
|
+
* @param encryptedValue - The encrypted value object
|
|
35
|
+
* @returns Decrypted plaintext
|
|
36
|
+
* @throws Error if decryption fails (wrong machine, corrupted data, etc.)
|
|
37
|
+
*/
|
|
38
|
+
export declare function decrypt(encryptedValue: EncryptedValue): string;
|
|
39
|
+
/**
|
|
40
|
+
* Check if a value is encrypted (has the expected structure).
|
|
41
|
+
*
|
|
42
|
+
* @param value - Value to check
|
|
43
|
+
* @returns True if value appears to be encrypted
|
|
44
|
+
*/
|
|
45
|
+
export declare function isEncrypted(value: unknown): value is EncryptedValue;
|
|
46
|
+
/**
|
|
47
|
+
* Encrypt an object's sensitive fields.
|
|
48
|
+
*
|
|
49
|
+
* @param obj - Object containing sensitive fields
|
|
50
|
+
* @param fieldsToEncrypt - Array of field names to encrypt
|
|
51
|
+
* @returns New object with encrypted fields
|
|
52
|
+
*/
|
|
53
|
+
export declare function encryptFields<T extends Record<string, any>>(obj: T, fieldsToEncrypt: string[]): T;
|
|
54
|
+
/**
|
|
55
|
+
* Decrypt an object's encrypted fields.
|
|
56
|
+
*
|
|
57
|
+
* @param obj - Object containing encrypted fields
|
|
58
|
+
* @param fieldsToDecrypt - Array of field names to decrypt
|
|
59
|
+
* @returns New object with decrypted fields
|
|
60
|
+
*/
|
|
61
|
+
export declare function decryptFields<T extends Record<string, any>>(obj: T, fieldsToDecrypt: string[]): T;
|
|
62
|
+
/**
|
|
63
|
+
* Test if encryption is working (for diagnostics).
|
|
64
|
+
*
|
|
65
|
+
* @returns True if encryption/decryption round-trip works
|
|
66
|
+
*/
|
|
67
|
+
export declare function testEncryption(): boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Get encryption info for diagnostics.
|
|
70
|
+
*/
|
|
71
|
+
export declare function getEncryptionInfo(): {
|
|
72
|
+
algorithm: string;
|
|
73
|
+
keyLength: number;
|
|
74
|
+
ivLength: number;
|
|
75
|
+
pbkdf2Iterations: number;
|
|
76
|
+
version: number;
|
|
77
|
+
machineId: string;
|
|
78
|
+
};
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API Key Encryption Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides secure encryption/decryption for API keys stored in configuration
|
|
5
|
+
* files (REQ-SEC-003).
|
|
6
|
+
*
|
|
7
|
+
* Uses Node.js crypto module with:
|
|
8
|
+
* - AES-256-GCM for encryption (authenticated encryption)
|
|
9
|
+
* - PBKDF2 for key derivation from machine-specific identifier
|
|
10
|
+
* - Random IV for each encryption
|
|
11
|
+
* - Authentication tag verification
|
|
12
|
+
*
|
|
13
|
+
* @module encryption
|
|
14
|
+
*/
|
|
15
|
+
import crypto from 'crypto';
|
|
16
|
+
import os from 'os';
|
|
17
|
+
/**
|
|
18
|
+
* Encryption configuration
|
|
19
|
+
*/
|
|
20
|
+
const ENCRYPTION_CONFIG = {
|
|
21
|
+
algorithm: 'aes-256-gcm',
|
|
22
|
+
keyLength: 32, // 256 bits
|
|
23
|
+
ivLength: 16, // 128 bits
|
|
24
|
+
saltLength: 32, // 256 bits
|
|
25
|
+
tagLength: 16, // 128 bits
|
|
26
|
+
pbkdf2Iterations: 100000, // OWASP recommendation
|
|
27
|
+
version: 1,
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Get a machine-specific identifier for key derivation.
|
|
31
|
+
* Uses hostname + platform + arch to create a unique-per-machine string.
|
|
32
|
+
*
|
|
33
|
+
* Note: This is not cryptographically strong protection (attacker with file
|
|
34
|
+
* access can derive the key), but it prevents casual browsing of config files
|
|
35
|
+
* and provides defense in depth.
|
|
36
|
+
*/
|
|
37
|
+
function getMachineIdentifier() {
|
|
38
|
+
const hostname = os.hostname();
|
|
39
|
+
const platform = os.platform();
|
|
40
|
+
const arch = os.arch();
|
|
41
|
+
// Combine machine-specific data
|
|
42
|
+
return `${hostname}-${platform}-${arch}`;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Derive an encryption key from machine identifier and salt using PBKDF2.
|
|
46
|
+
*
|
|
47
|
+
* @param salt - Salt for key derivation
|
|
48
|
+
* @returns Derived encryption key
|
|
49
|
+
*/
|
|
50
|
+
function deriveKey(salt) {
|
|
51
|
+
const machineId = getMachineIdentifier();
|
|
52
|
+
return crypto.pbkdf2Sync(machineId, salt, ENCRYPTION_CONFIG.pbkdf2Iterations, ENCRYPTION_CONFIG.keyLength, 'sha256');
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Encrypt a string value (typically an API key).
|
|
56
|
+
*
|
|
57
|
+
* @param plaintext - The value to encrypt
|
|
58
|
+
* @returns Encrypted value object with iv, tag, and encrypted data
|
|
59
|
+
*/
|
|
60
|
+
export function encrypt(plaintext) {
|
|
61
|
+
// Generate random salt and IV
|
|
62
|
+
const salt = crypto.randomBytes(ENCRYPTION_CONFIG.saltLength);
|
|
63
|
+
const iv = crypto.randomBytes(ENCRYPTION_CONFIG.ivLength);
|
|
64
|
+
// Derive key from machine identifier
|
|
65
|
+
const key = deriveKey(salt);
|
|
66
|
+
// Create cipher
|
|
67
|
+
const cipher = crypto.createCipheriv(ENCRYPTION_CONFIG.algorithm, key, iv);
|
|
68
|
+
// Encrypt the plaintext
|
|
69
|
+
let encrypted = cipher.update(plaintext, 'utf8', 'base64');
|
|
70
|
+
encrypted += cipher.final('base64');
|
|
71
|
+
// Get authentication tag
|
|
72
|
+
const tag = cipher.getAuthTag();
|
|
73
|
+
// Return encrypted value with metadata
|
|
74
|
+
// Store salt in the IV field for simplicity (both are public)
|
|
75
|
+
const saltAndIv = Buffer.concat([salt, iv]);
|
|
76
|
+
return {
|
|
77
|
+
encrypted,
|
|
78
|
+
iv: saltAndIv.toString('base64'),
|
|
79
|
+
tag: tag.toString('base64'),
|
|
80
|
+
version: ENCRYPTION_CONFIG.version,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Decrypt an encrypted value.
|
|
85
|
+
*
|
|
86
|
+
* @param encryptedValue - The encrypted value object
|
|
87
|
+
* @returns Decrypted plaintext
|
|
88
|
+
* @throws Error if decryption fails (wrong machine, corrupted data, etc.)
|
|
89
|
+
*/
|
|
90
|
+
export function decrypt(encryptedValue) {
|
|
91
|
+
try {
|
|
92
|
+
// Check version
|
|
93
|
+
if (encryptedValue.version !== ENCRYPTION_CONFIG.version) {
|
|
94
|
+
throw new Error(`Unsupported encryption version: ${encryptedValue.version}`);
|
|
95
|
+
}
|
|
96
|
+
// Extract salt and IV
|
|
97
|
+
const saltAndIv = Buffer.from(encryptedValue.iv, 'base64');
|
|
98
|
+
if (saltAndIv.length !== ENCRYPTION_CONFIG.saltLength + ENCRYPTION_CONFIG.ivLength) {
|
|
99
|
+
throw new Error('Invalid encrypted data: incorrect salt/IV length');
|
|
100
|
+
}
|
|
101
|
+
const salt = saltAndIv.subarray(0, ENCRYPTION_CONFIG.saltLength);
|
|
102
|
+
const iv = saltAndIv.subarray(ENCRYPTION_CONFIG.saltLength);
|
|
103
|
+
// Derive key from machine identifier
|
|
104
|
+
const key = deriveKey(salt);
|
|
105
|
+
// Create decipher
|
|
106
|
+
const decipher = crypto.createDecipheriv(ENCRYPTION_CONFIG.algorithm, key, iv);
|
|
107
|
+
// Set authentication tag
|
|
108
|
+
const tag = Buffer.from(encryptedValue.tag, 'base64');
|
|
109
|
+
decipher.setAuthTag(tag);
|
|
110
|
+
// Decrypt
|
|
111
|
+
let decrypted = decipher.update(encryptedValue.encrypted, 'base64', 'utf8');
|
|
112
|
+
decrypted += decipher.final('utf8');
|
|
113
|
+
return decrypted;
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
// Provide a user-friendly error message
|
|
117
|
+
if (error instanceof Error) {
|
|
118
|
+
if (error.message.includes('Unsupported state or unable to authenticate data')) {
|
|
119
|
+
throw new Error('Failed to decrypt API key. This may be due to: ' +
|
|
120
|
+
'(1) moving config to a different machine, ' +
|
|
121
|
+
'(2) corrupted config file, or ' +
|
|
122
|
+
'(3) config file was manually edited. ' +
|
|
123
|
+
'Please re-enter your API key.');
|
|
124
|
+
}
|
|
125
|
+
throw new Error(`Decryption failed: ${error.message}`);
|
|
126
|
+
}
|
|
127
|
+
throw new Error('Decryption failed: Unknown error');
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Check if a value is encrypted (has the expected structure).
|
|
132
|
+
*
|
|
133
|
+
* @param value - Value to check
|
|
134
|
+
* @returns True if value appears to be encrypted
|
|
135
|
+
*/
|
|
136
|
+
export function isEncrypted(value) {
|
|
137
|
+
if (typeof value !== 'object' || value === null) {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
const obj = value;
|
|
141
|
+
return (typeof obj.encrypted === 'string' &&
|
|
142
|
+
typeof obj.iv === 'string' &&
|
|
143
|
+
typeof obj.tag === 'string' &&
|
|
144
|
+
typeof obj.version === 'number');
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Encrypt an object's sensitive fields.
|
|
148
|
+
*
|
|
149
|
+
* @param obj - Object containing sensitive fields
|
|
150
|
+
* @param fieldsToEncrypt - Array of field names to encrypt
|
|
151
|
+
* @returns New object with encrypted fields
|
|
152
|
+
*/
|
|
153
|
+
export function encryptFields(obj, fieldsToEncrypt) {
|
|
154
|
+
const result = { ...obj };
|
|
155
|
+
for (const field of fieldsToEncrypt) {
|
|
156
|
+
if (field in result && typeof result[field] === 'string') {
|
|
157
|
+
// Don't re-encrypt already encrypted values
|
|
158
|
+
if (!isEncrypted(result[field])) {
|
|
159
|
+
result[field] = encrypt(result[field]);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return result;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Decrypt an object's encrypted fields.
|
|
167
|
+
*
|
|
168
|
+
* @param obj - Object containing encrypted fields
|
|
169
|
+
* @param fieldsToDecrypt - Array of field names to decrypt
|
|
170
|
+
* @returns New object with decrypted fields
|
|
171
|
+
*/
|
|
172
|
+
export function decryptFields(obj, fieldsToDecrypt) {
|
|
173
|
+
const result = { ...obj };
|
|
174
|
+
for (const field of fieldsToDecrypt) {
|
|
175
|
+
if (field in result && isEncrypted(result[field])) {
|
|
176
|
+
try {
|
|
177
|
+
result[field] = decrypt(result[field]);
|
|
178
|
+
}
|
|
179
|
+
catch (error) {
|
|
180
|
+
// If decryption fails, leave the field as-is and let caller handle it
|
|
181
|
+
console.error(`Failed to decrypt field "${field}":`, error instanceof Error ? error.message : String(error));
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return result;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Test if encryption is working (for diagnostics).
|
|
189
|
+
*
|
|
190
|
+
* @returns True if encryption/decryption round-trip works
|
|
191
|
+
*/
|
|
192
|
+
export function testEncryption() {
|
|
193
|
+
try {
|
|
194
|
+
const testValue = 'test-api-key-12345';
|
|
195
|
+
const encrypted = encrypt(testValue);
|
|
196
|
+
const decrypted = decrypt(encrypted);
|
|
197
|
+
return decrypted === testValue;
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Get encryption info for diagnostics.
|
|
205
|
+
*/
|
|
206
|
+
export function getEncryptionInfo() {
|
|
207
|
+
return {
|
|
208
|
+
algorithm: ENCRYPTION_CONFIG.algorithm,
|
|
209
|
+
keyLength: ENCRYPTION_CONFIG.keyLength,
|
|
210
|
+
ivLength: ENCRYPTION_CONFIG.ivLength,
|
|
211
|
+
pbkdf2Iterations: ENCRYPTION_CONFIG.pbkdf2Iterations,
|
|
212
|
+
version: ENCRYPTION_CONFIG.version,
|
|
213
|
+
machineId: getMachineIdentifier(),
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
//# sourceMappingURL=encryption.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encryption.js","sourceRoot":"","sources":["../../src/utils/encryption.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error Message Sanitization (REQ-SEC-010)
|
|
3
|
+
*
|
|
4
|
+
* Sanitizes error messages to prevent information disclosure
|
|
5
|
+
* Removes:
|
|
6
|
+
* - File system paths
|
|
7
|
+
* - API keys and secrets
|
|
8
|
+
* - Stack traces (for user-facing errors)
|
|
9
|
+
* - Internal implementation details
|
|
10
|
+
*
|
|
11
|
+
* Security: CVSS 6.5 (Medium Priority)
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Sanitized error structure
|
|
15
|
+
*/
|
|
16
|
+
export interface SanitizedError {
|
|
17
|
+
/**
|
|
18
|
+
* Sanitized error message (safe for user display)
|
|
19
|
+
*/
|
|
20
|
+
message: string;
|
|
21
|
+
/**
|
|
22
|
+
* Error code (for documentation lookup)
|
|
23
|
+
*/
|
|
24
|
+
code?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Generic error category
|
|
27
|
+
*/
|
|
28
|
+
category: string;
|
|
29
|
+
/**
|
|
30
|
+
* Suggested action for user
|
|
31
|
+
*/
|
|
32
|
+
suggestion?: string;
|
|
33
|
+
/**
|
|
34
|
+
* Original error (for internal logging only)
|
|
35
|
+
*/
|
|
36
|
+
originalError?: Error;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Error categories for user-friendly messages
|
|
40
|
+
*/
|
|
41
|
+
export declare enum ErrorCategory {
|
|
42
|
+
NETWORK = "NETWORK",
|
|
43
|
+
FILE_SYSTEM = "FILE_SYSTEM",
|
|
44
|
+
VALIDATION = "VALIDATION",
|
|
45
|
+
AUTHENTICATION = "AUTHENTICATION",
|
|
46
|
+
RATE_LIMIT = "RATE_LIMIT",
|
|
47
|
+
API_ERROR = "API_ERROR",
|
|
48
|
+
INTERNAL = "INTERNAL",
|
|
49
|
+
USER_INPUT = "USER_INPUT"
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Sanitize error message by removing sensitive information
|
|
53
|
+
*
|
|
54
|
+
* @param message - Raw error message
|
|
55
|
+
* @returns Sanitized message safe for user display
|
|
56
|
+
*/
|
|
57
|
+
export declare function sanitizeErrorMessage(message: string): string;
|
|
58
|
+
/**
|
|
59
|
+
* Sanitize stack trace by removing sensitive paths
|
|
60
|
+
*
|
|
61
|
+
* @param stack - Raw stack trace
|
|
62
|
+
* @returns Sanitized stack trace
|
|
63
|
+
*/
|
|
64
|
+
export declare function sanitizeStackTrace(stack: string): string;
|
|
65
|
+
/**
|
|
66
|
+
* Remove stack trace entirely (for user-facing errors)
|
|
67
|
+
*
|
|
68
|
+
* @param message - Error message with potential stack trace
|
|
69
|
+
* @returns Message without stack trace
|
|
70
|
+
*/
|
|
71
|
+
export declare function removeStackTrace(message: string): string;
|
|
72
|
+
/**
|
|
73
|
+
* Categorize error and create user-friendly message
|
|
74
|
+
*
|
|
75
|
+
* @param error - Error object
|
|
76
|
+
* @returns Sanitized error with category and suggestion
|
|
77
|
+
*/
|
|
78
|
+
export declare function sanitizeError(error: Error | unknown): SanitizedError;
|
|
79
|
+
/**
|
|
80
|
+
* Format sanitized error for user display
|
|
81
|
+
*
|
|
82
|
+
* @param sanitizedError - Sanitized error object
|
|
83
|
+
* @returns Formatted error message
|
|
84
|
+
*/
|
|
85
|
+
export declare function formatUserError(sanitizedError: SanitizedError): string;
|
|
86
|
+
/**
|
|
87
|
+
* Create internal log message with full details (not sanitized)
|
|
88
|
+
*
|
|
89
|
+
* @param error - Original error
|
|
90
|
+
* @param context - Additional context
|
|
91
|
+
* @returns Detailed log message
|
|
92
|
+
*/
|
|
93
|
+
export declare function createInternalLogMessage(error: Error | unknown, context?: Record<string, unknown>): string;
|
|
94
|
+
/**
|
|
95
|
+
* Safe error wrapper for user-facing operations
|
|
96
|
+
*
|
|
97
|
+
* @param operation - Async operation to execute
|
|
98
|
+
* @param errorHandler - Optional custom error handler
|
|
99
|
+
* @returns Result or sanitized error
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ```typescript
|
|
103
|
+
* const result = await safeExecute(
|
|
104
|
+
* () => riskyOperation(),
|
|
105
|
+
* (error) => console.error('Internal error:', error)
|
|
106
|
+
* );
|
|
107
|
+
*
|
|
108
|
+
* if (!result.success) {
|
|
109
|
+
* console.log(formatUserError(result.error));
|
|
110
|
+
* }
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
export declare function safeExecute<T>(operation: () => Promise<T>, errorHandler?: (error: Error, sanitized: SanitizedError) => void): Promise<{
|
|
114
|
+
success: true;
|
|
115
|
+
data: T;
|
|
116
|
+
} | {
|
|
117
|
+
success: false;
|
|
118
|
+
error: SanitizedError;
|
|
119
|
+
}>;
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error Message Sanitization (REQ-SEC-010)
|
|
3
|
+
*
|
|
4
|
+
* Sanitizes error messages to prevent information disclosure
|
|
5
|
+
* Removes:
|
|
6
|
+
* - File system paths
|
|
7
|
+
* - API keys and secrets
|
|
8
|
+
* - Stack traces (for user-facing errors)
|
|
9
|
+
* - Internal implementation details
|
|
10
|
+
*
|
|
11
|
+
* Security: CVSS 6.5 (Medium Priority)
|
|
12
|
+
*/
|
|
13
|
+
import { homedir } from 'os';
|
|
14
|
+
import { getAuditLogger, AuditCategory } from './audit-logger.js';
|
|
15
|
+
/**
|
|
16
|
+
* Patterns to detect and sanitize in error messages
|
|
17
|
+
*/
|
|
18
|
+
const SENSITIVE_PATTERNS = {
|
|
19
|
+
// File paths (Windows and Unix)
|
|
20
|
+
FILE_PATH: /([A-Za-z]:\\|\/)[^\s"'<>|]+/g,
|
|
21
|
+
// API keys and tokens (common formats)
|
|
22
|
+
// Matches patterns like "api_key=XXX", "secret: XXX", "API key: XXX", "bearer XXX"
|
|
23
|
+
API_KEY: /\b(?:api[_ -]?key|token|secret|password|bearer)[\s:=]+['"]?[a-zA-Z0-9_\-]{16,}['"]?/gi,
|
|
24
|
+
// Environment variables
|
|
25
|
+
ENV_VAR: /\$\{?[A-Z_][A-Z0-9_]*\}?/g,
|
|
26
|
+
// IP addresses (for SSRF protection)
|
|
27
|
+
IP_ADDRESS: /\b(?:\d{1,3}\.){3}\d{1,3}\b/g,
|
|
28
|
+
// URLs with credentials
|
|
29
|
+
URL_WITH_CREDS: /https?:\/\/[^:]+:[^@]+@[^\s]+/g,
|
|
30
|
+
// Stack trace lines
|
|
31
|
+
STACK_TRACE_LINE: /^\s*at\s+.+\(.+:\d+:\d+\)$/gm,
|
|
32
|
+
// Home directory references
|
|
33
|
+
HOME_DIR: new RegExp(homedir().replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'),
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Replacement strings for sanitized content
|
|
37
|
+
*/
|
|
38
|
+
const REPLACEMENTS = {
|
|
39
|
+
FILE_PATH: '[REDACTED_PATH]',
|
|
40
|
+
API_KEY: '[REDACTED_KEY]',
|
|
41
|
+
ENV_VAR: '[REDACTED_ENV]',
|
|
42
|
+
IP_ADDRESS: '[REDACTED_IP]',
|
|
43
|
+
URL_WITH_CREDS: '[REDACTED_URL]',
|
|
44
|
+
STACK_TRACE_LINE: '',
|
|
45
|
+
HOME_DIR: '[USER_HOME]',
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Error categories for user-friendly messages
|
|
49
|
+
*/
|
|
50
|
+
export var ErrorCategory;
|
|
51
|
+
(function (ErrorCategory) {
|
|
52
|
+
ErrorCategory["NETWORK"] = "NETWORK";
|
|
53
|
+
ErrorCategory["FILE_SYSTEM"] = "FILE_SYSTEM";
|
|
54
|
+
ErrorCategory["VALIDATION"] = "VALIDATION";
|
|
55
|
+
ErrorCategory["AUTHENTICATION"] = "AUTHENTICATION";
|
|
56
|
+
ErrorCategory["RATE_LIMIT"] = "RATE_LIMIT";
|
|
57
|
+
ErrorCategory["API_ERROR"] = "API_ERROR";
|
|
58
|
+
ErrorCategory["INTERNAL"] = "INTERNAL";
|
|
59
|
+
ErrorCategory["USER_INPUT"] = "USER_INPUT";
|
|
60
|
+
})(ErrorCategory || (ErrorCategory = {}));
|
|
61
|
+
/**
|
|
62
|
+
* Sanitize error message by removing sensitive information
|
|
63
|
+
*
|
|
64
|
+
* @param message - Raw error message
|
|
65
|
+
* @returns Sanitized message safe for user display
|
|
66
|
+
*/
|
|
67
|
+
export function sanitizeErrorMessage(message) {
|
|
68
|
+
let sanitized = message;
|
|
69
|
+
// Remove URLs with credentials first (before FILE_PATH catches them)
|
|
70
|
+
sanitized = sanitized.replace(SENSITIVE_PATTERNS.URL_WITH_CREDS, REPLACEMENTS.URL_WITH_CREDS);
|
|
71
|
+
// Remove home directory references (before FILE_PATH catches them)
|
|
72
|
+
sanitized = sanitized.replace(SENSITIVE_PATTERNS.HOME_DIR, REPLACEMENTS.HOME_DIR);
|
|
73
|
+
// Remove file paths
|
|
74
|
+
sanitized = sanitized.replace(SENSITIVE_PATTERNS.FILE_PATH, REPLACEMENTS.FILE_PATH);
|
|
75
|
+
// Remove API keys and secrets
|
|
76
|
+
sanitized = sanitized.replace(SENSITIVE_PATTERNS.API_KEY, REPLACEMENTS.API_KEY);
|
|
77
|
+
// Remove environment variables
|
|
78
|
+
sanitized = sanitized.replace(SENSITIVE_PATTERNS.ENV_VAR, REPLACEMENTS.ENV_VAR);
|
|
79
|
+
// Remove IP addresses
|
|
80
|
+
sanitized = sanitized.replace(SENSITIVE_PATTERNS.IP_ADDRESS, REPLACEMENTS.IP_ADDRESS);
|
|
81
|
+
return sanitized;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Sanitize stack trace by removing sensitive paths
|
|
85
|
+
*
|
|
86
|
+
* @param stack - Raw stack trace
|
|
87
|
+
* @returns Sanitized stack trace
|
|
88
|
+
*/
|
|
89
|
+
export function sanitizeStackTrace(stack) {
|
|
90
|
+
let sanitized = stack;
|
|
91
|
+
// Remove home directory references first
|
|
92
|
+
sanitized = sanitized.replace(SENSITIVE_PATTERNS.HOME_DIR, REPLACEMENTS.HOME_DIR);
|
|
93
|
+
// Remove file paths from stack frames
|
|
94
|
+
sanitized = sanitized.replace(SENSITIVE_PATTERNS.FILE_PATH, REPLACEMENTS.FILE_PATH);
|
|
95
|
+
return sanitized;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Remove stack trace entirely (for user-facing errors)
|
|
99
|
+
*
|
|
100
|
+
* @param message - Error message with potential stack trace
|
|
101
|
+
* @returns Message without stack trace
|
|
102
|
+
*/
|
|
103
|
+
export function removeStackTrace(message) {
|
|
104
|
+
// Split at first "at " (stack trace start)
|
|
105
|
+
const parts = message.split(/\n\s*at\s+/);
|
|
106
|
+
return parts[0].trim();
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Categorize error and create user-friendly message
|
|
110
|
+
*
|
|
111
|
+
* @param error - Error object
|
|
112
|
+
* @returns Sanitized error with category and suggestion
|
|
113
|
+
*/
|
|
114
|
+
export function sanitizeError(error) {
|
|
115
|
+
const errorObj = error instanceof Error ? error : new Error(String(error));
|
|
116
|
+
const message = errorObj.message;
|
|
117
|
+
// Sanitize the message
|
|
118
|
+
const sanitizedMessage = sanitizeErrorMessage(removeStackTrace(message));
|
|
119
|
+
// Determine category and suggestion
|
|
120
|
+
let category = ErrorCategory.INTERNAL;
|
|
121
|
+
let suggestion;
|
|
122
|
+
let code;
|
|
123
|
+
// Network errors
|
|
124
|
+
if (message.includes('ENOTFOUND') || message.includes('ECONNREFUSED') || message.includes('fetch failed')) {
|
|
125
|
+
category = ErrorCategory.NETWORK;
|
|
126
|
+
suggestion = 'Check your network connection and try again.';
|
|
127
|
+
code = 'ERR_NETWORK';
|
|
128
|
+
}
|
|
129
|
+
// File system errors
|
|
130
|
+
else if (message.includes('ENOENT') || message.includes('EACCES') || message.includes('EPERM')) {
|
|
131
|
+
category = ErrorCategory.FILE_SYSTEM;
|
|
132
|
+
suggestion = 'Check that the file exists and you have permission to access it.';
|
|
133
|
+
code = 'ERR_FILE_SYSTEM';
|
|
134
|
+
}
|
|
135
|
+
// Validation errors
|
|
136
|
+
else if (message.includes('validation') || message.includes('invalid') || message.includes('required')) {
|
|
137
|
+
category = ErrorCategory.VALIDATION;
|
|
138
|
+
suggestion = 'Check your input and try again.';
|
|
139
|
+
code = 'ERR_VALIDATION';
|
|
140
|
+
}
|
|
141
|
+
// Authentication errors
|
|
142
|
+
else if (message.includes('unauthorized') || message.includes('authentication') || message.includes('API key')) {
|
|
143
|
+
category = ErrorCategory.AUTHENTICATION;
|
|
144
|
+
suggestion = 'Check your API key configuration.';
|
|
145
|
+
code = 'ERR_AUTH';
|
|
146
|
+
}
|
|
147
|
+
// Rate limit errors
|
|
148
|
+
else if (message.includes('rate limit') || message.includes('too many requests')) {
|
|
149
|
+
category = ErrorCategory.RATE_LIMIT;
|
|
150
|
+
suggestion = 'Please wait a moment before trying again.';
|
|
151
|
+
code = 'ERR_RATE_LIMIT';
|
|
152
|
+
}
|
|
153
|
+
// API errors
|
|
154
|
+
else if (message.includes('API') || message.includes('status code')) {
|
|
155
|
+
category = ErrorCategory.API_ERROR;
|
|
156
|
+
suggestion = 'The API returned an error. Please try again later.';
|
|
157
|
+
code = 'ERR_API';
|
|
158
|
+
}
|
|
159
|
+
// REQ-SEC-008: Audit log errors with sensitive info detection
|
|
160
|
+
if (message !== sanitizedMessage) {
|
|
161
|
+
const auditLogger = getAuditLogger();
|
|
162
|
+
auditLogger.logWarning({
|
|
163
|
+
category: AuditCategory.SYSTEM_EVENT,
|
|
164
|
+
action: 'sensitive_data_in_error',
|
|
165
|
+
outcome: 'success',
|
|
166
|
+
details: {
|
|
167
|
+
category,
|
|
168
|
+
sanitized: true,
|
|
169
|
+
},
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
return {
|
|
173
|
+
message: sanitizedMessage,
|
|
174
|
+
code,
|
|
175
|
+
category,
|
|
176
|
+
suggestion,
|
|
177
|
+
originalError: errorObj,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Format sanitized error for user display
|
|
182
|
+
*
|
|
183
|
+
* @param sanitizedError - Sanitized error object
|
|
184
|
+
* @returns Formatted error message
|
|
185
|
+
*/
|
|
186
|
+
export function formatUserError(sanitizedError) {
|
|
187
|
+
const parts = [];
|
|
188
|
+
if (sanitizedError.code) {
|
|
189
|
+
parts.push(`[${sanitizedError.code}]`);
|
|
190
|
+
}
|
|
191
|
+
parts.push(sanitizedError.message);
|
|
192
|
+
if (sanitizedError.suggestion) {
|
|
193
|
+
parts.push(`\nℹ️ ${sanitizedError.suggestion}`);
|
|
194
|
+
}
|
|
195
|
+
return parts.join(' ');
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Create internal log message with full details (not sanitized)
|
|
199
|
+
*
|
|
200
|
+
* @param error - Original error
|
|
201
|
+
* @param context - Additional context
|
|
202
|
+
* @returns Detailed log message
|
|
203
|
+
*/
|
|
204
|
+
export function createInternalLogMessage(error, context) {
|
|
205
|
+
const errorObj = error instanceof Error ? error : new Error(String(error));
|
|
206
|
+
const parts = [
|
|
207
|
+
`Error: ${errorObj.message}`,
|
|
208
|
+
];
|
|
209
|
+
if (errorObj.stack) {
|
|
210
|
+
parts.push(`Stack: ${sanitizeStackTrace(errorObj.stack)}`);
|
|
211
|
+
}
|
|
212
|
+
if (context) {
|
|
213
|
+
parts.push(`Context: ${JSON.stringify(context, null, 2)}`);
|
|
214
|
+
}
|
|
215
|
+
return parts.join('\n');
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Safe error wrapper for user-facing operations
|
|
219
|
+
*
|
|
220
|
+
* @param operation - Async operation to execute
|
|
221
|
+
* @param errorHandler - Optional custom error handler
|
|
222
|
+
* @returns Result or sanitized error
|
|
223
|
+
*
|
|
224
|
+
* @example
|
|
225
|
+
* ```typescript
|
|
226
|
+
* const result = await safeExecute(
|
|
227
|
+
* () => riskyOperation(),
|
|
228
|
+
* (error) => console.error('Internal error:', error)
|
|
229
|
+
* );
|
|
230
|
+
*
|
|
231
|
+
* if (!result.success) {
|
|
232
|
+
* console.log(formatUserError(result.error));
|
|
233
|
+
* }
|
|
234
|
+
* ```
|
|
235
|
+
*/
|
|
236
|
+
export async function safeExecute(operation, errorHandler) {
|
|
237
|
+
try {
|
|
238
|
+
const data = await operation();
|
|
239
|
+
return { success: true, data };
|
|
240
|
+
}
|
|
241
|
+
catch (error) {
|
|
242
|
+
const sanitized = sanitizeError(error);
|
|
243
|
+
// Log internal error details
|
|
244
|
+
if (errorHandler) {
|
|
245
|
+
errorHandler(sanitized.originalError, sanitized);
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
console.error(createInternalLogMessage(error));
|
|
249
|
+
}
|
|
250
|
+
return { success: false, error: sanitized };
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
//# sourceMappingURL=error-sanitizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-sanitizer.js","sourceRoot":"","sources":["../../src/utils/error-sanitizer.ts"],"names":[],"mappings":"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"}
|