UNPKG

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
"use strict"; 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