@serve.zone/dcrouter 13.23.0 → 13.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist_serve/bundle.js +952 -792
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/db/documents/classes.ip-intelligence.doc.d.ts +1 -0
- package/dist_ts/db/documents/classes.ip-intelligence.doc.js +8 -2
- package/dist_ts/opsserver/handlers/security.handler.js +22 -1
- package/dist_ts/security/classes.security-policy-manager.d.ts +15 -4
- package/dist_ts/security/classes.security-policy-manager.js +108 -11
- package/dist_ts_interfaces/requests/security-policy.d.ts +32 -1
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/dist_ts_web/appstate.d.ts +28 -0
- package/dist_ts_web/appstate.js +171 -4
- package/dist_ts_web/elements/network/ops-view-network-activity.d.ts +9 -0
- package/dist_ts_web/elements/network/ops-view-network-activity.js +210 -3
- package/dist_ts_web/elements/security/ops-view-security-blocked.d.ts +12 -3
- package/dist_ts_web/elements/security/ops-view-security-blocked.js +407 -52
- package/package.json +2 -2
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/db/documents/classes.ip-intelligence.doc.ts +3 -0
- package/ts/opsserver/handlers/security.handler.ts +38 -0
- package/ts/security/classes.security-policy-manager.ts +119 -12
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/appstate.ts +236 -3
- package/ts_web/elements/network/ops-view-network-activity.ts +219 -2
- package/ts_web/elements/security/ops-view-security-blocked.ts +414 -51
|
@@ -5,6 +5,7 @@ import type {
|
|
|
5
5
|
IIpIntelligenceRecord,
|
|
6
6
|
ISecurityBlockRule,
|
|
7
7
|
ISecurityCompiledPolicy,
|
|
8
|
+
ISecurityPolicyAuditEvent,
|
|
8
9
|
TSecurityBlockRuleMatchMode,
|
|
9
10
|
TSecurityBlockRuleType,
|
|
10
11
|
} from '../../ts_interfaces/data/security-policy.js';
|
|
@@ -15,7 +16,7 @@ export interface ISecurityPolicyManagerOptions {
|
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
export interface IRemoteIngressFirewallSnapshot {
|
|
18
|
-
blockedIps
|
|
19
|
+
blockedIps: string[];
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
export class SecurityPolicyManager {
|
|
@@ -44,7 +45,7 @@ export class SecurityPolicyManager {
|
|
|
44
45
|
await Promise.allSettled(uniqueIps.map((ip) => this.observeIp(ip)));
|
|
45
46
|
}
|
|
46
47
|
|
|
47
|
-
public async observeIp(ipAddress: string): Promise<void> {
|
|
48
|
+
public async observeIp(ipAddress: string, options: { force?: boolean } = {}): Promise<void> {
|
|
48
49
|
const ip = this.normalizeIp(ipAddress);
|
|
49
50
|
if (!ip || !this.isPublicIp(ip) || this.inFlightObservations.has(ip)) {
|
|
50
51
|
return;
|
|
@@ -54,7 +55,7 @@ export class SecurityPolicyManager {
|
|
|
54
55
|
try {
|
|
55
56
|
const now = Date.now();
|
|
56
57
|
let doc = await IpIntelligenceDoc.findByIp(ip);
|
|
57
|
-
if (doc && now - doc.updatedAt < this.intelligenceRefreshMs) {
|
|
58
|
+
if (doc && !options.force && now - doc.updatedAt < this.intelligenceRefreshMs) {
|
|
58
59
|
if (now - doc.lastSeenAt > 60_000) {
|
|
59
60
|
doc.lastSeenAt = now;
|
|
60
61
|
doc.seenCount = (doc.seenCount || 0) + 1;
|
|
@@ -90,13 +91,38 @@ export class SecurityPolicyManager {
|
|
|
90
91
|
}
|
|
91
92
|
|
|
92
93
|
public async listIpIntelligence(): Promise<IIpIntelligenceRecord[]> {
|
|
93
|
-
return (await IpIntelligenceDoc.findAll()).map((doc) => (
|
|
94
|
+
return (await IpIntelligenceDoc.findAll()).map((doc) => this.intelligenceFromDoc(doc));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
public async refreshIpIntelligence(ipAddress: string): Promise<IIpIntelligenceRecord | null> {
|
|
98
|
+
const ip = this.normalizeIp(ipAddress);
|
|
99
|
+
if (!ip || !this.isPublicIp(ip)) {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
await this.observeIp(ip, { force: true });
|
|
103
|
+
const doc = await IpIntelligenceDoc.findByIp(ip);
|
|
104
|
+
return doc ? this.intelligenceFromDoc(doc) : null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
public async listAuditEvents(limit = 100): Promise<ISecurityPolicyAuditEvent[]> {
|
|
108
|
+
return (await SecurityPolicyAuditDoc.findRecent(limit)).map((doc) => ({
|
|
109
|
+
id: doc.id,
|
|
110
|
+
action: doc.action,
|
|
111
|
+
actor: doc.actor,
|
|
112
|
+
details: doc.details,
|
|
113
|
+
createdAt: doc.createdAt,
|
|
114
|
+
}));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
private intelligenceFromDoc(doc: IpIntelligenceDoc): IIpIntelligenceRecord {
|
|
118
|
+
return {
|
|
94
119
|
ipAddress: doc.ipAddress,
|
|
95
120
|
asn: doc.asn,
|
|
96
121
|
asnOrg: doc.asnOrg,
|
|
97
122
|
registrantOrg: doc.registrantOrg,
|
|
98
123
|
registrantCountry: doc.registrantCountry,
|
|
99
124
|
networkRange: doc.networkRange,
|
|
125
|
+
networkCidrs: doc.networkCidrs,
|
|
100
126
|
abuseContact: doc.abuseContact,
|
|
101
127
|
country: doc.country,
|
|
102
128
|
countryCode: doc.countryCode,
|
|
@@ -109,7 +135,7 @@ export class SecurityPolicyManager {
|
|
|
109
135
|
lastSeenAt: doc.lastSeenAt,
|
|
110
136
|
updatedAt: doc.updatedAt,
|
|
111
137
|
seenCount: doc.seenCount,
|
|
112
|
-
}
|
|
138
|
+
};
|
|
113
139
|
}
|
|
114
140
|
|
|
115
141
|
public async createBlockRule(input: {
|
|
@@ -180,16 +206,22 @@ export class SecurityPolicyManager {
|
|
|
180
206
|
}
|
|
181
207
|
|
|
182
208
|
if (rule.type === 'cidr') {
|
|
183
|
-
const cidr
|
|
184
|
-
|
|
209
|
+
for (const cidr of this.normalizeNetworkEntries(normalizedValue)) {
|
|
210
|
+
blockedCidrs.add(cidr);
|
|
211
|
+
}
|
|
185
212
|
continue;
|
|
186
213
|
}
|
|
187
214
|
|
|
188
215
|
for (const doc of intelligenceDocs) {
|
|
189
216
|
if (!this.ruleMatchesIntelligence(rule, doc)) continue;
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
217
|
+
const networkEntries = this.normalizeNetworkEntryList([
|
|
218
|
+
...(doc.networkCidrs || []),
|
|
219
|
+
doc.networkRange,
|
|
220
|
+
]);
|
|
221
|
+
if (networkEntries.length > 0) {
|
|
222
|
+
for (const cidr of networkEntries) {
|
|
223
|
+
blockedCidrs.add(cidr);
|
|
224
|
+
}
|
|
193
225
|
} else if (this.normalizeIp(doc.ipAddress)) {
|
|
194
226
|
blockedIps.add(this.normalizeIp(doc.ipAddress)!);
|
|
195
227
|
}
|
|
@@ -206,13 +238,13 @@ export class SecurityPolicyManager {
|
|
|
206
238
|
return await this.compilePolicy();
|
|
207
239
|
}
|
|
208
240
|
|
|
209
|
-
public async compileRemoteIngressFirewall(): Promise<IRemoteIngressFirewallSnapshot
|
|
241
|
+
public async compileRemoteIngressFirewall(): Promise<IRemoteIngressFirewallSnapshot> {
|
|
210
242
|
const policy = await this.compilePolicy();
|
|
211
243
|
const blockedIps = [
|
|
212
244
|
...policy.blockedIps.filter((ip) => plugins.net.isIP(ip) === 4),
|
|
213
245
|
...policy.blockedCidrs.filter((cidr) => plugins.net.isIP(cidr.split('/')[0]) === 4),
|
|
214
246
|
];
|
|
215
|
-
return
|
|
247
|
+
return { blockedIps };
|
|
216
248
|
}
|
|
217
249
|
|
|
218
250
|
private async matchesAnyReactiveRule(doc: IpIntelligenceDoc): Promise<boolean> {
|
|
@@ -262,6 +294,81 @@ export class SecurityPolicyManager {
|
|
|
262
294
|
return `${ip}/${prefix}`;
|
|
263
295
|
}
|
|
264
296
|
|
|
297
|
+
private normalizeNetworkEntries(value: string): string[] {
|
|
298
|
+
const trimmed = value.trim();
|
|
299
|
+
if (!trimmed) return [];
|
|
300
|
+
|
|
301
|
+
const cidr = this.normalizeCidr(trimmed);
|
|
302
|
+
if (cidr) return [cidr];
|
|
303
|
+
|
|
304
|
+
const rangeParts = trimmed.split(/\s+-\s+/);
|
|
305
|
+
if (rangeParts.length === 2) {
|
|
306
|
+
return this.ipv4RangeToCidrs(rangeParts[0], rangeParts[1]);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
return [];
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
private normalizeNetworkEntryList(values: Array<string | null | undefined>): string[] {
|
|
313
|
+
const cidrs = new Set<string>();
|
|
314
|
+
for (const value of values) {
|
|
315
|
+
if (!value) continue;
|
|
316
|
+
for (const entry of value.split(',').map((part) => part.trim()).filter(Boolean)) {
|
|
317
|
+
for (const cidr of this.normalizeNetworkEntries(entry)) {
|
|
318
|
+
cidrs.add(cidr);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return [...cidrs];
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
private ipv4RangeToCidrs(startIp: string, endIp: string): string[] {
|
|
326
|
+
const start = this.ipv4ToBigInt(startIp);
|
|
327
|
+
const end = this.ipv4ToBigInt(endIp);
|
|
328
|
+
if (start === undefined || end === undefined || start > end) return [];
|
|
329
|
+
|
|
330
|
+
const cidrs: string[] = [];
|
|
331
|
+
let current = start;
|
|
332
|
+
while (current <= end) {
|
|
333
|
+
let maxBlockSize = current === 0n ? 1n << 32n : current & -current;
|
|
334
|
+
const remaining = end - current + 1n;
|
|
335
|
+
while (maxBlockSize > remaining) {
|
|
336
|
+
maxBlockSize = maxBlockSize / 2n;
|
|
337
|
+
}
|
|
338
|
+
const prefixLength = 32 - this.powerOfTwoExponent(maxBlockSize);
|
|
339
|
+
cidrs.push(`${this.numberToIpv4(current)}/${prefixLength}`);
|
|
340
|
+
current += maxBlockSize;
|
|
341
|
+
}
|
|
342
|
+
return cidrs;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
private ipv4ToBigInt(ip: string): bigint | undefined {
|
|
346
|
+
const normalized = this.normalizeIp(ip);
|
|
347
|
+
if (!normalized || plugins.net.isIP(normalized) !== 4) return undefined;
|
|
348
|
+
return normalized
|
|
349
|
+
.split('.')
|
|
350
|
+
.reduce((sum, part) => (sum * 256n) + BigInt(Number(part)), 0n);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
private numberToIpv4(value: bigint): string {
|
|
354
|
+
return [
|
|
355
|
+
Number((value >> 24n) & 255n),
|
|
356
|
+
Number((value >> 16n) & 255n),
|
|
357
|
+
Number((value >> 8n) & 255n),
|
|
358
|
+
Number(value & 255n),
|
|
359
|
+
].join('.');
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
private powerOfTwoExponent(value: bigint): number {
|
|
363
|
+
let exponent = 0;
|
|
364
|
+
let remaining = value;
|
|
365
|
+
while (remaining > 1n) {
|
|
366
|
+
remaining >>= 1n;
|
|
367
|
+
exponent++;
|
|
368
|
+
}
|
|
369
|
+
return exponent;
|
|
370
|
+
}
|
|
371
|
+
|
|
265
372
|
private isPublicIp(ip: string): boolean {
|
|
266
373
|
const family = plugins.net.isIP(ip);
|
|
267
374
|
if (family === 4) {
|
package/ts_web/appstate.ts
CHANGED
|
@@ -54,6 +54,7 @@ export interface INetworkState {
|
|
|
54
54
|
topIPs: Array<{ ip: string; count: number }>;
|
|
55
55
|
topIPsByBandwidth: Array<{ ip: string; count: number; bwIn: number; bwOut: number }>;
|
|
56
56
|
throughputByIP: Array<{ ip: string; in: number; out: number }>;
|
|
57
|
+
ipIntelligence: interfaces.data.IIpIntelligenceRecord[];
|
|
57
58
|
domainActivity: interfaces.data.IDomainActivity[];
|
|
58
59
|
throughputHistory: Array<{ timestamp: number; in: number; out: number }>;
|
|
59
60
|
requestsPerSecond: number;
|
|
@@ -66,6 +67,16 @@ export interface INetworkState {
|
|
|
66
67
|
error: string | null;
|
|
67
68
|
}
|
|
68
69
|
|
|
70
|
+
export interface ISecurityPolicyState {
|
|
71
|
+
rules: interfaces.data.ISecurityBlockRule[];
|
|
72
|
+
ipIntelligence: interfaces.data.IIpIntelligenceRecord[];
|
|
73
|
+
compiledPolicy: interfaces.data.ISecurityCompiledPolicy | null;
|
|
74
|
+
auditEvents: interfaces.data.ISecurityPolicyAuditEvent[];
|
|
75
|
+
isLoading: boolean;
|
|
76
|
+
error: string | null;
|
|
77
|
+
lastUpdated: number;
|
|
78
|
+
}
|
|
79
|
+
|
|
69
80
|
export interface ICertificateState {
|
|
70
81
|
certificates: interfaces.requests.ICertificateInfo[];
|
|
71
82
|
summary: { total: number; valid: number; expiring: number; expired: number; failed: number; unknown: number };
|
|
@@ -164,6 +175,7 @@ export const networkStatePart = await appState.getStatePart<INetworkState>(
|
|
|
164
175
|
topIPs: [],
|
|
165
176
|
topIPsByBandwidth: [],
|
|
166
177
|
throughputByIP: [],
|
|
178
|
+
ipIntelligence: [],
|
|
167
179
|
domainActivity: [],
|
|
168
180
|
throughputHistory: [],
|
|
169
181
|
requestsPerSecond: 0,
|
|
@@ -178,6 +190,20 @@ export const networkStatePart = await appState.getStatePart<INetworkState>(
|
|
|
178
190
|
'soft'
|
|
179
191
|
);
|
|
180
192
|
|
|
193
|
+
export const securityPolicyStatePart = await appState.getStatePart<ISecurityPolicyState>(
|
|
194
|
+
'securityPolicy',
|
|
195
|
+
{
|
|
196
|
+
rules: [],
|
|
197
|
+
ipIntelligence: [],
|
|
198
|
+
compiledPolicy: null,
|
|
199
|
+
auditEvents: [],
|
|
200
|
+
isLoading: false,
|
|
201
|
+
error: null,
|
|
202
|
+
lastUpdated: 0,
|
|
203
|
+
},
|
|
204
|
+
'soft',
|
|
205
|
+
);
|
|
206
|
+
|
|
181
207
|
export const emailOpsStatePart = await appState.getStatePart<IEmailOpsState>(
|
|
182
208
|
'emailOps',
|
|
183
209
|
{
|
|
@@ -517,9 +543,18 @@ export const fetchNetworkStatsAction = networkStatePart.createAction(async (stat
|
|
|
517
543
|
interfaces.requests.IReq_GetNetworkStats
|
|
518
544
|
>('/typedrequest', 'getNetworkStats');
|
|
519
545
|
|
|
520
|
-
const
|
|
521
|
-
|
|
522
|
-
|
|
546
|
+
const ipIntelligenceRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
547
|
+
interfaces.requests.IReq_ListIpIntelligence
|
|
548
|
+
>('/typedrequest', 'listIpIntelligence');
|
|
549
|
+
|
|
550
|
+
const [networkStatsResponse, ipIntelligenceResponse] = await Promise.all([
|
|
551
|
+
networkStatsRequest.fire({
|
|
552
|
+
identity: context.identity,
|
|
553
|
+
}),
|
|
554
|
+
ipIntelligenceRequest.fire({
|
|
555
|
+
identity: context.identity,
|
|
556
|
+
}),
|
|
557
|
+
]);
|
|
523
558
|
|
|
524
559
|
// Use the connections data for the connection list
|
|
525
560
|
// and network stats for throughput and IP analytics
|
|
@@ -561,6 +596,7 @@ export const fetchNetworkStatsAction = networkStatePart.createAction(async (stat
|
|
|
561
596
|
topIPs: networkStatsResponse.topIPs || [],
|
|
562
597
|
topIPsByBandwidth: networkStatsResponse.topIPsByBandwidth || [],
|
|
563
598
|
throughputByIP: networkStatsResponse.throughputByIP || [],
|
|
599
|
+
ipIntelligence: ipIntelligenceResponse.records || [],
|
|
564
600
|
domainActivity: networkStatsResponse.domainActivity || [],
|
|
565
601
|
throughputHistory: networkStatsResponse.throughputHistory || [],
|
|
566
602
|
requestsPerSecond: networkStatsResponse.requestsPerSecond || 0,
|
|
@@ -582,6 +618,182 @@ export const fetchNetworkStatsAction = networkStatePart.createAction(async (stat
|
|
|
582
618
|
}
|
|
583
619
|
});
|
|
584
620
|
|
|
621
|
+
// ============================================================================
|
|
622
|
+
// Security Policy Actions
|
|
623
|
+
// ============================================================================
|
|
624
|
+
|
|
625
|
+
export const fetchSecurityPolicyAction = securityPolicyStatePart.createAction(
|
|
626
|
+
async (statePartArg): Promise<ISecurityPolicyState> => {
|
|
627
|
+
const context = getActionContext();
|
|
628
|
+
const currentState = statePartArg.getState()!;
|
|
629
|
+
if (!context.identity) return currentState;
|
|
630
|
+
|
|
631
|
+
try {
|
|
632
|
+
const rulesRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
633
|
+
interfaces.requests.IReq_ListSecurityBlockRules
|
|
634
|
+
>('/typedrequest', 'listSecurityBlockRules');
|
|
635
|
+
const intelligenceRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
636
|
+
interfaces.requests.IReq_ListIpIntelligence
|
|
637
|
+
>('/typedrequest', 'listIpIntelligence');
|
|
638
|
+
const compiledPolicyRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
639
|
+
interfaces.requests.IReq_GetCompiledSecurityPolicy
|
|
640
|
+
>('/typedrequest', 'getCompiledSecurityPolicy');
|
|
641
|
+
const auditRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
642
|
+
interfaces.requests.IReq_ListSecurityPolicyAudit
|
|
643
|
+
>('/typedrequest', 'listSecurityPolicyAudit');
|
|
644
|
+
|
|
645
|
+
const [rulesResponse, intelligenceResponse, compiledPolicyResponse, auditResponse] = await Promise.all([
|
|
646
|
+
rulesRequest.fire({ identity: context.identity }),
|
|
647
|
+
intelligenceRequest.fire({ identity: context.identity }),
|
|
648
|
+
compiledPolicyRequest.fire({ identity: context.identity }),
|
|
649
|
+
auditRequest.fire({ identity: context.identity, limit: 100 }),
|
|
650
|
+
]);
|
|
651
|
+
|
|
652
|
+
return {
|
|
653
|
+
rules: rulesResponse.rules || [],
|
|
654
|
+
ipIntelligence: intelligenceResponse.records || [],
|
|
655
|
+
compiledPolicy: compiledPolicyResponse.policy,
|
|
656
|
+
auditEvents: auditResponse.events || [],
|
|
657
|
+
isLoading: false,
|
|
658
|
+
error: null,
|
|
659
|
+
lastUpdated: Date.now(),
|
|
660
|
+
};
|
|
661
|
+
} catch (error: unknown) {
|
|
662
|
+
return {
|
|
663
|
+
...currentState,
|
|
664
|
+
isLoading: false,
|
|
665
|
+
error: error instanceof Error ? error.message : 'Failed to fetch security policy',
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
},
|
|
669
|
+
);
|
|
670
|
+
|
|
671
|
+
export const createSecurityBlockRuleAction = securityPolicyStatePart.createAction<{
|
|
672
|
+
type: interfaces.data.TSecurityBlockRuleType;
|
|
673
|
+
value: string;
|
|
674
|
+
matchMode?: interfaces.data.TSecurityBlockRuleMatchMode;
|
|
675
|
+
reason?: string;
|
|
676
|
+
enabled?: boolean;
|
|
677
|
+
}>(async (statePartArg, dataArg, actionContext): Promise<ISecurityPolicyState> => {
|
|
678
|
+
const context = getActionContext();
|
|
679
|
+
const currentState = statePartArg.getState()!;
|
|
680
|
+
if (!context.identity) return currentState;
|
|
681
|
+
|
|
682
|
+
try {
|
|
683
|
+
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
684
|
+
interfaces.requests.IReq_CreateSecurityBlockRule
|
|
685
|
+
>('/typedrequest', 'createSecurityBlockRule');
|
|
686
|
+
|
|
687
|
+
const response = await request.fire({
|
|
688
|
+
identity: context.identity,
|
|
689
|
+
type: dataArg.type,
|
|
690
|
+
value: dataArg.value,
|
|
691
|
+
matchMode: dataArg.matchMode,
|
|
692
|
+
reason: dataArg.reason,
|
|
693
|
+
enabled: dataArg.enabled,
|
|
694
|
+
});
|
|
695
|
+
|
|
696
|
+
if (!response.success) {
|
|
697
|
+
return { ...currentState, error: response.message || 'Failed to create security block rule' };
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
return await actionContext!.dispatch(fetchSecurityPolicyAction, null);
|
|
701
|
+
} catch (error: unknown) {
|
|
702
|
+
return {
|
|
703
|
+
...currentState,
|
|
704
|
+
error: error instanceof Error ? error.message : 'Failed to create security block rule',
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
export const updateSecurityBlockRuleAction = securityPolicyStatePart.createAction<{
|
|
710
|
+
id: string;
|
|
711
|
+
value?: string;
|
|
712
|
+
matchMode?: interfaces.data.TSecurityBlockRuleMatchMode;
|
|
713
|
+
reason?: string;
|
|
714
|
+
enabled?: boolean;
|
|
715
|
+
}>(async (statePartArg, dataArg, actionContext): Promise<ISecurityPolicyState> => {
|
|
716
|
+
const context = getActionContext();
|
|
717
|
+
const currentState = statePartArg.getState()!;
|
|
718
|
+
if (!context.identity) return currentState;
|
|
719
|
+
|
|
720
|
+
try {
|
|
721
|
+
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
722
|
+
interfaces.requests.IReq_UpdateSecurityBlockRule
|
|
723
|
+
>('/typedrequest', 'updateSecurityBlockRule');
|
|
724
|
+
|
|
725
|
+
const response = await request.fire({
|
|
726
|
+
identity: context.identity,
|
|
727
|
+
id: dataArg.id,
|
|
728
|
+
value: dataArg.value,
|
|
729
|
+
matchMode: dataArg.matchMode,
|
|
730
|
+
reason: dataArg.reason,
|
|
731
|
+
enabled: dataArg.enabled,
|
|
732
|
+
});
|
|
733
|
+
|
|
734
|
+
if (!response.success) {
|
|
735
|
+
return { ...currentState, error: response.message || 'Failed to update security block rule' };
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
return await actionContext!.dispatch(fetchSecurityPolicyAction, null);
|
|
739
|
+
} catch (error: unknown) {
|
|
740
|
+
return {
|
|
741
|
+
...currentState,
|
|
742
|
+
error: error instanceof Error ? error.message : 'Failed to update security block rule',
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
});
|
|
746
|
+
|
|
747
|
+
export const deleteSecurityBlockRuleAction = securityPolicyStatePart.createAction<string>(
|
|
748
|
+
async (statePartArg, ruleId, actionContext): Promise<ISecurityPolicyState> => {
|
|
749
|
+
const context = getActionContext();
|
|
750
|
+
const currentState = statePartArg.getState()!;
|
|
751
|
+
if (!context.identity) return currentState;
|
|
752
|
+
|
|
753
|
+
try {
|
|
754
|
+
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
755
|
+
interfaces.requests.IReq_DeleteSecurityBlockRule
|
|
756
|
+
>('/typedrequest', 'deleteSecurityBlockRule');
|
|
757
|
+
|
|
758
|
+
const response = await request.fire({ identity: context.identity, id: ruleId });
|
|
759
|
+
if (!response.success) {
|
|
760
|
+
return { ...currentState, error: response.message || 'Failed to delete security block rule' };
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
return await actionContext!.dispatch(fetchSecurityPolicyAction, null);
|
|
764
|
+
} catch (error: unknown) {
|
|
765
|
+
return {
|
|
766
|
+
...currentState,
|
|
767
|
+
error: error instanceof Error ? error.message : 'Failed to delete security block rule',
|
|
768
|
+
};
|
|
769
|
+
}
|
|
770
|
+
},
|
|
771
|
+
);
|
|
772
|
+
|
|
773
|
+
export const refreshIpIntelligenceAction = securityPolicyStatePart.createAction<string>(
|
|
774
|
+
async (statePartArg, ipAddress, actionContext): Promise<ISecurityPolicyState> => {
|
|
775
|
+
const context = getActionContext();
|
|
776
|
+
const currentState = statePartArg.getState()!;
|
|
777
|
+
if (!context.identity) return currentState;
|
|
778
|
+
|
|
779
|
+
try {
|
|
780
|
+
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
781
|
+
interfaces.requests.IReq_RefreshIpIntelligence
|
|
782
|
+
>('/typedrequest', 'refreshIpIntelligence');
|
|
783
|
+
const response = await request.fire({ identity: context.identity, ipAddress });
|
|
784
|
+
if (!response.success) {
|
|
785
|
+
return { ...currentState, error: response.message || 'Failed to refresh IP intelligence' };
|
|
786
|
+
}
|
|
787
|
+
return await actionContext!.dispatch(fetchSecurityPolicyAction, null);
|
|
788
|
+
} catch (error: unknown) {
|
|
789
|
+
return {
|
|
790
|
+
...currentState,
|
|
791
|
+
error: error instanceof Error ? error.message : 'Failed to refresh IP intelligence',
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
},
|
|
795
|
+
);
|
|
796
|
+
|
|
585
797
|
// ============================================================================
|
|
586
798
|
// Email Operations Actions
|
|
587
799
|
// ============================================================================
|
|
@@ -2665,6 +2877,27 @@ async function dispatchCombinedRefreshActionInner() {
|
|
|
2665
2877
|
isLoading: false,
|
|
2666
2878
|
error: null,
|
|
2667
2879
|
});
|
|
2880
|
+
|
|
2881
|
+
try {
|
|
2882
|
+
const intelligenceRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
2883
|
+
interfaces.requests.IReq_ListIpIntelligence
|
|
2884
|
+
>('/typedrequest', 'listIpIntelligence');
|
|
2885
|
+
const intelligenceResponse = await intelligenceRequest.fire({ identity: context.identity });
|
|
2886
|
+
networkStatePart.setState({
|
|
2887
|
+
...networkStatePart.getState()!,
|
|
2888
|
+
ipIntelligence: intelligenceResponse.records || [],
|
|
2889
|
+
});
|
|
2890
|
+
} catch (error) {
|
|
2891
|
+
console.error('IP intelligence refresh failed:', error);
|
|
2892
|
+
}
|
|
2893
|
+
}
|
|
2894
|
+
|
|
2895
|
+
if (currentView === 'security') {
|
|
2896
|
+
try {
|
|
2897
|
+
await securityPolicyStatePart.dispatchAction(fetchSecurityPolicyAction, null);
|
|
2898
|
+
} catch (error) {
|
|
2899
|
+
console.error('Security policy refresh failed:', error);
|
|
2900
|
+
}
|
|
2668
2901
|
}
|
|
2669
2902
|
|
|
2670
2903
|
// Refresh certificate data if on Domains > Certificates subview
|