test-error-sanitization.js•7.7 kB
import assert from 'assert';
import path from 'path';
// Local implementation of sanitizeError for testing
// This mirrors the implementation in src/utils/capture.ts but avoids import issues
// when running tests. The actual sanitization logic is identical.
//
// NOTE: If you update the sanitizeError function in src/utils/capture.ts,
// be sure to update this implementation as well to keep tests accurate.
function sanitizeError(error) {
let errorMessage = '';
let errorCode = undefined;
if (error instanceof Error) {
// Extract just the error name and message without stack trace
errorMessage = error.name + ': ' + error.message;
// Extract error code if available (common in Node.js errors)
if ('code' in error) {
errorCode = error.code;
}
} else if (typeof error === 'string') {
errorMessage = error;
} else if (error && error.message) {
errorMessage = error.message;
} else {
errorMessage = 'Unknown error';
}
// Remove any file paths using regex
// This pattern matches common path formats including Windows and Unix-style paths
errorMessage = errorMessage.replace(/(?:\/|\\)[\w\d_.-\/\\]+/g, '[PATH]');
errorMessage = errorMessage.replace(/[A-Za-z]:\\[\w\d_.-\/\\]+/g, '[PATH]');
return {
message: errorMessage,
code: errorCode
};
}
// Helper function to run a test and report results
const runTest = (name, testFn) => {
try {
testFn();
console.log(`✅ Test passed: ${name}`);
return true;
} catch (error) {
console.error(`❌ Test failed: ${name}`);
console.error(error);
return false;
}
};
// Main test function that will be exported
const runAllTests = async () => {
let allPassed = true;
// Test sanitization of error objects with file paths
allPassed = runTest('sanitizeError - Error object with path', () => {
const mockError = new Error('Failed to read file at /Users/username/sensitive/path/file.txt');
const sanitized = sanitizeError(mockError);
assert(!sanitized.message.includes('/Users/username/sensitive/path/file.txt'), 'Error message should not contain file path');
assert(sanitized.message.includes('[PATH]'), 'Error message should replace path with [PATH]');
}) && allPassed;
// Test sanitization of Windows-style paths
allPassed = runTest('sanitizeError - Windows path', () => {
const mockError = new Error('Failed to read file at C:\\Users\\username\\Documents\\file.txt');
const sanitized = sanitizeError(mockError);
assert(!sanitized.message.includes('C:\\Users\\username\\Documents\\file.txt'), 'Error message should not contain Windows file path');
assert(sanitized.message.includes('[PATH]'), 'Error message should replace Windows path with [PATH]');
}) && allPassed;
// Test sanitization of error with multiple paths
allPassed = runTest('sanitizeError - Multiple paths', () => {
const mockError = new Error('Failed to move file from /path/source.txt to /path/destination.txt');
const sanitized = sanitizeError(mockError);
assert(!sanitized.message.includes('/path/source.txt'), 'Error message should not contain source path');
assert(!sanitized.message.includes('/path/destination.txt'), 'Error message should not contain destination path');
assert(sanitized.message.includes('[PATH]'), 'Error message should replace paths with [PATH]');
}) && allPassed;
// Test sanitization of string errors
allPassed = runTest('sanitizeError - String error', () => {
const errorString = 'Cannot access /var/log/sensitive/data.log due to permissions';
const sanitized = sanitizeError(errorString);
assert(!sanitized.message.includes('/var/log/sensitive/data.log'), 'String error should not contain file path');
assert(sanitized.message.includes('[PATH]'), 'String error should replace path with [PATH]');
}) && allPassed;
// Test error code preservation
allPassed = runTest('sanitizeError - Error code preservation', () => {
const mockError = new Error('ENOENT: no such file or directory, open \'/path/to/file.txt\'');
mockError.code = 'ENOENT';
const sanitized = sanitizeError(mockError);
assert(sanitized.code === 'ENOENT', 'Error code should be preserved');
assert(!sanitized.message.includes('/path/to/file.txt'), 'Error message should not contain file path');
}) && allPassed;
// Test path with special characters
allPassed = runTest('sanitizeError - Path with special characters', () => {
const mockError = new Error('Failed to process /path/with-special_chars/file!@#$%.txt');
const sanitized = sanitizeError(mockError);
assert(!sanitized.message.includes('/path/with-special_chars/file!@#$%.txt'), 'Error message should sanitize paths with special characters');
}) && allPassed;
// Test non-error input
allPassed = runTest('sanitizeError - Non-error input', () => {
const nonError = { custom: 'object' };
const sanitized = sanitizeError(nonError);
assert(sanitized.message === 'Unknown error', 'Non-error objects should be handled gracefully');
}) && allPassed;
// Test actual paths from the current environment
allPassed = runTest('sanitizeError - Actual system paths', () => {
const currentDir = process.cwd();
const homeDir = process.env.HOME || process.env.USERPROFILE;
const mockError = new Error(`Failed to operate on ${currentDir} or ${homeDir}`);
const sanitized = sanitizeError(mockError);
assert(!sanitized.message.includes(currentDir), 'Error message should not contain current directory');
assert(!sanitized.message.includes(homeDir), 'Error message should not contain home directory');
}) && allPassed;
// Integration test with capture function mock
allPassed = runTest('Integration - capture with error object', () => {
// Create a mock capture function to test integration
const mockCapture = (event, properties) => {
// Check that no file paths are in the properties
const stringified = JSON.stringify(properties);
const containsPaths = /(?:\/|\\)[\w\d_.-\/\\]+/.test(stringified) ||
/[A-Za-z]:\\[\w\d_.-\/\\]+/.test(stringified);
assert(!containsPaths, 'Capture properties should not contain file paths');
return properties;
};
// Create an error with file path
const mockError = new Error(`Failed to read ${process.cwd()}/sensitive/file.txt`);
// Manually sanitize for test
const sanitizedError = sanitizeError(mockError).message;
// Call the mock capture with the error
const properties = mockCapture('test_event', {
error: sanitizedError,
operation: 'read_file'
});
// Verify the error was properly processed
assert(typeof properties.error === 'string', 'Error property should be a string');
assert(!properties.error.includes(process.cwd()), 'Error should not contain file path');
}) && allPassed;
console.log('All error sanitization tests complete.');
return allPassed;
};
// Run tests if this file is executed directly
if (process.argv[1] === import.meta.url) {
runAllTests().then(success => {
process.exit(success ? 0 : 1);
});
}
// Export the test function for the test runner
export default runAllTests;