@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,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Input Sanitization Framework (REQ-SEC-007)
|
|
3
|
+
*
|
|
4
|
+
* Provides comprehensive input validation and sanitization to prevent:
|
|
5
|
+
* - ReDoS (Regular Expression Denial of Service)
|
|
6
|
+
* - Command injection
|
|
7
|
+
* - Path traversal
|
|
8
|
+
* - Unicode attacks
|
|
9
|
+
* - Buffer overflow
|
|
10
|
+
*
|
|
11
|
+
* Security: CVSS 7.3 (High Priority)
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Sanitization result with validation and cleaned value
|
|
15
|
+
*/
|
|
16
|
+
export interface SanitizationResult {
|
|
17
|
+
/**
|
|
18
|
+
* Whether the input passed validation
|
|
19
|
+
*/
|
|
20
|
+
valid: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Sanitized/cleaned value (only if valid)
|
|
23
|
+
*/
|
|
24
|
+
value?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Error message if validation failed
|
|
27
|
+
*/
|
|
28
|
+
error?: string;
|
|
29
|
+
/**
|
|
30
|
+
* Warning messages (non-fatal issues)
|
|
31
|
+
*/
|
|
32
|
+
warnings?: string[];
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Configuration for input sanitization
|
|
36
|
+
*/
|
|
37
|
+
export interface SanitizerOptions {
|
|
38
|
+
/**
|
|
39
|
+
* Maximum allowed length (default: 10,000 characters)
|
|
40
|
+
*/
|
|
41
|
+
maxLength?: number;
|
|
42
|
+
/**
|
|
43
|
+
* Whether to normalize Unicode (default: true)
|
|
44
|
+
*/
|
|
45
|
+
normalizeUnicode?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Character whitelist pattern (regex)
|
|
48
|
+
*/
|
|
49
|
+
allowedPattern?: RegExp;
|
|
50
|
+
/**
|
|
51
|
+
* Whether to trim whitespace (default: true)
|
|
52
|
+
*/
|
|
53
|
+
trim?: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Whether to allow empty strings (default: false)
|
|
56
|
+
*/
|
|
57
|
+
allowEmpty?: boolean;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Default maximum input lengths for different contexts
|
|
61
|
+
*/
|
|
62
|
+
export declare const MAX_INPUT_LENGTHS: {
|
|
63
|
+
readonly COMMAND: 10000;
|
|
64
|
+
readonly FILE_PATH: 4096;
|
|
65
|
+
readonly USER_INPUT: 50000;
|
|
66
|
+
readonly SEARCH_QUERY: 1000;
|
|
67
|
+
readonly ENV_VALUE: 10000;
|
|
68
|
+
readonly CONFIG_VALUE: 10000;
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Safe characters for different contexts
|
|
72
|
+
*/
|
|
73
|
+
export declare const SAFE_PATTERNS: {
|
|
74
|
+
/**
|
|
75
|
+
* Alphanumeric, spaces, and common punctuation
|
|
76
|
+
*/
|
|
77
|
+
readonly BASIC: RegExp;
|
|
78
|
+
/**
|
|
79
|
+
* Safe for file paths (no directory traversal)
|
|
80
|
+
*/
|
|
81
|
+
readonly FILE_PATH: RegExp;
|
|
82
|
+
/**
|
|
83
|
+
* Safe for environment variable values
|
|
84
|
+
*/
|
|
85
|
+
readonly ENV_VALUE: RegExp;
|
|
86
|
+
/**
|
|
87
|
+
* Printable ASCII only (most restrictive)
|
|
88
|
+
*/
|
|
89
|
+
readonly ASCII_PRINTABLE: RegExp;
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Normalize Unicode string to prevent homograph attacks
|
|
93
|
+
*
|
|
94
|
+
* Uses NFC (Canonical Decomposition, followed by Canonical Composition)
|
|
95
|
+
* which is the recommended normalization form for most use cases
|
|
96
|
+
*
|
|
97
|
+
* @param input - String to normalize
|
|
98
|
+
* @returns Normalized string
|
|
99
|
+
*/
|
|
100
|
+
export declare function normalizeUnicode(input: string): string;
|
|
101
|
+
/**
|
|
102
|
+
* Check for dangerous patterns in input
|
|
103
|
+
*
|
|
104
|
+
* @param input - String to check
|
|
105
|
+
* @returns Array of detected dangerous patterns
|
|
106
|
+
*/
|
|
107
|
+
export declare function detectDangerousPatterns(input: string): string[];
|
|
108
|
+
/**
|
|
109
|
+
* Sanitize general user input with configurable options
|
|
110
|
+
*
|
|
111
|
+
* @param input - Raw input string
|
|
112
|
+
* @param options - Sanitization options
|
|
113
|
+
* @returns Sanitization result
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* ```typescript
|
|
117
|
+
* const result = sanitizeInput('User input here', {
|
|
118
|
+
* maxLength: 1000,
|
|
119
|
+
* normalizeUnicode: true,
|
|
120
|
+
* allowedPattern: SAFE_PATTERNS.BASIC,
|
|
121
|
+
* });
|
|
122
|
+
*
|
|
123
|
+
* if (result.valid) {
|
|
124
|
+
* // Use result.value safely
|
|
125
|
+
* } else {
|
|
126
|
+
* console.error(result.error);
|
|
127
|
+
* }
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
130
|
+
export declare function sanitizeInput(input: string, options?: SanitizerOptions): SanitizationResult;
|
|
131
|
+
/**
|
|
132
|
+
* Sanitize file path input to prevent path traversal
|
|
133
|
+
*
|
|
134
|
+
* @param path - File path to sanitize
|
|
135
|
+
* @returns Sanitization result
|
|
136
|
+
*
|
|
137
|
+
* @example
|
|
138
|
+
* ```typescript
|
|
139
|
+
* const result = sanitizeFilePath('../../../etc/passwd');
|
|
140
|
+
* if (!result.valid) {
|
|
141
|
+
* console.error('Invalid path:', result.error);
|
|
142
|
+
* }
|
|
143
|
+
* ```
|
|
144
|
+
*/
|
|
145
|
+
export declare function sanitizeFilePath(path: string): SanitizationResult;
|
|
146
|
+
/**
|
|
147
|
+
* Sanitize shell command input
|
|
148
|
+
*
|
|
149
|
+
* NOTE: This is a last line of defense. Prefer execFile over exec
|
|
150
|
+
* and use argument arrays instead of concatenating commands.
|
|
151
|
+
*
|
|
152
|
+
* @param command - Command string to sanitize
|
|
153
|
+
* @returns Sanitization result
|
|
154
|
+
*
|
|
155
|
+
* @example
|
|
156
|
+
* ```typescript
|
|
157
|
+
* const result = sanitizeCommand('ls -la');
|
|
158
|
+
* if (result.valid) {
|
|
159
|
+
* // Still prefer execFile with args array
|
|
160
|
+
* execFile(result.value.split(' ')[0], result.value.split(' ').slice(1));
|
|
161
|
+
* }
|
|
162
|
+
* ```
|
|
163
|
+
*/
|
|
164
|
+
export declare function sanitizeCommand(command: string): SanitizationResult;
|
|
165
|
+
/**
|
|
166
|
+
* Sanitize search query input
|
|
167
|
+
*
|
|
168
|
+
* @param query - Search query to sanitize
|
|
169
|
+
* @returns Sanitization result
|
|
170
|
+
*/
|
|
171
|
+
export declare function sanitizeSearchQuery(query: string): SanitizationResult;
|
|
172
|
+
/**
|
|
173
|
+
* Sanitize environment variable value
|
|
174
|
+
*
|
|
175
|
+
* @param value - Environment variable value to sanitize
|
|
176
|
+
* @returns Sanitization result
|
|
177
|
+
*/
|
|
178
|
+
export declare function sanitizeEnvValue(value: string): SanitizationResult;
|
|
179
|
+
/**
|
|
180
|
+
* Escape shell arguments for safe execution
|
|
181
|
+
*
|
|
182
|
+
* NOTE: This is a defense-in-depth measure. Always prefer:
|
|
183
|
+
* 1. execFile with argument array over exec
|
|
184
|
+
* 2. Argument validation/whitelisting
|
|
185
|
+
* 3. This escaping function as a last resort
|
|
186
|
+
*
|
|
187
|
+
* @param arg - Argument to escape
|
|
188
|
+
* @returns Safely escaped argument
|
|
189
|
+
*/
|
|
190
|
+
export declare function escapeShellArg(arg: string): string;
|
|
191
|
+
/**
|
|
192
|
+
* Validate regex pattern for ReDoS protection
|
|
193
|
+
*
|
|
194
|
+
* Checks for common ReDoS patterns:
|
|
195
|
+
* - Nested quantifiers (e.g., (a+)+)
|
|
196
|
+
* - Alternation with overlapping patterns
|
|
197
|
+
* - Excessive backtracking potential
|
|
198
|
+
*
|
|
199
|
+
* @param pattern - Regex pattern to validate
|
|
200
|
+
* @returns Validation result
|
|
201
|
+
*
|
|
202
|
+
* @example
|
|
203
|
+
* ```typescript
|
|
204
|
+
* const result = validateRegexPattern('(a+)+b');
|
|
205
|
+
* if (!result.valid) {
|
|
206
|
+
* console.error('Unsafe regex:', result.error);
|
|
207
|
+
* }
|
|
208
|
+
* ```
|
|
209
|
+
*/
|
|
210
|
+
export declare function validateRegexPattern(pattern: string): SanitizationResult;
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Input Sanitization Framework (REQ-SEC-007)
|
|
3
|
+
*
|
|
4
|
+
* Provides comprehensive input validation and sanitization to prevent:
|
|
5
|
+
* - ReDoS (Regular Expression Denial of Service)
|
|
6
|
+
* - Command injection
|
|
7
|
+
* - Path traversal
|
|
8
|
+
* - Unicode attacks
|
|
9
|
+
* - Buffer overflow
|
|
10
|
+
*
|
|
11
|
+
* Security: CVSS 7.3 (High Priority)
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Default maximum input lengths for different contexts
|
|
15
|
+
*/
|
|
16
|
+
export const MAX_INPUT_LENGTHS = {
|
|
17
|
+
COMMAND: 10_000, // Shell commands
|
|
18
|
+
FILE_PATH: 4_096, // File system paths
|
|
19
|
+
USER_INPUT: 50_000, // General user input (prompts, etc.)
|
|
20
|
+
SEARCH_QUERY: 1_000, // Search queries
|
|
21
|
+
ENV_VALUE: 10_000, // Environment variable values
|
|
22
|
+
CONFIG_VALUE: 10_000, // Configuration values
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Dangerous patterns that indicate potential attacks
|
|
26
|
+
* These patterns are designed to be fast and avoid ReDoS
|
|
27
|
+
*/
|
|
28
|
+
const DANGEROUS_PATTERNS = {
|
|
29
|
+
// Null bytes (path traversal, command injection)
|
|
30
|
+
NULL_BYTE: /\0/,
|
|
31
|
+
// Excessive repetition (ReDoS indicator)
|
|
32
|
+
// Using fixed quantifier to prevent ReDoS
|
|
33
|
+
EXCESSIVE_REPETITION: /(.)\1{100,}/,
|
|
34
|
+
// Control characters (except common ones like \n, \t)
|
|
35
|
+
CONTROL_CHARS: /[\x00-\x08\x0B-\x0C\x0E-\x1F\x7F]/,
|
|
36
|
+
// Unicode direction override (used in homograph attacks)
|
|
37
|
+
UNICODE_OVERRIDE: /[\u202A-\u202E\u2066-\u2069]/,
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Safe characters for different contexts
|
|
41
|
+
*/
|
|
42
|
+
export const SAFE_PATTERNS = {
|
|
43
|
+
/**
|
|
44
|
+
* Alphanumeric, spaces, and common punctuation
|
|
45
|
+
*/
|
|
46
|
+
BASIC: /^[a-zA-Z0-9\s.,!?'"()\-_]+$/,
|
|
47
|
+
/**
|
|
48
|
+
* Safe for file paths (no directory traversal)
|
|
49
|
+
*/
|
|
50
|
+
FILE_PATH: /^[a-zA-Z0-9/._\-]+$/,
|
|
51
|
+
/**
|
|
52
|
+
* Safe for environment variable values
|
|
53
|
+
*/
|
|
54
|
+
ENV_VALUE: /^[a-zA-Z0-9._\-:/=]+$/,
|
|
55
|
+
/**
|
|
56
|
+
* Printable ASCII only (most restrictive)
|
|
57
|
+
*/
|
|
58
|
+
ASCII_PRINTABLE: /^[\x20-\x7E]+$/,
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Normalize Unicode string to prevent homograph attacks
|
|
62
|
+
*
|
|
63
|
+
* Uses NFC (Canonical Decomposition, followed by Canonical Composition)
|
|
64
|
+
* which is the recommended normalization form for most use cases
|
|
65
|
+
*
|
|
66
|
+
* @param input - String to normalize
|
|
67
|
+
* @returns Normalized string
|
|
68
|
+
*/
|
|
69
|
+
export function normalizeUnicode(input) {
|
|
70
|
+
return input.normalize('NFC');
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Check for dangerous patterns in input
|
|
74
|
+
*
|
|
75
|
+
* @param input - String to check
|
|
76
|
+
* @returns Array of detected dangerous patterns
|
|
77
|
+
*/
|
|
78
|
+
export function detectDangerousPatterns(input) {
|
|
79
|
+
const detected = [];
|
|
80
|
+
if (DANGEROUS_PATTERNS.NULL_BYTE.test(input)) {
|
|
81
|
+
detected.push('Null byte detected');
|
|
82
|
+
}
|
|
83
|
+
if (DANGEROUS_PATTERNS.EXCESSIVE_REPETITION.test(input)) {
|
|
84
|
+
detected.push('Excessive character repetition detected');
|
|
85
|
+
}
|
|
86
|
+
if (DANGEROUS_PATTERNS.CONTROL_CHARS.test(input)) {
|
|
87
|
+
detected.push('Control characters detected');
|
|
88
|
+
}
|
|
89
|
+
if (DANGEROUS_PATTERNS.UNICODE_OVERRIDE.test(input)) {
|
|
90
|
+
detected.push('Unicode direction override detected');
|
|
91
|
+
}
|
|
92
|
+
return detected;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Sanitize general user input with configurable options
|
|
96
|
+
*
|
|
97
|
+
* @param input - Raw input string
|
|
98
|
+
* @param options - Sanitization options
|
|
99
|
+
* @returns Sanitization result
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ```typescript
|
|
103
|
+
* const result = sanitizeInput('User input here', {
|
|
104
|
+
* maxLength: 1000,
|
|
105
|
+
* normalizeUnicode: true,
|
|
106
|
+
* allowedPattern: SAFE_PATTERNS.BASIC,
|
|
107
|
+
* });
|
|
108
|
+
*
|
|
109
|
+
* if (result.valid) {
|
|
110
|
+
* // Use result.value safely
|
|
111
|
+
* } else {
|
|
112
|
+
* console.error(result.error);
|
|
113
|
+
* }
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
export function sanitizeInput(input, options = {}) {
|
|
117
|
+
const { maxLength = MAX_INPUT_LENGTHS.USER_INPUT, normalizeUnicode: shouldNormalize = true, allowedPattern, trim = true, allowEmpty = false, } = options;
|
|
118
|
+
const warnings = [];
|
|
119
|
+
let value = input;
|
|
120
|
+
// 1. Trim if requested
|
|
121
|
+
if (trim) {
|
|
122
|
+
value = value.trim();
|
|
123
|
+
}
|
|
124
|
+
// 2. Check if empty
|
|
125
|
+
if (!allowEmpty && value.length === 0) {
|
|
126
|
+
return {
|
|
127
|
+
valid: false,
|
|
128
|
+
error: 'Input cannot be empty',
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
// 3. Check length BEFORE normalization (prevent DoS via normalization)
|
|
132
|
+
if (value.length > maxLength) {
|
|
133
|
+
return {
|
|
134
|
+
valid: false,
|
|
135
|
+
error: `Input exceeds maximum length of ${maxLength} characters (got ${value.length})`,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
// 4. Unicode normalization (prevent homograph attacks)
|
|
139
|
+
if (shouldNormalize) {
|
|
140
|
+
const normalized = normalizeUnicode(value);
|
|
141
|
+
if (normalized !== value) {
|
|
142
|
+
warnings.push('Input was normalized (Unicode)');
|
|
143
|
+
value = normalized;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// 5. Check for dangerous patterns
|
|
147
|
+
const dangerous = detectDangerousPatterns(value);
|
|
148
|
+
if (dangerous.length > 0) {
|
|
149
|
+
return {
|
|
150
|
+
valid: false,
|
|
151
|
+
error: `Dangerous patterns detected: ${dangerous.join(', ')}`,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
// 6. Apply character whitelist if provided
|
|
155
|
+
if (allowedPattern && !allowedPattern.test(value)) {
|
|
156
|
+
return {
|
|
157
|
+
valid: false,
|
|
158
|
+
error: 'Input contains disallowed characters',
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
valid: true,
|
|
163
|
+
value,
|
|
164
|
+
warnings: warnings.length > 0 ? warnings : undefined,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Sanitize file path input to prevent path traversal
|
|
169
|
+
*
|
|
170
|
+
* @param path - File path to sanitize
|
|
171
|
+
* @returns Sanitization result
|
|
172
|
+
*
|
|
173
|
+
* @example
|
|
174
|
+
* ```typescript
|
|
175
|
+
* const result = sanitizeFilePath('../../../etc/passwd');
|
|
176
|
+
* if (!result.valid) {
|
|
177
|
+
* console.error('Invalid path:', result.error);
|
|
178
|
+
* }
|
|
179
|
+
* ```
|
|
180
|
+
*/
|
|
181
|
+
export function sanitizeFilePath(path) {
|
|
182
|
+
const result = sanitizeInput(path, {
|
|
183
|
+
maxLength: MAX_INPUT_LENGTHS.FILE_PATH,
|
|
184
|
+
trim: true,
|
|
185
|
+
allowEmpty: false,
|
|
186
|
+
});
|
|
187
|
+
if (!result.valid) {
|
|
188
|
+
return result;
|
|
189
|
+
}
|
|
190
|
+
const value = result.value;
|
|
191
|
+
// Additional path-specific checks
|
|
192
|
+
const warnings = result.warnings || [];
|
|
193
|
+
// Check for path traversal patterns
|
|
194
|
+
if (value.includes('..')) {
|
|
195
|
+
warnings.push('Path contains parent directory references (..)');
|
|
196
|
+
}
|
|
197
|
+
// Check for absolute paths (may be intentional, so just warn)
|
|
198
|
+
if (value.startsWith('/') || /^[A-Z]:/i.test(value)) {
|
|
199
|
+
warnings.push('Absolute path detected');
|
|
200
|
+
}
|
|
201
|
+
// Check for hidden files (Unix)
|
|
202
|
+
if (value.split('/').some(part => part.startsWith('.'))) {
|
|
203
|
+
warnings.push('Path contains hidden file/directory');
|
|
204
|
+
}
|
|
205
|
+
return {
|
|
206
|
+
valid: true,
|
|
207
|
+
value,
|
|
208
|
+
warnings: warnings.length > 0 ? warnings : undefined,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Sanitize shell command input
|
|
213
|
+
*
|
|
214
|
+
* NOTE: This is a last line of defense. Prefer execFile over exec
|
|
215
|
+
* and use argument arrays instead of concatenating commands.
|
|
216
|
+
*
|
|
217
|
+
* @param command - Command string to sanitize
|
|
218
|
+
* @returns Sanitization result
|
|
219
|
+
*
|
|
220
|
+
* @example
|
|
221
|
+
* ```typescript
|
|
222
|
+
* const result = sanitizeCommand('ls -la');
|
|
223
|
+
* if (result.valid) {
|
|
224
|
+
* // Still prefer execFile with args array
|
|
225
|
+
* execFile(result.value.split(' ')[0], result.value.split(' ').slice(1));
|
|
226
|
+
* }
|
|
227
|
+
* ```
|
|
228
|
+
*/
|
|
229
|
+
export function sanitizeCommand(command) {
|
|
230
|
+
const result = sanitizeInput(command, {
|
|
231
|
+
maxLength: MAX_INPUT_LENGTHS.COMMAND,
|
|
232
|
+
trim: true,
|
|
233
|
+
allowEmpty: false,
|
|
234
|
+
});
|
|
235
|
+
if (!result.valid) {
|
|
236
|
+
return result;
|
|
237
|
+
}
|
|
238
|
+
const value = result.value;
|
|
239
|
+
// Check for shell metacharacters that could enable injection
|
|
240
|
+
const shellMetaChars = /[;&|`$()<>\\]/;
|
|
241
|
+
if (shellMetaChars.test(value)) {
|
|
242
|
+
return {
|
|
243
|
+
valid: false,
|
|
244
|
+
error: 'Command contains dangerous shell metacharacters',
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
return {
|
|
248
|
+
valid: true,
|
|
249
|
+
value,
|
|
250
|
+
warnings: result.warnings,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Sanitize search query input
|
|
255
|
+
*
|
|
256
|
+
* @param query - Search query to sanitize
|
|
257
|
+
* @returns Sanitization result
|
|
258
|
+
*/
|
|
259
|
+
export function sanitizeSearchQuery(query) {
|
|
260
|
+
return sanitizeInput(query, {
|
|
261
|
+
maxLength: MAX_INPUT_LENGTHS.SEARCH_QUERY,
|
|
262
|
+
trim: true,
|
|
263
|
+
allowEmpty: false,
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Sanitize environment variable value
|
|
268
|
+
*
|
|
269
|
+
* @param value - Environment variable value to sanitize
|
|
270
|
+
* @returns Sanitization result
|
|
271
|
+
*/
|
|
272
|
+
export function sanitizeEnvValue(value) {
|
|
273
|
+
return sanitizeInput(value, {
|
|
274
|
+
maxLength: MAX_INPUT_LENGTHS.ENV_VALUE,
|
|
275
|
+
trim: true,
|
|
276
|
+
allowEmpty: true, // Empty env vars are valid
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Escape shell arguments for safe execution
|
|
281
|
+
*
|
|
282
|
+
* NOTE: This is a defense-in-depth measure. Always prefer:
|
|
283
|
+
* 1. execFile with argument array over exec
|
|
284
|
+
* 2. Argument validation/whitelisting
|
|
285
|
+
* 3. This escaping function as a last resort
|
|
286
|
+
*
|
|
287
|
+
* @param arg - Argument to escape
|
|
288
|
+
* @returns Safely escaped argument
|
|
289
|
+
*/
|
|
290
|
+
export function escapeShellArg(arg) {
|
|
291
|
+
// On Windows, use double quotes
|
|
292
|
+
if (process.platform === 'win32') {
|
|
293
|
+
// Escape double quotes and backslashes
|
|
294
|
+
return `"${arg.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
|
|
295
|
+
}
|
|
296
|
+
// On Unix, use single quotes (safest - no interpolation)
|
|
297
|
+
// To include a single quote, end the quote, add escaped quote, resume quote
|
|
298
|
+
return `'${arg.replace(/'/g, "'\\''")}'`;
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Validate regex pattern for ReDoS protection
|
|
302
|
+
*
|
|
303
|
+
* Checks for common ReDoS patterns:
|
|
304
|
+
* - Nested quantifiers (e.g., (a+)+)
|
|
305
|
+
* - Alternation with overlapping patterns
|
|
306
|
+
* - Excessive backtracking potential
|
|
307
|
+
*
|
|
308
|
+
* @param pattern - Regex pattern to validate
|
|
309
|
+
* @returns Validation result
|
|
310
|
+
*
|
|
311
|
+
* @example
|
|
312
|
+
* ```typescript
|
|
313
|
+
* const result = validateRegexPattern('(a+)+b');
|
|
314
|
+
* if (!result.valid) {
|
|
315
|
+
* console.error('Unsafe regex:', result.error);
|
|
316
|
+
* }
|
|
317
|
+
* ```
|
|
318
|
+
*/
|
|
319
|
+
export function validateRegexPattern(pattern) {
|
|
320
|
+
// Check length first
|
|
321
|
+
if (pattern.length > 1000) {
|
|
322
|
+
return {
|
|
323
|
+
valid: false,
|
|
324
|
+
error: 'Regex pattern too long (max 1000 characters)',
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
const warnings = [];
|
|
328
|
+
// Check for nested quantifiers (major ReDoS risk)
|
|
329
|
+
// Pattern: quantifier inside a group that is itself quantified
|
|
330
|
+
const nestedQuantifiers = /\([^)]*[*+?{][^)]*\)[*+?{]/;
|
|
331
|
+
if (nestedQuantifiers.test(pattern)) {
|
|
332
|
+
return {
|
|
333
|
+
valid: false,
|
|
334
|
+
error: 'Regex contains nested quantifiers (ReDoS risk)',
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
// Check for excessive alternation
|
|
338
|
+
const alternations = pattern.split('|');
|
|
339
|
+
if (alternations.length > 20) {
|
|
340
|
+
warnings.push('Regex has many alternations (may be slow)');
|
|
341
|
+
}
|
|
342
|
+
// Check for backreferences (can cause exponential backtracking)
|
|
343
|
+
if (/\\[1-9]/.test(pattern)) {
|
|
344
|
+
warnings.push('Regex contains backreferences (may be slow)');
|
|
345
|
+
}
|
|
346
|
+
// Try to compile the regex to catch syntax errors
|
|
347
|
+
try {
|
|
348
|
+
new RegExp(pattern);
|
|
349
|
+
}
|
|
350
|
+
catch (error) {
|
|
351
|
+
return {
|
|
352
|
+
valid: false,
|
|
353
|
+
error: `Invalid regex: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
return {
|
|
357
|
+
valid: true,
|
|
358
|
+
value: pattern,
|
|
359
|
+
warnings: warnings.length > 0 ? warnings : undefined,
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
//# sourceMappingURL=input-sanitizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input-sanitizer.js","sourceRoot":"","sources":["../../src/utils/input-sanitizer.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* JSON Parsing Utilities
|
|
3
3
|
* Centralized JSON operations with validation and error handling
|
|
4
|
+
*
|
|
5
|
+
* Security: REQ-SEC-005 - Prototype Pollution Prevention
|
|
6
|
+
* - Sanitizes dangerous keys (__proto__, constructor, prototype)
|
|
7
|
+
* - Validates JSON structure before use
|
|
8
|
+
* - Prevents object property injection attacks
|
|
4
9
|
*/
|
|
5
10
|
import { z } from 'zod';
|
|
6
11
|
/**
|
|
@@ -57,3 +62,11 @@ export declare function parseJsonWithFallback<T>(jsonString: string, fallback: T
|
|
|
57
62
|
* Parse JSON file with fallback value on error
|
|
58
63
|
*/
|
|
59
64
|
export declare function parseJsonFileWithFallback<T>(filePath: string, fallback: T, schema?: z.ZodSchema<T>): T;
|
|
65
|
+
/**
|
|
66
|
+
* Sanitize an object to prevent prototype pollution
|
|
67
|
+
* Exported for testing purposes
|
|
68
|
+
*
|
|
69
|
+
* @param obj - Object to sanitize
|
|
70
|
+
* @returns Sanitized object
|
|
71
|
+
*/
|
|
72
|
+
export declare function sanitizeJson<T>(obj: T): T;
|
package/dist/utils/json-utils.js
CHANGED
|
@@ -1,15 +1,59 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* JSON Parsing Utilities
|
|
3
3
|
* Centralized JSON operations with validation and error handling
|
|
4
|
+
*
|
|
5
|
+
* Security: REQ-SEC-005 - Prototype Pollution Prevention
|
|
6
|
+
* - Sanitizes dangerous keys (__proto__, constructor, prototype)
|
|
7
|
+
* - Validates JSON structure before use
|
|
8
|
+
* - Prevents object property injection attacks
|
|
4
9
|
*/
|
|
5
10
|
import { readFileSync, writeFileSync, renameSync, unlinkSync, existsSync, copyFileSync, mkdirSync } from 'fs';
|
|
6
11
|
import { dirname } from 'path';
|
|
12
|
+
/**
|
|
13
|
+
* Dangerous keys that can cause prototype pollution
|
|
14
|
+
* These keys should never be allowed in parsed JSON
|
|
15
|
+
*/
|
|
16
|
+
const DANGEROUS_KEYS = ['__proto__', 'constructor', 'prototype'];
|
|
17
|
+
/**
|
|
18
|
+
* Sanitize parsed JSON by removing dangerous keys
|
|
19
|
+
* Prevents prototype pollution attacks (REQ-SEC-005)
|
|
20
|
+
*
|
|
21
|
+
* @param obj - Object to sanitize (recursively)
|
|
22
|
+
* @returns Sanitized object with dangerous keys removed
|
|
23
|
+
*/
|
|
24
|
+
function sanitizeObject(obj) {
|
|
25
|
+
if (obj === null || typeof obj !== 'object') {
|
|
26
|
+
return obj;
|
|
27
|
+
}
|
|
28
|
+
// Handle arrays
|
|
29
|
+
if (Array.isArray(obj)) {
|
|
30
|
+
return obj.map(item => sanitizeObject(item));
|
|
31
|
+
}
|
|
32
|
+
// Handle objects
|
|
33
|
+
const sanitized = {};
|
|
34
|
+
for (const key in obj) {
|
|
35
|
+
// Skip dangerous keys
|
|
36
|
+
if (DANGEROUS_KEYS.includes(key)) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
// Skip inherited properties
|
|
40
|
+
if (!Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
// Recursively sanitize nested objects
|
|
44
|
+
const value = obj[key];
|
|
45
|
+
sanitized[key] = sanitizeObject(value);
|
|
46
|
+
}
|
|
47
|
+
return sanitized;
|
|
48
|
+
}
|
|
7
49
|
/**
|
|
8
50
|
* Parse JSON string with Zod schema validation
|
|
9
51
|
*/
|
|
10
52
|
export function parseJson(jsonString, schema) {
|
|
11
53
|
try {
|
|
12
|
-
const
|
|
54
|
+
const rawData = JSON.parse(jsonString);
|
|
55
|
+
// SECURITY: Sanitize to prevent prototype pollution (REQ-SEC-005)
|
|
56
|
+
const data = sanitizeObject(rawData);
|
|
13
57
|
if (schema) {
|
|
14
58
|
const result = schema.safeParse(data);
|
|
15
59
|
if (!result.success) {
|
|
@@ -169,4 +213,14 @@ export function parseJsonFileWithFallback(filePath, fallback, schema) {
|
|
|
169
213
|
const result = parseJsonFile(filePath, schema);
|
|
170
214
|
return result.success ? result.data : fallback;
|
|
171
215
|
}
|
|
216
|
+
/**
|
|
217
|
+
* Sanitize an object to prevent prototype pollution
|
|
218
|
+
* Exported for testing purposes
|
|
219
|
+
*
|
|
220
|
+
* @param obj - Object to sanitize
|
|
221
|
+
* @returns Sanitized object
|
|
222
|
+
*/
|
|
223
|
+
export function sanitizeJson(obj) {
|
|
224
|
+
return sanitizeObject(obj);
|
|
225
|
+
}
|
|
172
226
|
//# sourceMappingURL=json-utils.js.map
|