initial commit

Signed-off-by: ale <ale@manalejandro.com>
Este commit está contenido en:
ale
2025-08-19 02:22:37 +02:00
commit 2e6d9b4306
Se han modificado 20 ficheros con 5533 adiciones y 0 borrados

129
tests/cache-manager.test.js Archivo normal
Ver fichero

@@ -0,0 +1,129 @@
const CacheManager = require('../src/cache/cache-manager');
const path = require('path');
const fs = require('fs-extra');
describe('CacheManager', () => {
let cacheManager;
let testCacheDir;
beforeEach(async () => {
testCacheDir = path.join(global.TEST_DIR, 'cache');
cacheManager = new CacheManager();
cacheManager.cacheDir = testCacheDir;
cacheManager.metadataFile = path.join(testCacheDir, 'metadata.json');
await cacheManager.init();
});
describe('init', () => {
test('should create cache directory', async () => {
expect(await fs.pathExists(testCacheDir)).toBe(true);
});
test('should create metadata file', async () => {
expect(await fs.pathExists(cacheManager.metadataFile)).toBe(true);
});
});
describe('store and get', () => {
test('should store and retrieve package data', async () => {
const packageName = 'test-package';
const version = '1.0.0';
const data = Buffer.from('test package data');
await cacheManager.store(packageName, version, data);
const retrieved = await cacheManager.get(packageName, version);
expect(retrieved).toEqual(data);
});
test('should return null for non-existent package', async () => {
const retrieved = await cacheManager.get('non-existent', '1.0.0');
expect(retrieved).toBeNull();
});
test('should handle binary data correctly', async () => {
const packageName = 'binary-package';
const version = '1.0.0';
const binaryData = Buffer.from([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]);
await cacheManager.store(packageName, version, binaryData);
const retrieved = await cacheManager.get(packageName, version);
expect(retrieved).toEqual(binaryData);
});
});
describe('remove', () => {
test('should remove package from cache', async () => {
const packageName = 'test-package';
const version = '1.0.0';
const data = Buffer.from('test data');
await cacheManager.store(packageName, version, data);
expect(await cacheManager.get(packageName, version)).toEqual(data);
const removed = await cacheManager.remove(packageName, version);
expect(removed).toBe(true);
expect(await cacheManager.get(packageName, version)).toBeNull();
});
test('should return false for non-existent package', async () => {
const removed = await cacheManager.remove('non-existent', '1.0.0');
expect(removed).toBe(false);
});
});
describe('verify', () => {
test('should verify cache integrity', async () => {
const packageName = 'test-package';
const version = '1.0.0';
const data = Buffer.from('test data');
await cacheManager.store(packageName, version, data);
const result = await cacheManager.verify();
expect(result.corrupted).toBe(0);
expect(result.missing).toBe(0);
expect(result.valid).toBe(1);
});
});
describe('clean', () => {
test('should clean entire cache', async () => {
const packageName = 'test-package';
const version = '1.0.0';
const data = Buffer.from('test data');
await cacheManager.store(packageName, version, data);
expect(await cacheManager.get(packageName, version)).toEqual(data);
const cleanedSize = await cacheManager.clean();
expect(cleanedSize).toBeGreaterThan(0);
expect(await cacheManager.get(packageName, version)).toBeNull();
});
});
describe('getStats', () => {
test('should return cache statistics', async () => {
const stats = await cacheManager.getStats();
expect(stats).toHaveProperty('totalEntries');
expect(stats).toHaveProperty('totalSize');
expect(stats).toHaveProperty('compressionRatio');
expect(typeof stats.totalEntries).toBe('number');
expect(typeof stats.totalSize).toBe('number');
});
test('should calculate compression ratio correctly', async () => {
const packageName = 'test-package';
const version = '1.0.0';
const data = Buffer.from('test data '.repeat(100)); // Repeatable data for good compression
await cacheManager.store(packageName, version, data);
const stats = await cacheManager.getStats();
expect(stats.totalEntries).toBe(1);
expect(stats.compressionRatio).toBeLessThan(1); // Should be compressed
});
});
});

209
tests/security-manager.test.js Archivo normal
Ver fichero

@@ -0,0 +1,209 @@
const SecurityManager = require('../src/security/security-manager');
describe('SecurityManager', () => {
let securityManager;
beforeEach(() => {
securityManager = new SecurityManager();
});
describe('verifyIntegrity', () => {
test('should verify valid integrity hash', async () => {
const data = Buffer.from('test data');
const crypto = require('crypto');
const hash = crypto.createHash('sha256').update(data).digest('base64');
const integrity = `sha256-${hash}`;
const result = await securityManager.verifyIntegrity(data, integrity);
expect(result).toBe(true);
});
test('should reject invalid integrity hash', async () => {
const data = Buffer.from('test data');
const integrity = 'sha256-invalidhash';
await expect(securityManager.verifyIntegrity(data, integrity))
.rejects.toThrow('Package integrity verification failed');
});
test('should reject invalid integrity format', async () => {
const data = Buffer.from('test data');
const integrity = 'invalid-format';
await expect(securityManager.verifyIntegrity(data, integrity))
.rejects.toThrow('Invalid integrity format');
});
test('should reject unsupported hash algorithm', async () => {
const data = Buffer.from('test data');
const integrity = 'sha1-somehash'; // sha1 is not in allowedHashAlgorithms
await expect(securityManager.verifyIntegrity(data, integrity))
.rejects.toThrow('Unsupported hash algorithm: sha1');
});
});
describe('checkPackageSize', () => {
test('should allow packages under size limit', async () => {
const smallData = Buffer.from('small package');
const result = await securityManager.checkPackageSize(smallData);
expect(result).toBe(true);
});
test('should reject packages over size limit', async () => {
// Create a large buffer (larger than 100MB default limit)
const largeSize = 101 * 1024 * 1024; // 101MB
const largeData = Buffer.alloc(largeSize);
await expect(securityManager.checkPackageSize(largeData))
.rejects.toThrow('Package size exceeds maximum allowed');
});
});
describe('checkBlockedPackages', () => {
test('should allow non-blocked packages', async () => {
const result = await securityManager.checkBlockedPackages('safe-package');
expect(result).toBe(true);
});
test('should reject blocked packages', async () => {
securityManager.securityConfig.blockedPackages.add('blocked-package');
await expect(securityManager.checkBlockedPackages('blocked-package'))
.rejects.toThrow('Package "blocked-package" is blocked for security reasons');
});
});
describe('scanPackageContent', () => {
test('should detect suspicious patterns', async () => {
const suspiciousCode = Buffer.from(`
const fs = require('fs');
eval('malicious code');
require('child_process').exec('rm -rf /');
`);
const indicators = await securityManager.scanPackageContent(suspiciousCode, 'test-package');
expect(indicators.length).toBeGreaterThan(0);
expect(indicators.some(i => i.pattern.includes('eval'))).toBe(true);
});
test('should not flag clean code', async () => {
const cleanCode = Buffer.from(`
function add(a, b) {
return a + b;
}
module.exports = { add };
`);
const indicators = await securityManager.scanPackageContent(cleanCode, 'clean-package');
expect(indicators.length).toBe(0);
});
test('should detect obfuscated code', async () => {
const obfuscatedCode = Buffer.from(`
var _0x1234567890abcdefghijklmnop = ['\\x65\\x76\\x61\\x6c'];
function _0x1234567890abcdefghijklmnopqrstuvwxyz() { return _0x1234567890abcdefghijklmnop; }
var _0x9abcdefghijklmnopqrstuvwxyz0123456789 = _0x1234567890abcdefghijklmnopqrstuvwxyz();
_0x9abcdefghijklmnopqrstuvwxyz0123456789[0]('\\u0061\\u006c\\u0065\\u0072\\u0074("test")');
unescape('%61%6c%65%72%74');
String.fromCharCode(97,108,101,114,116);
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
`);
const indicators = await securityManager.scanPackageContent(obfuscatedCode, 'obfuscated-package');
expect(indicators.length).toBeGreaterThan(0);
});
});
describe('generatePackageHash', () => {
test('should generate consistent hash', async () => {
const data = Buffer.from('test data');
const hash1 = await securityManager.generatePackageHash(data);
const hash2 = await securityManager.generatePackageHash(data);
expect(hash1).toBe(hash2);
expect(typeof hash1).toBe('string');
expect(hash1.length).toBeGreaterThan(0);
});
test('should generate different hashes for different data', async () => {
const data1 = Buffer.from('test data 1');
const data2 = Buffer.from('test data 2');
const hash1 = await securityManager.generatePackageHash(data1);
const hash2 = await securityManager.generatePackageHash(data2);
expect(hash1).not.toBe(hash2);
});
});
describe('createIntegrityString', () => {
test('should create valid integrity string', async () => {
const data = Buffer.from('test data');
const integrity = await securityManager.createIntegrityString(data);
expect(integrity).toMatch(/^sha512-.+$/);
// Verify it can be used for verification
const isValid = await securityManager.verifyIntegrity(data, integrity);
expect(isValid).toBe(true);
});
});
describe('assessPackageRisk', () => {
test('should assess risk for new package', () => {
const pkg = {
name: 'new-package',
time: { created: Date.now() - (20 * 24 * 60 * 60 * 1000) }, // 20 days ago
downloads: { weekly: 50 },
maintainers: []
};
const scanResults = [];
const risk = securityManager.assessPackageRisk(pkg, scanResults);
expect(risk.score).toBeGreaterThan(0);
expect(risk.level).toBe('high'); // Score is 5: 2 (new) + 2 (low downloads) + 1 (no maintainers)
expect(risk.factors).toContain('Package is very new (< 30 days)');
expect(risk.factors).toContain('Low download count');
expect(risk.factors).toContain('No maintainers');
});
test('should assess lower risk for established package', () => {
const pkg = {
name: 'established-package',
time: { created: Date.now() - (365 * 24 * 60 * 60 * 1000) }, // 1 year ago
downloads: { weekly: 10000 },
maintainers: [{ name: 'maintainer1' }],
dependencies: {}
};
const scanResults = [];
const risk = securityManager.assessPackageRisk(pkg, scanResults);
expect(risk.score).toBe(0);
expect(risk.level).toBe('low');
expect(risk.factors).toHaveLength(0);
});
test('should increase risk for suspicious scan results', () => {
const pkg = {
name: 'suspicious-package',
time: { created: Date.now() - (365 * 24 * 60 * 60 * 1000) },
downloads: { weekly: 1000 },
maintainers: [{ name: 'maintainer1' }]
};
const scanResults = [
{ severity: 'high' },
{ severity: 'medium' }
];
const risk = securityManager.assessPackageRisk(pkg, scanResults);
expect(risk.score).toBe(4); // 3 for high + 1 for medium
expect(risk.level).toBe('high');
expect(risk.factors).toContain('2 suspicious patterns detected');
});
});
});

24
tests/setup.js Archivo normal
Ver fichero

@@ -0,0 +1,24 @@
// Test setup file
const fs = require('fs-extra');
const path = require('path');
const os = require('os');
// Create temporary directory for tests
global.TEST_DIR = path.join(os.tmpdir(), 'alepm-tests');
beforeEach(async () => {
await fs.ensureDir(global.TEST_DIR);
});
afterEach(async () => {
await fs.remove(global.TEST_DIR);
});
// Mock console methods to avoid noise in tests
global.console = {
...console,
log: jest.fn(),
info: jest.fn(),
warn: jest.fn(),
error: jest.fn()
};