201 lines
6.2 KiB
TypeScript
201 lines
6.2 KiB
TypeScript
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
|
import * as plugins from '../ts/plugins.js';
|
|
import { DcRouterDb, IpIntelligenceDoc, SecurityBlockRuleDoc, SecurityPolicyAuditDoc } from '../ts/db/index.js';
|
|
import { SecurityPolicyManager } from '../ts/security/index.js';
|
|
|
|
const createTestDb = async () => {
|
|
const storagePath = plugins.path.join(
|
|
plugins.os.tmpdir(),
|
|
`dcrouter-security-policy-${Date.now()}-${Math.random().toString(16).slice(2)}`,
|
|
);
|
|
|
|
DcRouterDb.resetInstance();
|
|
const db = DcRouterDb.getInstance({
|
|
storagePath,
|
|
dbName: `dcrouter-security-policy-${Date.now()}-${Math.random().toString(16).slice(2)}`,
|
|
});
|
|
await db.start();
|
|
await db.getDb().mongoDb.createCollection('__test_init');
|
|
|
|
return {
|
|
async cleanup() {
|
|
await db.stop();
|
|
DcRouterDb.resetInstance();
|
|
await plugins.fs.promises.rm(storagePath, { recursive: true, force: true });
|
|
},
|
|
};
|
|
};
|
|
|
|
const testDbPromise = createTestDb();
|
|
|
|
const clearTestState = async () => {
|
|
for (const rule of await SecurityBlockRuleDoc.findAll()) {
|
|
await rule.delete();
|
|
}
|
|
for (const record of await IpIntelligenceDoc.findAll()) {
|
|
await record.delete();
|
|
}
|
|
for (const event of await SecurityPolicyAuditDoc.findRecent(1000)) {
|
|
await event.delete();
|
|
}
|
|
};
|
|
|
|
const createIntelligenceResult = (asn: number) => ({
|
|
asn,
|
|
asnOrg: `ASN ${asn}`,
|
|
registrantOrg: null,
|
|
registrantCountry: null,
|
|
networkRange: null,
|
|
networkCidrs: null,
|
|
abuseContact: null,
|
|
country: null,
|
|
countryCode: 'US',
|
|
city: null,
|
|
latitude: null,
|
|
longitude: null,
|
|
accuracyRadius: null,
|
|
timezone: null,
|
|
});
|
|
|
|
tap.test('SecurityPolicyManager compiles start-end CIDR rules for edge firewall snapshots', async () => {
|
|
await testDbPromise;
|
|
await clearTestState();
|
|
const manager = new SecurityPolicyManager();
|
|
|
|
await manager.createBlockRule({
|
|
type: 'cidr',
|
|
value: '203.0.113.0 - 203.0.113.255',
|
|
reason: 'test range',
|
|
});
|
|
|
|
const policy = await manager.compilePolicy();
|
|
expect(policy.blockedCidrs).toEqual(['203.0.113.0/24']);
|
|
|
|
const firewall = await manager.compileRemoteIngressFirewall();
|
|
expect(firewall.blockedIps).toEqual(['203.0.113.0/24']);
|
|
});
|
|
|
|
tap.test('SecurityPolicyManager compiles intelligence network ranges for ASN rules', async () => {
|
|
await testDbPromise;
|
|
await clearTestState();
|
|
const manager = new SecurityPolicyManager();
|
|
|
|
const intelligenceDoc = new IpIntelligenceDoc();
|
|
intelligenceDoc.ipAddress = '198.51.100.23';
|
|
intelligenceDoc.asn = 64500;
|
|
intelligenceDoc.asnOrg = 'Example Network';
|
|
intelligenceDoc.networkRange = '198.51.100.0 - 198.51.100.127';
|
|
intelligenceDoc.firstSeenAt = Date.now();
|
|
intelligenceDoc.lastSeenAt = Date.now();
|
|
intelligenceDoc.updatedAt = Date.now();
|
|
intelligenceDoc.seenCount = 1;
|
|
await intelligenceDoc.save();
|
|
|
|
await manager.createBlockRule({
|
|
type: 'asn',
|
|
value: 'AS64500',
|
|
reason: 'test asn range',
|
|
});
|
|
|
|
const policy = await manager.compilePolicy();
|
|
expect(policy.blockedCidrs).toEqual(['198.51.100.0/25']);
|
|
});
|
|
|
|
tap.test('SecurityPolicyManager compiles intelligence CIDR arrays for ASN rules', async () => {
|
|
await testDbPromise;
|
|
await clearTestState();
|
|
const manager = new SecurityPolicyManager();
|
|
|
|
const intelligenceDoc = new IpIntelligenceDoc();
|
|
intelligenceDoc.ipAddress = '198.51.100.130';
|
|
intelligenceDoc.asn = 64501;
|
|
intelligenceDoc.asnOrg = 'Example Split Network';
|
|
intelligenceDoc.networkRange = null;
|
|
intelligenceDoc.networkCidrs = ['198.51.100.128/25', '198.51.101.0/24'];
|
|
intelligenceDoc.firstSeenAt = Date.now();
|
|
intelligenceDoc.lastSeenAt = Date.now();
|
|
intelligenceDoc.updatedAt = Date.now();
|
|
intelligenceDoc.seenCount = 1;
|
|
await intelligenceDoc.save();
|
|
|
|
await manager.createBlockRule({
|
|
type: 'asn',
|
|
value: 'AS64501',
|
|
reason: 'test asn cidr array',
|
|
});
|
|
|
|
const policy = await manager.compilePolicy();
|
|
expect(policy.blockedCidrs).toEqual(['198.51.100.128/25', '198.51.101.0/24']);
|
|
});
|
|
|
|
tap.test('SecurityPolicyManager returns an explicit empty edge firewall snapshot', async () => {
|
|
await testDbPromise;
|
|
await clearTestState();
|
|
const manager = new SecurityPolicyManager();
|
|
|
|
const firewall = await manager.compileRemoteIngressFirewall();
|
|
expect(firewall).toEqual({ blockedIps: [] });
|
|
});
|
|
|
|
tap.test('SecurityPolicyManager filters listed IP intelligence records', async () => {
|
|
await testDbPromise;
|
|
await clearTestState();
|
|
const manager = new SecurityPolicyManager();
|
|
|
|
for (const [ipAddress, asn] of [['8.8.8.8', 15169], ['1.1.1.1', 13335]] as const) {
|
|
const intelligenceDoc = new IpIntelligenceDoc();
|
|
intelligenceDoc.ipAddress = ipAddress;
|
|
intelligenceDoc.asn = asn;
|
|
intelligenceDoc.asnOrg = `ASN ${asn}`;
|
|
intelligenceDoc.firstSeenAt = Date.now();
|
|
intelligenceDoc.lastSeenAt = Date.now();
|
|
intelligenceDoc.updatedAt = Date.now();
|
|
intelligenceDoc.seenCount = 1;
|
|
await intelligenceDoc.save();
|
|
}
|
|
|
|
const records = await manager.listIpIntelligence({ ipAddresses: ['1.1.1.1'] });
|
|
|
|
expect(records).toHaveLength(1);
|
|
expect(records[0].ipAddress).toEqual('1.1.1.1');
|
|
});
|
|
|
|
tap.test('SecurityPolicyManager force refresh waits for an in-flight background observation', async () => {
|
|
await testDbPromise;
|
|
await clearTestState();
|
|
const manager = new SecurityPolicyManager({ intelligenceRefreshMs: 0 });
|
|
|
|
let releaseFirstLookup!: () => void;
|
|
let lookupCount = 0;
|
|
(manager as any).smartNetwork = {
|
|
getIpIntelligence: async () => {
|
|
lookupCount++;
|
|
if (lookupCount === 1) {
|
|
await new Promise<void>((resolve) => { releaseFirstLookup = resolve; });
|
|
return createIntelligenceResult(64500);
|
|
}
|
|
return createIntelligenceResult(64501);
|
|
},
|
|
stop: async () => {},
|
|
};
|
|
|
|
const backgroundObservation = manager.observeIp('8.8.8.8');
|
|
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
const forcedRefresh = manager.refreshIpIntelligence('8.8.8.8');
|
|
releaseFirstLookup();
|
|
|
|
const record = await forcedRefresh;
|
|
await backgroundObservation;
|
|
|
|
expect(lookupCount).toEqual(2);
|
|
expect(record?.asn).toEqual(64501);
|
|
});
|
|
|
|
tap.test('cleanup security policy test db', async () => {
|
|
const dbHandle = await testDbPromise;
|
|
await clearTestState();
|
|
await dbHandle.cleanup();
|
|
});
|
|
|
|
export default tap.start();
|