129
tests/cache-manager.test.js
Archivo normal
129
tests/cache-manager.test.js
Archivo normal
@@ -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
209
tests/security-manager.test.js
Archivo normal
@@ -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
24
tests/setup.js
Archivo normal
@@ -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()
|
||||
};
|
||||
Referencia en una nueva incidencia
Block a user