feat(security): add security policy management and IP intelligence operations to the ops UI

This commit is contained in:
2026-04-26 19:51:08 +00:00
parent 1567606c49
commit e5c3578163
9 changed files with 991 additions and 62 deletions
+29 -4
View File
@@ -5,6 +5,7 @@ import type {
IIpIntelligenceRecord,
ISecurityBlockRule,
ISecurityCompiledPolicy,
ISecurityPolicyAuditEvent,
TSecurityBlockRuleMatchMode,
TSecurityBlockRuleType,
} from '../../ts_interfaces/data/security-policy.js';
@@ -44,7 +45,7 @@ export class SecurityPolicyManager {
await Promise.allSettled(uniqueIps.map((ip) => this.observeIp(ip)));
}
public async observeIp(ipAddress: string): Promise<void> {
public async observeIp(ipAddress: string, options: { force?: boolean } = {}): Promise<void> {
const ip = this.normalizeIp(ipAddress);
if (!ip || !this.isPublicIp(ip) || this.inFlightObservations.has(ip)) {
return;
@@ -54,7 +55,7 @@ export class SecurityPolicyManager {
try {
const now = Date.now();
let doc = await IpIntelligenceDoc.findByIp(ip);
if (doc && now - doc.updatedAt < this.intelligenceRefreshMs) {
if (doc && !options.force && now - doc.updatedAt < this.intelligenceRefreshMs) {
if (now - doc.lastSeenAt > 60_000) {
doc.lastSeenAt = now;
doc.seenCount = (doc.seenCount || 0) + 1;
@@ -90,7 +91,31 @@ export class SecurityPolicyManager {
}
public async listIpIntelligence(): Promise<IIpIntelligenceRecord[]> {
return (await IpIntelligenceDoc.findAll()).map((doc) => ({
return (await IpIntelligenceDoc.findAll()).map((doc) => this.intelligenceFromDoc(doc));
}
public async refreshIpIntelligence(ipAddress: string): Promise<IIpIntelligenceRecord | null> {
const ip = this.normalizeIp(ipAddress);
if (!ip || !this.isPublicIp(ip)) {
return null;
}
await this.observeIp(ip, { force: true });
const doc = await IpIntelligenceDoc.findByIp(ip);
return doc ? this.intelligenceFromDoc(doc) : null;
}
public async listAuditEvents(limit = 100): Promise<ISecurityPolicyAuditEvent[]> {
return (await SecurityPolicyAuditDoc.findRecent(limit)).map((doc) => ({
id: doc.id,
action: doc.action,
actor: doc.actor,
details: doc.details,
createdAt: doc.createdAt,
}));
}
private intelligenceFromDoc(doc: IpIntelligenceDoc): IIpIntelligenceRecord {
return {
ipAddress: doc.ipAddress,
asn: doc.asn,
asnOrg: doc.asnOrg,
@@ -109,7 +134,7 @@ export class SecurityPolicyManager {
lastSeenAt: doc.lastSeenAt,
updatedAt: doc.updatedAt,
seenCount: doc.seenCount,
}));
};
}
public async createBlockRule(input: {