@defai.digital/ax-cli 3.5.4 → 3.6.1
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/checkpoints/2025-11-20/checkpoint-11e9e0ba-c39d-4fd2-aa77-bc818811c921.json +69 -0
- package/.ax-cli/checkpoints/2025-11-20/checkpoint-2b260b98-b418-4c7c-9694-e2b94967e662.json +24 -0
- package/.ax-cli/checkpoints/2025-11-20/checkpoint-7e03601e-e8ab-4cd7-9841-a74b66adf78f.json +69 -0
- package/.ax-cli/checkpoints/2025-11-20/checkpoint-7f9c6562-771f-4fd0-adcf-9e7e9ac34ae8.json +44 -0
- package/.ax-cli/checkpoints/2025-11-20/checkpoint-e1ebe666-4c3a-4367-ba5c-27fe512a9c70.json +24 -0
- package/.ax-cli/checkpoints/2025-11-21/checkpoint-15743e7d-430c-4d76-b6fc-955d7a5c250c.json +44 -0
- package/.ax-cli/checkpoints/2025-11-21/checkpoint-25cf7679-0b3f-4988-83d7-704548fbba91.json +69 -0
- package/.ax-cli/checkpoints/2025-11-21/checkpoint-54aedbac-6db0-464e-8ebb-dbb3979e6dca.json +24 -0
- package/.ax-cli/checkpoints/2025-11-21/checkpoint-7658aed8-fe5d-4222-903f-1a7c63717ea7.json +24 -0
- package/.ax-cli/checkpoints/2025-11-21/checkpoint-c9c13497-40dc-4294-a327-6a5fc854eaa1.json +69 -0
- package/.ax-cli/memory.json +15 -8
- package/README.md +423 -82
- package/ax.config.json +333 -0
- package/config-defaults/messages.yaml +75 -0
- package/config-defaults/models.yaml +66 -0
- package/config-defaults/prompts.yaml +156 -0
- package/config-defaults/settings.yaml +86 -0
- 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/code-smells/detectors/data-clumps-detector.js +7 -9
- package/dist/analyzers/code-smells/detectors/data-clumps-detector.js.map +1 -1
- package/dist/analyzers/code-smells/detectors/dead-code-detector.js +1 -1
- package/dist/analyzers/code-smells/detectors/dead-code-detector.js.map +1 -1
- package/dist/analyzers/code-smells/detectors/duplicate-code-detector.js +22 -10
- package/dist/analyzers/code-smells/detectors/duplicate-code-detector.js.map +1 -1
- package/dist/analyzers/code-smells/detectors/feature-envy-detector.js +1 -1
- package/dist/analyzers/code-smells/detectors/feature-envy-detector.js.map +1 -1
- package/dist/analyzers/code-smells/detectors/inappropriate-intimacy-detector.js +1 -1
- package/dist/analyzers/code-smells/detectors/inappropriate-intimacy-detector.js.map +1 -1
- package/dist/analyzers/code-smells/detectors/large-class-detector.js +4 -1
- package/dist/analyzers/code-smells/detectors/large-class-detector.js.map +1 -1
- package/dist/analyzers/code-smells/detectors/long-method-detector.js +4 -1
- package/dist/analyzers/code-smells/detectors/long-method-detector.js.map +1 -1
- package/dist/analyzers/code-smells/detectors/long-parameter-list-detector.js +4 -1
- package/dist/analyzers/code-smells/detectors/long-parameter-list-detector.js.map +1 -1
- package/dist/analyzers/code-smells/detectors/magic-numbers-detector.js +4 -5
- package/dist/analyzers/code-smells/detectors/magic-numbers-detector.js.map +1 -1
- package/dist/analyzers/code-smells/detectors/nested-conditionals-detector.js +4 -1
- package/dist/analyzers/code-smells/detectors/nested-conditionals-detector.js.map +1 -1
- package/dist/commands/memory.js +1 -1
- package/dist/commands/memory.js.map +1 -1
- package/dist/commands/setup.js +19 -6
- package/dist/commands/setup.js.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.bak +664 -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/health.js +4 -2
- package/dist/mcp/health.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 +53 -0
- package/dist/schemas/settings-schemas.js +47 -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/tools/web-search/index.d.ts +0 -2
- package/dist/tools/web-search/index.js +0 -2
- package/dist/tools/web-search/index.js.map +1 -1
- package/dist/tools/web-search/router.d.ts +0 -2
- package/dist/tools/web-search/router.js +2 -37
- package/dist/tools/web-search/router.js.map +1 -1
- package/dist/tools/web-search/web-search-tool.js +2 -12
- package/dist/tools/web-search/web-search-tool.js.map +1 -1
- package/dist/ui/components/chat-history.js +1 -1
- package/dist/ui/components/chat-history.js.map +1 -1
- package/dist/ui/components/chat-input.d.ts +4 -1
- package/dist/ui/components/chat-input.js +133 -52
- package/dist/ui/components/chat-input.js.map +1 -1
- package/dist/ui/components/chat-interface.js +5 -4
- package/dist/ui/components/chat-interface.js.map +1 -1
- package/dist/ui/components/confirmation-dialog.js +1 -1
- package/dist/ui/components/confirmation-dialog.js.map +1 -1
- package/dist/ui/components/keyboard-hints.js +2 -0
- package/dist/ui/components/keyboard-hints.js.map +1 -1
- package/dist/ui/components/status-bar.js +3 -13
- package/dist/ui/components/status-bar.js.map +1 -1
- package/dist/ui/components/welcome-panel.js +4 -0
- package/dist/ui/components/welcome-panel.js.map +1 -1
- package/dist/ui/hooks/use-chat-reducer.d.ts +61 -0
- package/dist/ui/hooks/use-chat-reducer.js +118 -0
- package/dist/ui/hooks/use-chat-reducer.js.map +1 -0
- package/dist/ui/hooks/use-enhanced-input.d.ts +44 -0
- package/dist/ui/hooks/use-enhanced-input.js +364 -0
- package/dist/ui/hooks/use-enhanced-input.js.map +1 -0
- package/dist/ui/hooks/use-input-handler.d.ts +48 -0
- package/dist/ui/hooks/use-input-handler.js +1446 -0
- package/dist/ui/hooks/use-input-handler.js.map +1 -0
- package/dist/utils/audit-logger.d.ts +205 -0
- package/dist/utils/audit-logger.js +269 -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/config-loader.js +3 -3
- package/dist/utils/config-loader.js.map +1 -1
- 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/paste-collapse.d.ts +46 -0
- package/dist/utils/paste-collapse.js +77 -0
- package/dist/utils/paste-collapse.js.map +1 -0
- package/dist/utils/paste-utils.d.ts +99 -0
- package/dist/utils/paste-utils.js +239 -0
- package/dist/utils/paste-utils.js.map +1 -0
- 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 +221 -0
- package/dist/utils/rate-limiter.js +317 -0
- package/dist/utils/rate-limiter.js.map +1 -0
- package/dist/utils/settings-manager.js +99 -6
- package/dist/utils/settings-manager.js.map +1 -1
- package/dist/utils/streaming-analyzer.js +9 -21
- package/dist/utils/streaming-analyzer.js.map +1 -1
- package/package.json +3 -7
- package/packages/schemas/dist/index.d.ts +14 -0
- package/packages/schemas/dist/index.d.ts.map +1 -0
- package/packages/schemas/dist/index.js +19 -0
- package/packages/schemas/dist/index.js.map +1 -0
- package/packages/schemas/dist/public/core/brand-types.d.ts +308 -0
- package/packages/schemas/dist/public/core/brand-types.d.ts.map +1 -0
- package/packages/schemas/dist/public/core/brand-types.js +243 -0
- package/packages/schemas/dist/public/core/brand-types.js.map +1 -0
- package/packages/schemas/dist/public/core/enums.d.ts +227 -0
- package/packages/schemas/dist/public/core/enums.d.ts.map +1 -0
- package/packages/schemas/dist/public/core/enums.js +222 -0
- package/packages/schemas/dist/public/core/enums.js.map +1 -0
- package/packages/schemas/dist/public/core/id-types.d.ts +286 -0
- package/packages/schemas/dist/public/core/id-types.d.ts.map +1 -0
- package/packages/schemas/dist/public/core/id-types.js +136 -0
- package/packages/schemas/dist/public/core/id-types.js.map +1 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Command Security Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides secure command execution with whitelisting and validation.
|
|
5
|
+
* Prevents command injection vulnerabilities (REQ-SEC-001).
|
|
6
|
+
*
|
|
7
|
+
* @module command-security
|
|
8
|
+
*/
|
|
9
|
+
import { execFile } from 'child_process';
|
|
10
|
+
import { promisify } from 'util';
|
|
11
|
+
const execFileAsync = promisify(execFile);
|
|
12
|
+
/**
|
|
13
|
+
* Whitelist of safe commands allowed for execution.
|
|
14
|
+
* Only these commands can be executed via the BashTool.
|
|
15
|
+
*
|
|
16
|
+
* CRITICAL: Do not add arbitrary commands without security review.
|
|
17
|
+
*/
|
|
18
|
+
export const SAFE_COMMANDS = [
|
|
19
|
+
'ls',
|
|
20
|
+
'grep',
|
|
21
|
+
'find',
|
|
22
|
+
'cat',
|
|
23
|
+
'head',
|
|
24
|
+
'tail',
|
|
25
|
+
'wc',
|
|
26
|
+
'sort',
|
|
27
|
+
'uniq',
|
|
28
|
+
'cut',
|
|
29
|
+
'awk',
|
|
30
|
+
'sed',
|
|
31
|
+
'pwd',
|
|
32
|
+
'echo',
|
|
33
|
+
'date',
|
|
34
|
+
'whoami',
|
|
35
|
+
'hostname',
|
|
36
|
+
'git',
|
|
37
|
+
'rg',
|
|
38
|
+
'fd',
|
|
39
|
+
'rm', // File deletion (safe with validation)
|
|
40
|
+
'mkdir', // Directory creation
|
|
41
|
+
'touch', // File creation
|
|
42
|
+
'cp', // File copy
|
|
43
|
+
'mv', // File move/rename
|
|
44
|
+
];
|
|
45
|
+
/**
|
|
46
|
+
* Environment variables safe to pass to child processes.
|
|
47
|
+
* Only these will be included in the child process environment.
|
|
48
|
+
*/
|
|
49
|
+
const SAFE_ENV_VARS = [
|
|
50
|
+
'PATH',
|
|
51
|
+
'HOME',
|
|
52
|
+
'USER',
|
|
53
|
+
'LANG',
|
|
54
|
+
'LC_ALL',
|
|
55
|
+
'TERM',
|
|
56
|
+
'TMPDIR',
|
|
57
|
+
'PWD',
|
|
58
|
+
];
|
|
59
|
+
/**
|
|
60
|
+
* Shell metacharacters that are forbidden in command arguments.
|
|
61
|
+
* These could enable command injection if not properly validated.
|
|
62
|
+
*/
|
|
63
|
+
const SHELL_METACHARACTERS = /[;&|`$(){}[\]<>'"\\*?~!#]/;
|
|
64
|
+
/**
|
|
65
|
+
* Sanitize environment variables for child process.
|
|
66
|
+
* Only includes safe environment variables to prevent injection.
|
|
67
|
+
*
|
|
68
|
+
* @param env - Original process environment
|
|
69
|
+
* @returns Sanitized environment object
|
|
70
|
+
*/
|
|
71
|
+
export function sanitizeEnv(env) {
|
|
72
|
+
const sanitized = {};
|
|
73
|
+
for (const key of SAFE_ENV_VARS) {
|
|
74
|
+
if (env[key]) {
|
|
75
|
+
sanitized[key] = env[key];
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return sanitized;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Parse a command string into command and arguments.
|
|
82
|
+
* Validates that the command is in the whitelist.
|
|
83
|
+
*
|
|
84
|
+
* @param commandString - Full command string (e.g., "ls -la /tmp")
|
|
85
|
+
* @returns Parsed command structure
|
|
86
|
+
* @throws Error if command is not whitelisted
|
|
87
|
+
*/
|
|
88
|
+
export function parseCommand(commandString) {
|
|
89
|
+
const trimmed = commandString.trim();
|
|
90
|
+
if (!trimmed) {
|
|
91
|
+
throw new Error('Empty command string');
|
|
92
|
+
}
|
|
93
|
+
// Simple split by whitespace
|
|
94
|
+
const parts = trimmed.split(/\s+/);
|
|
95
|
+
const command = parts[0];
|
|
96
|
+
const args = parts.slice(1);
|
|
97
|
+
// Validate command is in whitelist
|
|
98
|
+
if (!SAFE_COMMANDS.includes(command)) {
|
|
99
|
+
throw new Error(`Command '${command}' not in whitelist. Allowed commands: ${SAFE_COMMANDS.join(', ')}`);
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
command: command,
|
|
103
|
+
args,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Validate command arguments for shell metacharacters.
|
|
108
|
+
* Prevents command injection via argument injection.
|
|
109
|
+
*
|
|
110
|
+
* @param args - Command arguments to validate
|
|
111
|
+
* @returns Validation result
|
|
112
|
+
*/
|
|
113
|
+
export function validateArguments(args) {
|
|
114
|
+
const errors = [];
|
|
115
|
+
for (let i = 0; i < args.length; i++) {
|
|
116
|
+
const arg = args[i];
|
|
117
|
+
// Check for shell metacharacters
|
|
118
|
+
if (SHELL_METACHARACTERS.test(arg)) {
|
|
119
|
+
errors.push(`Argument ${i} contains forbidden shell metacharacters: "${arg}"`);
|
|
120
|
+
}
|
|
121
|
+
// Check for null bytes
|
|
122
|
+
if (arg.includes('\0')) {
|
|
123
|
+
errors.push(`Argument ${i} contains null byte`);
|
|
124
|
+
}
|
|
125
|
+
// Check length (prevent buffer overflow)
|
|
126
|
+
if (arg.length > 10000) {
|
|
127
|
+
errors.push(`Argument ${i} exceeds maximum length (10000 chars)`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
valid: errors.length === 0,
|
|
132
|
+
errors,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Execute a safe command with validation.
|
|
137
|
+
* Uses execFile to avoid shell invocation and command injection.
|
|
138
|
+
*
|
|
139
|
+
* SECURITY: This function uses execFile instead of spawn('bash', ['-c'])
|
|
140
|
+
* to prevent command injection attacks.
|
|
141
|
+
*
|
|
142
|
+
* @param commandString - Command to execute
|
|
143
|
+
* @param options - Execution options
|
|
144
|
+
* @returns Tool result with output or error
|
|
145
|
+
*/
|
|
146
|
+
export async function executeSafeCommand(commandString, options = {}) {
|
|
147
|
+
try {
|
|
148
|
+
// 1. Parse command into command + args
|
|
149
|
+
const parsed = parseCommand(commandString);
|
|
150
|
+
// 2. Validate arguments
|
|
151
|
+
const validation = validateArguments(parsed.args);
|
|
152
|
+
if (!validation.valid) {
|
|
153
|
+
return {
|
|
154
|
+
success: false,
|
|
155
|
+
error: `Command validation failed:\n${validation.errors.join('\n')}`,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
// 3. Prepare execution options
|
|
159
|
+
const execOptions = {
|
|
160
|
+
cwd: options.cwd || process.cwd(),
|
|
161
|
+
env: sanitizeEnv(process.env),
|
|
162
|
+
timeout: options.timeout || 30000, // 30 second default
|
|
163
|
+
maxBuffer: options.maxBuffer || 1024 * 1024, // 1MB default
|
|
164
|
+
};
|
|
165
|
+
// 4. Execute using execFile (no shell invocation)
|
|
166
|
+
const { stdout, stderr } = await execFileAsync(parsed.command, parsed.args, execOptions);
|
|
167
|
+
// 5. Return successful result
|
|
168
|
+
return {
|
|
169
|
+
success: true,
|
|
170
|
+
output: stdout || stderr || 'Command completed successfully',
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
// Handle execution errors
|
|
175
|
+
const errorMessage = error.message || String(error);
|
|
176
|
+
const exitCode = error.code || 'unknown';
|
|
177
|
+
return {
|
|
178
|
+
success: false,
|
|
179
|
+
error: `Command execution failed (exit code: ${exitCode}): ${errorMessage}`,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Check if a command is safe to execute.
|
|
185
|
+
*
|
|
186
|
+
* @param command - Command name to check
|
|
187
|
+
* @returns True if command is in whitelist
|
|
188
|
+
*/
|
|
189
|
+
export function isSafeCommand(command) {
|
|
190
|
+
return SAFE_COMMANDS.includes(command);
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Get list of safe commands (for documentation/help).
|
|
194
|
+
*
|
|
195
|
+
* @returns Array of safe command names
|
|
196
|
+
*/
|
|
197
|
+
export function getSafeCommands() {
|
|
198
|
+
return SAFE_COMMANDS;
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=command-security.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"command-security.js","sourceRoot":"","sources":["../../src/utils/command-security.ts"],"names":[],"mappings":"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"}
|
|
@@ -16,9 +16,9 @@ const configCache = new Map();
|
|
|
16
16
|
* Get the config directory path
|
|
17
17
|
*/
|
|
18
18
|
function getConfigDir() {
|
|
19
|
-
// In development: src/utils -> ../../config
|
|
20
|
-
// In production: dist/utils -> ../../config
|
|
21
|
-
return join(__dirname, '../../config');
|
|
19
|
+
// In development: src/utils -> ../../config-defaults
|
|
20
|
+
// In production: dist/utils -> ../../config-defaults
|
|
21
|
+
return join(__dirname, '../../config-defaults');
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
24
24
|
* Load a YAML configuration file with optional schema validation
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-loader.js","sourceRoot":"","sources":["../../src/utils/config-loader.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,IAAI,MAAM,SAAS,CAAC;AAC3B,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAE/B,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAEzH,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAEtC,kCAAkC;AAClC,MAAM,WAAW,GAAG,IAAI,GAAG,EAAe,CAAC;AAE3C;;GAEG;AACH,SAAS,YAAY;IACnB,
|
|
1
|
+
{"version":3,"file":"config-loader.js","sourceRoot":"","sources":["../../src/utils/config-loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -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
|
+
}>;
|