spanwright
Version:
CLI tool to generate Cloud Spanner E2E testing framework projects with Go database tools and Playwright browser automation
183 lines • 12.2 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
const vitest_1 = require("vitest");
const security_1 = require("../security");
const errors_1 = require("../errors");
const path = __importStar(require("path"));
(0, vitest_1.describe)('Security Module', () => {
(0, vitest_1.describe)('isSafePath', () => {
(0, vitest_1.it)('should return true for safe relative paths', () => {
const basePath = '/home/user/project';
(0, vitest_1.expect)((0, security_1.isSafePath)(basePath, 'file.txt')).toBe(true);
(0, vitest_1.expect)((0, security_1.isSafePath)(basePath, './file.txt')).toBe(true);
(0, vitest_1.expect)((0, security_1.isSafePath)(basePath, 'subdir/file.txt')).toBe(true);
(0, vitest_1.expect)((0, security_1.isSafePath)(basePath, './subdir/file.txt')).toBe(true);
});
(0, vitest_1.it)('should return false for path traversal attempts', () => {
const basePath = '/home/user/project';
(0, vitest_1.expect)((0, security_1.isSafePath)(basePath, '../file.txt')).toBe(false);
(0, vitest_1.expect)((0, security_1.isSafePath)(basePath, '../../file.txt')).toBe(false);
(0, vitest_1.expect)((0, security_1.isSafePath)(basePath, '../../../etc/passwd')).toBe(false);
(0, vitest_1.expect)((0, security_1.isSafePath)(basePath, 'subdir/../../file.txt')).toBe(false);
(0, vitest_1.expect)((0, security_1.isSafePath)(basePath, './subdir/../../../file.txt')).toBe(false);
});
(0, vitest_1.it)('should return false for absolute paths', () => {
const basePath = '/home/user/project';
(0, vitest_1.expect)((0, security_1.isSafePath)(basePath, '/etc/passwd')).toBe(false);
(0, vitest_1.expect)((0, security_1.isSafePath)(basePath, '/home/user/other')).toBe(false);
(0, vitest_1.expect)((0, security_1.isSafePath)(basePath, 'C:\\Windows\\System32')).toBe(true); // On Unix, this is treated as relative path
});
(0, vitest_1.it)('should handle paths with multiple dots correctly', () => {
const basePath = '/home/user/project';
(0, vitest_1.expect)((0, security_1.isSafePath)(basePath, 'file..txt')).toBe(false); // contains ..
(0, vitest_1.expect)((0, security_1.isSafePath)(basePath, 'file...txt')).toBe(false); // contains ..
(0, vitest_1.expect)((0, security_1.isSafePath)(basePath, '...file')).toBe(false); // contains ..
(0, vitest_1.expect)((0, security_1.isSafePath)(basePath, 'dir_name/file_txt')).toBe(true); // safe path
});
(0, vitest_1.it)('should handle empty and special inputs', () => {
const basePath = '/home/user/project';
(0, vitest_1.expect)((0, security_1.isSafePath)(basePath, '')).toBe(true);
(0, vitest_1.expect)((0, security_1.isSafePath)(basePath, '.')).toBe(true);
(0, vitest_1.expect)((0, security_1.isSafePath)(basePath, './')).toBe(true);
});
(0, vitest_1.it)('should handle Windows-style paths', () => {
const basePath = 'C:\\Users\\Project';
(0, vitest_1.expect)((0, security_1.isSafePath)(basePath, 'file.txt')).toBe(true);
(0, vitest_1.expect)((0, security_1.isSafePath)(basePath, 'subdir\\file.txt')).toBe(true);
(0, vitest_1.expect)((0, security_1.isSafePath)(basePath, '..\\file.txt')).toBe(false);
(0, vitest_1.expect)((0, security_1.isSafePath)(basePath, '..\\..\\Windows\\System32')).toBe(false);
});
});
(0, vitest_1.describe)('validatePath', () => {
(0, vitest_1.it)('should not throw for safe paths', () => {
const basePath = '/home/user/project';
(0, vitest_1.expect)(() => (0, security_1.validatePath)(basePath, 'file.txt', 'test')).not.toThrow();
(0, vitest_1.expect)(() => (0, security_1.validatePath)(basePath, './subdir/file.txt', 'test')).not.toThrow();
(0, vitest_1.expect)(() => (0, security_1.validatePath)(basePath, 'deeply/nested/file.txt', 'test')).not.toThrow();
});
(0, vitest_1.it)('should throw SecurityError for path traversal attempts', () => {
const basePath = '/home/user/project';
(0, vitest_1.expect)(() => (0, security_1.validatePath)(basePath, '../file.txt', 'test')).toThrow(errors_1.SecurityError);
(0, vitest_1.expect)(() => (0, security_1.validatePath)(basePath, '../../etc/passwd', 'test')).toThrow(errors_1.SecurityError);
(0, vitest_1.expect)(() => (0, security_1.validatePath)(basePath, '../../../root/.ssh/id_rsa', 'test')).toThrow(errors_1.SecurityError);
});
(0, vitest_1.it)('should throw SecurityError for absolute paths', () => {
const basePath = '/home/user/project';
(0, vitest_1.expect)(() => (0, security_1.validatePath)(basePath, '/etc/passwd', 'test')).toThrow(errors_1.SecurityError);
(0, vitest_1.expect)(() => (0, security_1.validatePath)(basePath, '/root/.ssh/id_rsa', 'test')).toThrow(errors_1.SecurityError);
});
(0, vitest_1.it)('should include operation name in error message', () => {
const basePath = '/home/user/project';
try {
(0, security_1.validatePath)(basePath, '../file.txt', 'copyFile');
vitest_1.expect.fail('Should have thrown');
}
catch (error) {
(0, vitest_1.expect)(error).toBeInstanceOf(errors_1.SecurityError);
(0, vitest_1.expect)(error.message).toContain('copyFile');
(0, vitest_1.expect)(error.path).toBe('../file.txt');
}
});
(0, vitest_1.it)('should handle complex traversal patterns', () => {
const basePath = '/home/user/project';
// These should all throw
const maliciousPaths = [
'subdir/../../../etc/passwd',
'./././../file.txt',
'a/b/c/../../../../../../../etc/passwd',
'normal/path/../../../../../../etc/passwd',
];
// All should throw
(0, vitest_1.expect)(() => (0, security_1.validatePath)(basePath, maliciousPaths[0], 'test')).toThrow(errors_1.SecurityError);
(0, vitest_1.expect)(() => (0, security_1.validatePath)(basePath, maliciousPaths[1], 'test')).toThrow(errors_1.SecurityError);
(0, vitest_1.expect)(() => (0, security_1.validatePath)(basePath, maliciousPaths[2], 'test')).toThrow(errors_1.SecurityError);
(0, vitest_1.expect)(() => (0, security_1.validatePath)(basePath, maliciousPaths[3], 'test')).toThrow(errors_1.SecurityError);
// This is actually safe (weird but safe filenames)
(0, vitest_1.expect)(() => (0, security_1.validatePath)(basePath, '.../.../etc/passwd', 'test')).toThrow(errors_1.SecurityError);
});
});
(0, vitest_1.describe)('getSafePath', () => {
(0, vitest_1.it)('should return normalized absolute path for safe paths', () => {
const basePath = '/home/user/project';
(0, vitest_1.expect)((0, security_1.getSafePath)(basePath, 'file.txt')).toBe(path.resolve(basePath, 'file.txt'));
(0, vitest_1.expect)((0, security_1.getSafePath)(basePath, './subdir/file.txt')).toBe(path.resolve(basePath, './subdir/file.txt'));
});
(0, vitest_1.it)('should throw SecurityError for unsafe paths', () => {
const basePath = '/home/user/project';
(0, vitest_1.expect)(() => (0, security_1.getSafePath)(basePath, '../file.txt')).toThrow(errors_1.SecurityError);
(0, vitest_1.expect)(() => (0, security_1.getSafePath)(basePath, '/etc/passwd')).toThrow(errors_1.SecurityError);
});
(0, vitest_1.it)('should normalize paths correctly', () => {
const basePath = '/home/user/project';
(0, vitest_1.expect)((0, security_1.getSafePath)(basePath, './././file.txt')).toBe(path.resolve(basePath, 'file.txt'));
(0, vitest_1.expect)((0, security_1.getSafePath)(basePath, 'subdir/../file.txt')).toBe(path.resolve(basePath, 'file.txt'));
(0, vitest_1.expect)((0, security_1.getSafePath)(basePath, './subdir/./nested/./file.txt')).toBe(path.resolve(basePath, 'subdir/nested/file.txt'));
});
});
(0, vitest_1.describe)('Edge Cases and Security Scenarios', () => {
(0, vitest_1.it)('should handle symbolic link traversal attempts', () => {
const basePath = '/home/user/project';
// These patterns might be used to try to escape via symlinks
(0, vitest_1.expect)((0, security_1.isSafePath)(basePath, 'symlink/../../../etc/passwd')).toBe(false);
(0, vitest_1.expect)((0, security_1.isSafePath)(basePath, './symlink/../outside')).toBe(true); // This stays within bounds
});
(0, vitest_1.it)('should handle URL-encoded path traversal attempts', () => {
const basePath = '/home/user/project';
// Note: These won't be decoded by our function, so they're "safe" as literal filenames
(0, vitest_1.expect)((0, security_1.isSafePath)(basePath, '%2e%2e%2f')).toBe(true); // Literal filename, not traversal
(0, vitest_1.expect)((0, security_1.isSafePath)(basePath, '..%2f')).toBe(false); // Contains .. which is detected
});
(0, vitest_1.it)('should handle very long paths', () => {
const basePath = '/home/user/project';
const longPath = 'a/'.repeat(100) + 'file.txt';
(0, vitest_1.expect)(() => (0, security_1.validatePath)(basePath, longPath, 'test')).not.toThrow();
});
(0, vitest_1.it)('should handle Unicode in paths', () => {
const basePath = '/home/user/project';
(0, vitest_1.expect)((0, security_1.isSafePath)(basePath, '文件.txt')).toBe(true);
(0, vitest_1.expect)((0, security_1.isSafePath)(basePath, 'папка/файл.txt')).toBe(true);
(0, vitest_1.expect)((0, security_1.isSafePath)(basePath, '📁/📄.txt')).toBe(true);
(0, vitest_1.expect)((0, security_1.isSafePath)(basePath, '../文件.txt')).toBe(false);
});
(0, vitest_1.it)('should handle paths with spaces', () => {
const basePath = '/home/user/my project';
(0, vitest_1.expect)((0, security_1.isSafePath)(basePath, 'my file.txt')).toBe(true);
(0, vitest_1.expect)((0, security_1.isSafePath)(basePath, 'my folder/my file.txt')).toBe(true);
(0, vitest_1.expect)((0, security_1.isSafePath)(basePath, '../my file.txt')).toBe(false);
});
});
});
//# sourceMappingURL=security.test.js.map