@unrdf/hooks 5.0.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/LICENSE +21 -0
- package/README.md +86 -0
- package/package.json +70 -0
- package/src/hooks/builtin-hooks.mjs +296 -0
- package/src/hooks/condition-cache.mjs +109 -0
- package/src/hooks/condition-evaluator.mjs +722 -0
- package/src/hooks/define-hook.mjs +211 -0
- package/src/hooks/effect-sandbox-worker.mjs +170 -0
- package/src/hooks/effect-sandbox.mjs +517 -0
- package/src/hooks/file-resolver.mjs +387 -0
- package/src/hooks/hook-chain-compiler.mjs +236 -0
- package/src/hooks/hook-executor-batching.mjs +277 -0
- package/src/hooks/hook-executor.mjs +465 -0
- package/src/hooks/hook-management.mjs +202 -0
- package/src/hooks/hook-scheduler.mjs +413 -0
- package/src/hooks/knowledge-hook-engine.mjs +358 -0
- package/src/hooks/knowledge-hook-manager.mjs +269 -0
- package/src/hooks/observability.mjs +531 -0
- package/src/hooks/policy-pack.mjs +572 -0
- package/src/hooks/quad-pool.mjs +249 -0
- package/src/hooks/quality-metrics.mjs +544 -0
- package/src/hooks/security/error-sanitizer.mjs +257 -0
- package/src/hooks/security/path-validator.mjs +194 -0
- package/src/hooks/security/sandbox-restrictions.mjs +331 -0
- package/src/hooks/telemetry.mjs +167 -0
- package/src/index.mjs +101 -0
- package/src/security/sandbox/browser-executor.mjs +220 -0
- package/src/security/sandbox/detector.mjs +342 -0
- package/src/security/sandbox/isolated-vm-executor.mjs +373 -0
- package/src/security/sandbox/vm2-executor.mjs +217 -0
- package/src/security/sandbox/worker-executor-runtime.mjs +74 -0
- package/src/security/sandbox/worker-executor.mjs +212 -0
- package/src/security/sandbox-adapter.mjs +141 -0
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Error Message Sanitizer
|
|
3
|
+
* @module error-sanitizer
|
|
4
|
+
*
|
|
5
|
+
* @description
|
|
6
|
+
* Sanitizes error messages to prevent information disclosure vulnerabilities.
|
|
7
|
+
* Removes sensitive data like passwords, file paths, stack traces, and environment variables.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { z } from 'zod';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Schema for sanitization options
|
|
14
|
+
*/
|
|
15
|
+
const SanitizationOptionsSchema = z
|
|
16
|
+
.object({
|
|
17
|
+
removeStackTraces: z.boolean().default(true),
|
|
18
|
+
removeFilePaths: z.boolean().default(true),
|
|
19
|
+
removeCredentials: z.boolean().default(true),
|
|
20
|
+
removeEnvironmentVars: z.boolean().default(true),
|
|
21
|
+
genericErrorMessage: z.string().default('An error occurred'),
|
|
22
|
+
})
|
|
23
|
+
.strict();
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Patterns to detect and sanitize sensitive information
|
|
27
|
+
*/
|
|
28
|
+
const SENSITIVE_PATTERNS = {
|
|
29
|
+
// File paths (absolute paths)
|
|
30
|
+
filePaths: [
|
|
31
|
+
/\/[a-zA-Z0-9_\-./]+\.js(?::\d+:\d+)?/g, // Unix paths with line numbers
|
|
32
|
+
/\\[a-zA-Z0-9_\-.\\]+\.js(?::\d+:\d+)?/g, // Windows paths
|
|
33
|
+
/\/app\/[^\s]*/g, // Docker app paths
|
|
34
|
+
/\/usr\/[^\s]*/g, // System paths
|
|
35
|
+
/\/etc\/[^\s]*/g,
|
|
36
|
+
/\/var\/[^\s]*/g,
|
|
37
|
+
/\/home\/[^\s]*/g,
|
|
38
|
+
/C:\\[^\s]*/g, // Windows paths
|
|
39
|
+
/D:\\[^\s]*/g,
|
|
40
|
+
],
|
|
41
|
+
|
|
42
|
+
// Database connection strings
|
|
43
|
+
credentials: [
|
|
44
|
+
/\b(?:postgres|mysql|mongodb):\/\/[^:\s]+:[^@\s]+@[^\s]+/gi, // DB URLs with passwords
|
|
45
|
+
/password\s*[:=]\s*["']?[^"'\s]+["']?/gi, // password= or password:
|
|
46
|
+
/api[_-]?key\s*[:=]\s*["']?[^"'\s]+["']?/gi, // API keys
|
|
47
|
+
/secret\s*[:=]\s*["']?[^"'\s]+["']?/gi, // secrets
|
|
48
|
+
/token\s*[:=]\s*["']?[^"'\s]+["']?/gi, // tokens
|
|
49
|
+
/authorization\s*:\s*["']?[^"'\s]+["']?/gi, // auth headers
|
|
50
|
+
],
|
|
51
|
+
|
|
52
|
+
// Environment variables
|
|
53
|
+
environmentVars: [
|
|
54
|
+
/\bDATABASE_URL\s*=\s*[^\s]+/gi,
|
|
55
|
+
/API_KEY\s*=\s*[^\s]+/gi,
|
|
56
|
+
/SECRET\s*=\s*[^\s]+/gi,
|
|
57
|
+
/PASSWORD\s*=\s*[^\s]+/gi,
|
|
58
|
+
/TOKEN\s*=\s*[^\s]+/gi,
|
|
59
|
+
/\w+_KEY\s*=\s*[^\s]+/gi,
|
|
60
|
+
/\w+_SECRET\s*=\s*[^\s]+/gi,
|
|
61
|
+
],
|
|
62
|
+
|
|
63
|
+
// Stack trace patterns
|
|
64
|
+
stackTraces: [
|
|
65
|
+
/\bat\s+[^\s]+\s+\([^)]+:\d+:\d+\)/g, // at Function (file:line:col)
|
|
66
|
+
/at\s+[^(]+\([^)]+\)/g, // at Function(...)
|
|
67
|
+
/^\s*at\s.+$/gm, // Full stack trace lines
|
|
68
|
+
],
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Error Sanitizer for preventing information disclosure
|
|
73
|
+
*/
|
|
74
|
+
export class ErrorSanitizer {
|
|
75
|
+
/**
|
|
76
|
+
* @param {Object} [options] - Sanitization options
|
|
77
|
+
*/
|
|
78
|
+
constructor(options = {}) {
|
|
79
|
+
const validated = SanitizationOptionsSchema.parse(options);
|
|
80
|
+
this.options = validated;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Sanitize an error message
|
|
85
|
+
* @param {Error | string} error - Error to sanitize
|
|
86
|
+
* @returns {string} Sanitized error message
|
|
87
|
+
*/
|
|
88
|
+
sanitize(error) {
|
|
89
|
+
let message = error instanceof Error ? error.message : String(error);
|
|
90
|
+
|
|
91
|
+
// Remove credentials
|
|
92
|
+
if (this.options.removeCredentials) {
|
|
93
|
+
message = this._removeCredentials(message);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Remove file paths
|
|
97
|
+
if (this.options.removeFilePaths) {
|
|
98
|
+
message = this._removeFilePaths(message);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Remove environment variables
|
|
102
|
+
if (this.options.removeEnvironmentVars) {
|
|
103
|
+
message = this._removeEnvironmentVars(message);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// If sanitization removed too much, return generic message
|
|
107
|
+
if (message.trim().length < 10) {
|
|
108
|
+
return this.options.genericErrorMessage;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return message;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Sanitize a complete error object
|
|
116
|
+
* @param {Error} error - Error object
|
|
117
|
+
* @returns {Object} Sanitized error object
|
|
118
|
+
*/
|
|
119
|
+
sanitizeError(error) {
|
|
120
|
+
if (!error || typeof error !== 'object') {
|
|
121
|
+
return {
|
|
122
|
+
message: this.options.genericErrorMessage,
|
|
123
|
+
sanitized: true,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const sanitized = {
|
|
128
|
+
message: this.sanitize(error.message || 'Unknown error'),
|
|
129
|
+
sanitized: true,
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
// Only include stack if not removing stack traces
|
|
133
|
+
if (!this.options.removeStackTraces && error.stack) {
|
|
134
|
+
sanitized.stack = this._sanitizeStack(error.stack);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return sanitized;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Remove credential patterns from text
|
|
142
|
+
* @param {string} text - Text to sanitize
|
|
143
|
+
* @returns {string} Sanitized text
|
|
144
|
+
* @private
|
|
145
|
+
*/
|
|
146
|
+
_removeCredentials(text) {
|
|
147
|
+
let sanitized = text;
|
|
148
|
+
|
|
149
|
+
for (const pattern of SENSITIVE_PATTERNS.credentials) {
|
|
150
|
+
sanitized = sanitized.replace(pattern, match => {
|
|
151
|
+
if (match.includes('://')) {
|
|
152
|
+
// Replace password in connection string
|
|
153
|
+
return match.replace(/:\/\/[^:]+:[^@]+@/, '://***:***@');
|
|
154
|
+
}
|
|
155
|
+
// Replace entire credential assignment
|
|
156
|
+
return match.split(/[:=]/)[0] + '=***';
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return sanitized;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Remove file path patterns from text
|
|
165
|
+
* @param {string} text - Text to sanitize
|
|
166
|
+
* @returns {string} Sanitized text
|
|
167
|
+
* @private
|
|
168
|
+
*/
|
|
169
|
+
_removeFilePaths(text) {
|
|
170
|
+
let sanitized = text;
|
|
171
|
+
|
|
172
|
+
for (const pattern of SENSITIVE_PATTERNS.filePaths) {
|
|
173
|
+
sanitized = sanitized.replace(pattern, '[file path removed]');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return sanitized;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Remove environment variable patterns from text
|
|
181
|
+
* @param {string} text - Text to sanitize
|
|
182
|
+
* @returns {string} Sanitized text
|
|
183
|
+
* @private
|
|
184
|
+
*/
|
|
185
|
+
_removeEnvironmentVars(text) {
|
|
186
|
+
let sanitized = text;
|
|
187
|
+
|
|
188
|
+
for (const pattern of SENSITIVE_PATTERNS.environmentVars) {
|
|
189
|
+
sanitized = sanitized.replace(pattern, match => {
|
|
190
|
+
return match.split('=')[0] + '=***';
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return sanitized;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Sanitize stack trace
|
|
199
|
+
* @param {string} stack - Stack trace
|
|
200
|
+
* @returns {string} Sanitized stack trace
|
|
201
|
+
* @private
|
|
202
|
+
*/
|
|
203
|
+
_sanitizeStack(stack) {
|
|
204
|
+
let sanitized = stack;
|
|
205
|
+
|
|
206
|
+
// Remove file paths from stack trace
|
|
207
|
+
for (const pattern of SENSITIVE_PATTERNS.filePaths) {
|
|
208
|
+
sanitized = sanitized.replace(pattern, '[sanitized]');
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Remove full stack trace lines if configured
|
|
212
|
+
if (this.options.removeStackTraces) {
|
|
213
|
+
return 'Stack trace removed for security';
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return sanitized;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Check if text contains sensitive information
|
|
221
|
+
* @param {string} text - Text to check
|
|
222
|
+
* @returns {boolean} True if sensitive info detected
|
|
223
|
+
*/
|
|
224
|
+
containsSensitiveInfo(text) {
|
|
225
|
+
for (const patterns of Object.values(SENSITIVE_PATTERNS)) {
|
|
226
|
+
for (const pattern of patterns) {
|
|
227
|
+
if (pattern.test(text)) {
|
|
228
|
+
return true;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Create an error sanitizer instance
|
|
238
|
+
* @param {Object} [options] - Sanitization options
|
|
239
|
+
* @returns {ErrorSanitizer} Sanitizer instance
|
|
240
|
+
*/
|
|
241
|
+
export function createErrorSanitizer(options = {}) {
|
|
242
|
+
return new ErrorSanitizer(options);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Default error sanitizer instance (strict mode)
|
|
247
|
+
*/
|
|
248
|
+
export const defaultErrorSanitizer = new ErrorSanitizer();
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Sanitize an error message (convenience function)
|
|
252
|
+
* @param {Error | string} error - Error to sanitize
|
|
253
|
+
* @returns {string} Sanitized error message
|
|
254
|
+
*/
|
|
255
|
+
export function sanitizeError(error) {
|
|
256
|
+
return defaultErrorSanitizer.sanitize(error);
|
|
257
|
+
}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Path Traversal Validator
|
|
3
|
+
* @module path-validator
|
|
4
|
+
*
|
|
5
|
+
* @description
|
|
6
|
+
* Validates file paths to prevent directory traversal and unauthorized file access attacks.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { z } from 'zod';
|
|
10
|
+
import { resolve, normalize, _isAbsolute } from 'path';
|
|
11
|
+
import { fileURLToPath } from 'url';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Schema for path validation options
|
|
15
|
+
*/
|
|
16
|
+
const PathValidationOptionsSchema = z
|
|
17
|
+
.object({
|
|
18
|
+
basePath: z.string().optional(),
|
|
19
|
+
allowAbsolutePaths: z.boolean().default(false),
|
|
20
|
+
allowedDirectories: z.array(z.string()).default([]),
|
|
21
|
+
blockedDirectories: z
|
|
22
|
+
.array(z.string())
|
|
23
|
+
.default([
|
|
24
|
+
'/etc',
|
|
25
|
+
'/usr',
|
|
26
|
+
'/bin',
|
|
27
|
+
'/sbin',
|
|
28
|
+
'/var',
|
|
29
|
+
'/root',
|
|
30
|
+
'C:\\Windows',
|
|
31
|
+
'C:\\System32',
|
|
32
|
+
'D:\\Windows',
|
|
33
|
+
]),
|
|
34
|
+
})
|
|
35
|
+
.strict();
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Path Validator for preventing traversal attacks
|
|
39
|
+
*/
|
|
40
|
+
export class PathValidator {
|
|
41
|
+
/**
|
|
42
|
+
* @param {Object} [options] - Validation options
|
|
43
|
+
*/
|
|
44
|
+
constructor(options = {}) {
|
|
45
|
+
const validated = PathValidationOptionsSchema.parse(options);
|
|
46
|
+
this.basePath = validated.basePath || process.cwd();
|
|
47
|
+
this.allowAbsolutePaths = validated.allowAbsolutePaths;
|
|
48
|
+
this.allowedDirectories = validated.allowedDirectories;
|
|
49
|
+
this.blockedDirectories = validated.blockedDirectories;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Validate a file URI for path traversal attacks
|
|
54
|
+
* @param {string} uri - File URI to validate
|
|
55
|
+
* @returns {Object} Validation result { valid, violations, sanitizedPath }
|
|
56
|
+
*/
|
|
57
|
+
validateFileUri(uri) {
|
|
58
|
+
const violations = [];
|
|
59
|
+
|
|
60
|
+
if (!uri || typeof uri !== 'string') {
|
|
61
|
+
return {
|
|
62
|
+
valid: false,
|
|
63
|
+
violations: ['Invalid URI: must be a non-empty string'],
|
|
64
|
+
sanitizedPath: null,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
// Convert file:// URI to path
|
|
70
|
+
let filePath = uri;
|
|
71
|
+
if (uri.startsWith('file://')) {
|
|
72
|
+
filePath = fileURLToPath(uri);
|
|
73
|
+
} else if (uri.includes('://')) {
|
|
74
|
+
violations.push('Only file:// URIs are supported');
|
|
75
|
+
return { valid: false, violations, sanitizedPath: null };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Decode URI encoding (including double encoding)
|
|
79
|
+
let decodedPath = filePath;
|
|
80
|
+
let previousPath = '';
|
|
81
|
+
// Decode up to 3 times to catch double/triple encoding
|
|
82
|
+
for (let i = 0; i < 3 && decodedPath !== previousPath; i++) {
|
|
83
|
+
previousPath = decodedPath;
|
|
84
|
+
decodedPath = decodeURIComponent(decodedPath);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Check for null byte injection
|
|
88
|
+
if (decodedPath.includes('\x00') || decodedPath.includes('%00')) {
|
|
89
|
+
violations.push('Null byte injection detected');
|
|
90
|
+
return { valid: false, violations, sanitizedPath: null };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Check for path traversal patterns
|
|
94
|
+
if (this._hasTraversalPattern(decodedPath)) {
|
|
95
|
+
violations.push('Path traversal pattern detected');
|
|
96
|
+
return { valid: false, violations, sanitizedPath: null };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Normalize and resolve path
|
|
100
|
+
const normalizedPath = normalize(decodedPath);
|
|
101
|
+
const resolvedPath = resolve(this.basePath, normalizedPath);
|
|
102
|
+
|
|
103
|
+
// Check if resolved path is within base path
|
|
104
|
+
if (!this.allowAbsolutePaths && !resolvedPath.startsWith(resolve(this.basePath))) {
|
|
105
|
+
violations.push('Path escapes base directory');
|
|
106
|
+
return { valid: false, violations, sanitizedPath: null };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Check against blocked directories
|
|
110
|
+
for (const blockedDir of this.blockedDirectories) {
|
|
111
|
+
if (resolvedPath.startsWith(blockedDir)) {
|
|
112
|
+
violations.push(`Access to blocked directory: ${blockedDir}`);
|
|
113
|
+
return { valid: false, violations, sanitizedPath: null };
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// If allowed directories are specified, check inclusion
|
|
118
|
+
if (this.allowedDirectories.length > 0) {
|
|
119
|
+
const isAllowed = this.allowedDirectories.some(allowedDir =>
|
|
120
|
+
resolvedPath.startsWith(resolve(allowedDir))
|
|
121
|
+
);
|
|
122
|
+
if (!isAllowed) {
|
|
123
|
+
violations.push('Path not in allowed directories');
|
|
124
|
+
return { valid: false, violations, sanitizedPath: null };
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
valid: true,
|
|
130
|
+
violations: [],
|
|
131
|
+
sanitizedPath: resolvedPath,
|
|
132
|
+
};
|
|
133
|
+
} catch (error) {
|
|
134
|
+
violations.push(`Path validation error: ${error.message}`);
|
|
135
|
+
return { valid: false, violations, sanitizedPath: null };
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Check for path traversal patterns
|
|
141
|
+
* @param {string} path - Path to check
|
|
142
|
+
* @returns {boolean} True if traversal pattern detected
|
|
143
|
+
* @private
|
|
144
|
+
*/
|
|
145
|
+
_hasTraversalPattern(path) {
|
|
146
|
+
const traversalPatterns = [
|
|
147
|
+
/\.\.\//g, // ../
|
|
148
|
+
/\.\.\\/g, // ..\
|
|
149
|
+
/\.\.%2f/gi, // ..%2f (URL encoded)
|
|
150
|
+
/\.\.%5c/gi, // ..%5c (URL encoded)
|
|
151
|
+
/\.\.%252f/gi, // ..%252f (double URL encoded)
|
|
152
|
+
/\.\.%255c/gi, // ..%255c (double URL encoded)
|
|
153
|
+
/\.\.%c0%af/gi, // Unicode bypass
|
|
154
|
+
/\.\.%c1%9c/gi, // Unicode bypass
|
|
155
|
+
];
|
|
156
|
+
|
|
157
|
+
return traversalPatterns.some(pattern => pattern.test(path));
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Sanitize a file path (best effort - returns null if unsafe)
|
|
162
|
+
* @param {string} path - Path to sanitize
|
|
163
|
+
* @returns {string | null} Sanitized path or null if unsafe
|
|
164
|
+
*/
|
|
165
|
+
sanitizePath(path) {
|
|
166
|
+
const validation = this.validateFileUri(path);
|
|
167
|
+
return validation.valid ? validation.sanitizedPath : null;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Create a path validator instance
|
|
173
|
+
* @param {Object} [options] - Validation options
|
|
174
|
+
* @returns {PathValidator} Validator instance
|
|
175
|
+
*/
|
|
176
|
+
export function createPathValidator(options = {}) {
|
|
177
|
+
return new PathValidator(options);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Default path validator instance
|
|
182
|
+
*/
|
|
183
|
+
export const defaultPathValidator = new PathValidator();
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Validate a file URI (convenience function)
|
|
187
|
+
* @param {string} uri - URI to validate
|
|
188
|
+
* @param {string} [basePath] - Base path for validation
|
|
189
|
+
* @returns {Object} Validation result
|
|
190
|
+
*/
|
|
191
|
+
export function validatePath(uri, basePath) {
|
|
192
|
+
const validator = basePath ? new PathValidator({ basePath }) : defaultPathValidator;
|
|
193
|
+
return validator.validateFileUri(uri);
|
|
194
|
+
}
|