@grc-claw/regulatory-change-management 0.8.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/index.d.ts +180 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +308 -0
- package/dist/index.js.map +1 -0
- package/package.json +19 -0
- package/src/index.ts +444 -0
- package/tsconfig.json +9 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RegulatoryChangeManagement - Automated tracking of regulatory changes
|
|
3
|
+
*
|
|
4
|
+
* Monitors regulatory sources, detects changes, and maps them to existing controls.
|
|
5
|
+
* Provides impact analysis and remediation recommendations.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Multi-source regulatory monitoring (RSS, API, web scraping)
|
|
9
|
+
* - Change detection and classification
|
|
10
|
+
* - Impact analysis on existing controls
|
|
11
|
+
* - Automated remediation recommendations
|
|
12
|
+
* - Compliance gap identification
|
|
13
|
+
* - Regulatory timeline tracking
|
|
14
|
+
*/
|
|
15
|
+
export interface RegulatorySource {
|
|
16
|
+
id: string;
|
|
17
|
+
name: string;
|
|
18
|
+
type: 'rss' | 'api' | 'webhook' | 'manual';
|
|
19
|
+
url: string;
|
|
20
|
+
framework: string;
|
|
21
|
+
frequency: 'realtime' | 'daily' | 'weekly' | 'monthly';
|
|
22
|
+
lastChecked: Date;
|
|
23
|
+
enabled: boolean;
|
|
24
|
+
}
|
|
25
|
+
export interface RegulatoryChange {
|
|
26
|
+
id: string;
|
|
27
|
+
sourceId: string;
|
|
28
|
+
title: string;
|
|
29
|
+
description: string;
|
|
30
|
+
effectiveDate: Date;
|
|
31
|
+
publishedDate: Date;
|
|
32
|
+
framework: string;
|
|
33
|
+
changeType: 'new_regulation' | 'amendment' | 'guidance' | 'enforcement' | 'deadline';
|
|
34
|
+
severity: 'critical' | 'high' | 'medium' | 'low';
|
|
35
|
+
affectedControls: string[];
|
|
36
|
+
impactAnalysis?: ImpactAnalysis;
|
|
37
|
+
status: 'detected' | 'analyzed' | 'acknowledged' | 'in_remediation' | 'completed';
|
|
38
|
+
}
|
|
39
|
+
export interface ImpactAnalysis {
|
|
40
|
+
affectedControls: AffectedControl[];
|
|
41
|
+
overallImpact: 'critical' | 'high' | 'medium' | 'low' | 'none';
|
|
42
|
+
estimatedEffort: string;
|
|
43
|
+
recommendedActions: string[];
|
|
44
|
+
deadlineImplications: string[];
|
|
45
|
+
}
|
|
46
|
+
export interface AffectedControl {
|
|
47
|
+
controlId: string;
|
|
48
|
+
framework: string;
|
|
49
|
+
impact: 'critical' | 'high' | 'medium' | 'low' | 'none';
|
|
50
|
+
gapDescription: string;
|
|
51
|
+
remediationRequired: boolean;
|
|
52
|
+
estimatedEffort: string;
|
|
53
|
+
}
|
|
54
|
+
export interface RegulatoryTimeline {
|
|
55
|
+
changeId: string;
|
|
56
|
+
milestones: TimelineMilestone[];
|
|
57
|
+
currentPhase: string;
|
|
58
|
+
daysRemaining?: number;
|
|
59
|
+
}
|
|
60
|
+
export interface TimelineMilestone {
|
|
61
|
+
name: string;
|
|
62
|
+
date: Date;
|
|
63
|
+
status: 'pending' | 'in_progress' | 'completed' | 'overdue';
|
|
64
|
+
description: string;
|
|
65
|
+
}
|
|
66
|
+
export interface ComplianceGap {
|
|
67
|
+
id: string;
|
|
68
|
+
changeId: string;
|
|
69
|
+
controlId: string;
|
|
70
|
+
framework: string;
|
|
71
|
+
gapType: 'new_requirement' | 'strengthened_requirement' | 'new_evidence' | 'deadline_change';
|
|
72
|
+
description: string;
|
|
73
|
+
remediationPlan?: RemediationPlan;
|
|
74
|
+
status: 'open' | 'in_remediation' | 'closed';
|
|
75
|
+
}
|
|
76
|
+
export interface RemediationPlan {
|
|
77
|
+
steps: RemediationStep[];
|
|
78
|
+
estimatedEffort: string;
|
|
79
|
+
assignedTo?: string;
|
|
80
|
+
deadline?: Date;
|
|
81
|
+
status: 'not_started' | 'in_progress' | 'completed';
|
|
82
|
+
}
|
|
83
|
+
export interface RemediationStep {
|
|
84
|
+
name: string;
|
|
85
|
+
description: string;
|
|
86
|
+
estimatedHours: number;
|
|
87
|
+
dependencies: string[];
|
|
88
|
+
status: 'pending' | 'in_progress' | 'completed';
|
|
89
|
+
}
|
|
90
|
+
export declare class RegulatoryChangeManagement {
|
|
91
|
+
private readonly options;
|
|
92
|
+
private sources;
|
|
93
|
+
private changes;
|
|
94
|
+
private gaps;
|
|
95
|
+
private timelines;
|
|
96
|
+
constructor(options?: {
|
|
97
|
+
checkIntervalMs?: number;
|
|
98
|
+
maxChanges?: number;
|
|
99
|
+
autoAnalyze?: boolean;
|
|
100
|
+
});
|
|
101
|
+
/**
|
|
102
|
+
* Register a regulatory source
|
|
103
|
+
*/
|
|
104
|
+
registerSource(source: RegulatorySource): void;
|
|
105
|
+
/**
|
|
106
|
+
* Check for regulatory changes
|
|
107
|
+
*/
|
|
108
|
+
checkForChanges(): Promise<RegulatoryChange[]>;
|
|
109
|
+
/**
|
|
110
|
+
* Analyze impact of a regulatory change
|
|
111
|
+
*/
|
|
112
|
+
analyzeChange(changeId: string): Promise<ImpactAnalysis | null>;
|
|
113
|
+
/**
|
|
114
|
+
* Get all regulatory changes
|
|
115
|
+
*/
|
|
116
|
+
getChanges(filters?: {
|
|
117
|
+
framework?: string;
|
|
118
|
+
severity?: string;
|
|
119
|
+
status?: string;
|
|
120
|
+
fromDate?: Date;
|
|
121
|
+
toDate?: Date;
|
|
122
|
+
}): RegulatoryChange[];
|
|
123
|
+
/**
|
|
124
|
+
* Get compliance gaps
|
|
125
|
+
*/
|
|
126
|
+
getGaps(filters?: {
|
|
127
|
+
framework?: string;
|
|
128
|
+
status?: string;
|
|
129
|
+
changeId?: string;
|
|
130
|
+
}): ComplianceGap[];
|
|
131
|
+
/**
|
|
132
|
+
* Get timeline for a change
|
|
133
|
+
*/
|
|
134
|
+
getTimeline(changeId: string): RegulatoryTimeline | undefined;
|
|
135
|
+
/**
|
|
136
|
+
* Update gap status
|
|
137
|
+
*/
|
|
138
|
+
updateGapStatus(gapId: string, status: ComplianceGap['status']): void;
|
|
139
|
+
/**
|
|
140
|
+
* Create remediation plan for a gap
|
|
141
|
+
*/
|
|
142
|
+
createRemediationPlan(gapId: string, plan: RemediationPlan): void;
|
|
143
|
+
/**
|
|
144
|
+
* Get regulatory sources
|
|
145
|
+
*/
|
|
146
|
+
getSources(): RegulatorySource[];
|
|
147
|
+
/**
|
|
148
|
+
* Fetch changes from a source (stub - would integrate with real APIs)
|
|
149
|
+
*/
|
|
150
|
+
private fetchChanges;
|
|
151
|
+
/**
|
|
152
|
+
* Analyze affected controls
|
|
153
|
+
*/
|
|
154
|
+
private analyzeAffectedControls;
|
|
155
|
+
/**
|
|
156
|
+
* Calculate overall impact
|
|
157
|
+
*/
|
|
158
|
+
private calculateOverallImpact;
|
|
159
|
+
/**
|
|
160
|
+
* Estimate remediation effort
|
|
161
|
+
*/
|
|
162
|
+
private estimateRemediationEffort;
|
|
163
|
+
/**
|
|
164
|
+
* Generate recommendations
|
|
165
|
+
*/
|
|
166
|
+
private generateRecommendations;
|
|
167
|
+
/**
|
|
168
|
+
* Analyze deadline implications
|
|
169
|
+
*/
|
|
170
|
+
private analyzeDeadlineImplications;
|
|
171
|
+
/**
|
|
172
|
+
* Create compliance gaps
|
|
173
|
+
*/
|
|
174
|
+
private createComplianceGaps;
|
|
175
|
+
/**
|
|
176
|
+
* Create timeline
|
|
177
|
+
*/
|
|
178
|
+
private createTimeline;
|
|
179
|
+
}
|
|
180
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* RegulatoryChangeManagement - Automated tracking of regulatory changes
|
|
4
|
+
*
|
|
5
|
+
* Monitors regulatory sources, detects changes, and maps them to existing controls.
|
|
6
|
+
* Provides impact analysis and remediation recommendations.
|
|
7
|
+
*
|
|
8
|
+
* Features:
|
|
9
|
+
* - Multi-source regulatory monitoring (RSS, API, web scraping)
|
|
10
|
+
* - Change detection and classification
|
|
11
|
+
* - Impact analysis on existing controls
|
|
12
|
+
* - Automated remediation recommendations
|
|
13
|
+
* - Compliance gap identification
|
|
14
|
+
* - Regulatory timeline tracking
|
|
15
|
+
*/
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.RegulatoryChangeManagement = void 0;
|
|
18
|
+
class RegulatoryChangeManagement {
|
|
19
|
+
options;
|
|
20
|
+
sources = new Map();
|
|
21
|
+
changes = new Map();
|
|
22
|
+
gaps = new Map();
|
|
23
|
+
timelines = new Map();
|
|
24
|
+
constructor(options = {}) {
|
|
25
|
+
this.options = options;
|
|
26
|
+
this.options = {
|
|
27
|
+
checkIntervalMs: 3600000, // 1 hour
|
|
28
|
+
maxChanges: 1000,
|
|
29
|
+
autoAnalyze: true,
|
|
30
|
+
...options
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Register a regulatory source
|
|
35
|
+
*/
|
|
36
|
+
registerSource(source) {
|
|
37
|
+
this.sources.set(source.id, source);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Check for regulatory changes
|
|
41
|
+
*/
|
|
42
|
+
async checkForChanges() {
|
|
43
|
+
const newChanges = [];
|
|
44
|
+
for (const source of this.sources.values()) {
|
|
45
|
+
if (!source.enabled)
|
|
46
|
+
continue;
|
|
47
|
+
try {
|
|
48
|
+
const changes = await this.fetchChanges(source);
|
|
49
|
+
for (const change of changes) {
|
|
50
|
+
if (!this.changes.has(change.id)) {
|
|
51
|
+
this.changes.set(change.id, change);
|
|
52
|
+
newChanges.push(change);
|
|
53
|
+
// Auto-analyze if enabled
|
|
54
|
+
if (this.options.autoAnalyze) {
|
|
55
|
+
await this.analyzeChange(change.id);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
source.lastChecked = new Date();
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
console.error(`Failed to check source ${source.name}:`, error);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return newChanges;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Analyze impact of a regulatory change
|
|
69
|
+
*/
|
|
70
|
+
async analyzeChange(changeId) {
|
|
71
|
+
const change = this.changes.get(changeId);
|
|
72
|
+
if (!change)
|
|
73
|
+
return null;
|
|
74
|
+
// Analyze impact on controls
|
|
75
|
+
const affectedControls = await this.analyzeAffectedControls(change);
|
|
76
|
+
const impactAnalysis = {
|
|
77
|
+
affectedControls,
|
|
78
|
+
overallImpact: this.calculateOverallImpact(affectedControls),
|
|
79
|
+
estimatedEffort: this.estimateRemediationEffort(affectedControls),
|
|
80
|
+
recommendedActions: this.generateRecommendations(change, affectedControls),
|
|
81
|
+
deadlineImplications: this.analyzeDeadlineImplications(change)
|
|
82
|
+
};
|
|
83
|
+
change.impactAnalysis = impactAnalysis;
|
|
84
|
+
change.status = 'analyzed';
|
|
85
|
+
// Create compliance gaps
|
|
86
|
+
this.createComplianceGaps(change, affectedControls);
|
|
87
|
+
// Create timeline
|
|
88
|
+
this.createTimeline(change);
|
|
89
|
+
return impactAnalysis;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Get all regulatory changes
|
|
93
|
+
*/
|
|
94
|
+
getChanges(filters) {
|
|
95
|
+
let changes = Array.from(this.changes.values());
|
|
96
|
+
if (filters) {
|
|
97
|
+
if (filters.framework) {
|
|
98
|
+
changes = changes.filter(c => c.framework === filters.framework);
|
|
99
|
+
}
|
|
100
|
+
if (filters.severity) {
|
|
101
|
+
changes = changes.filter(c => c.severity === filters.severity);
|
|
102
|
+
}
|
|
103
|
+
if (filters.status) {
|
|
104
|
+
changes = changes.filter(c => c.status === filters.status);
|
|
105
|
+
}
|
|
106
|
+
if (filters.fromDate) {
|
|
107
|
+
changes = changes.filter(c => c.publishedDate >= filters.fromDate);
|
|
108
|
+
}
|
|
109
|
+
if (filters.toDate) {
|
|
110
|
+
changes = changes.filter(c => c.publishedDate <= filters.toDate);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return changes.sort((a, b) => b.publishedDate.getTime() - a.publishedDate.getTime());
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Get compliance gaps
|
|
117
|
+
*/
|
|
118
|
+
getGaps(filters) {
|
|
119
|
+
let gaps = Array.from(this.gaps.values());
|
|
120
|
+
if (filters) {
|
|
121
|
+
if (filters.framework) {
|
|
122
|
+
gaps = gaps.filter(g => g.framework === filters.framework);
|
|
123
|
+
}
|
|
124
|
+
if (filters.status) {
|
|
125
|
+
gaps = gaps.filter(g => g.status === filters.status);
|
|
126
|
+
}
|
|
127
|
+
if (filters.changeId) {
|
|
128
|
+
gaps = gaps.filter(g => g.changeId === filters.changeId);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return gaps;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Get timeline for a change
|
|
135
|
+
*/
|
|
136
|
+
getTimeline(changeId) {
|
|
137
|
+
return this.timelines.get(changeId);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Update gap status
|
|
141
|
+
*/
|
|
142
|
+
updateGapStatus(gapId, status) {
|
|
143
|
+
const gap = this.gaps.get(gapId);
|
|
144
|
+
if (gap) {
|
|
145
|
+
gap.status = status;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Create remediation plan for a gap
|
|
150
|
+
*/
|
|
151
|
+
createRemediationPlan(gapId, plan) {
|
|
152
|
+
const gap = this.gaps.get(gapId);
|
|
153
|
+
if (gap) {
|
|
154
|
+
gap.remediationPlan = plan;
|
|
155
|
+
gap.status = 'in_remediation';
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Get regulatory sources
|
|
160
|
+
*/
|
|
161
|
+
getSources() {
|
|
162
|
+
return Array.from(this.sources.values());
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Fetch changes from a source (stub - would integrate with real APIs)
|
|
166
|
+
*/
|
|
167
|
+
async fetchChanges(source) {
|
|
168
|
+
// In a real implementation, this would fetch from RSS/API/webhook
|
|
169
|
+
// For now, return empty array
|
|
170
|
+
return [];
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Analyze affected controls
|
|
174
|
+
*/
|
|
175
|
+
async analyzeAffectedControls(change) {
|
|
176
|
+
// In a real implementation, this would analyze the change against the control framework
|
|
177
|
+
// For now, return empty array
|
|
178
|
+
return [];
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Calculate overall impact
|
|
182
|
+
*/
|
|
183
|
+
calculateOverallImpact(controls) {
|
|
184
|
+
if (controls.length === 0)
|
|
185
|
+
return 'none';
|
|
186
|
+
const hasCritical = controls.some(c => c.impact === 'critical');
|
|
187
|
+
const hasHigh = controls.some(c => c.impact === 'high');
|
|
188
|
+
const hasMedium = controls.some(c => c.impact === 'medium');
|
|
189
|
+
if (hasCritical)
|
|
190
|
+
return 'critical';
|
|
191
|
+
if (hasHigh)
|
|
192
|
+
return 'high';
|
|
193
|
+
if (hasMedium)
|
|
194
|
+
return 'medium';
|
|
195
|
+
return 'low';
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Estimate remediation effort
|
|
199
|
+
*/
|
|
200
|
+
estimateRemediationEffort(controls) {
|
|
201
|
+
const totalHours = controls.reduce((sum, c) => {
|
|
202
|
+
const hours = parseInt(c.estimatedEffort) || 0;
|
|
203
|
+
return sum + hours;
|
|
204
|
+
}, 0);
|
|
205
|
+
if (totalHours === 0)
|
|
206
|
+
return 'Minimal';
|
|
207
|
+
if (totalHours < 40)
|
|
208
|
+
return `${totalHours} hours`;
|
|
209
|
+
if (totalHours < 160)
|
|
210
|
+
return `${Math.round(totalHours / 40)} weeks`;
|
|
211
|
+
return `${Math.round(totalHours / 160)} months`;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Generate recommendations
|
|
215
|
+
*/
|
|
216
|
+
generateRecommendations(change, controls) {
|
|
217
|
+
const recommendations = [];
|
|
218
|
+
if (controls.length > 0) {
|
|
219
|
+
recommendations.push(`Review and update ${controls.length} affected controls`);
|
|
220
|
+
}
|
|
221
|
+
if (change.severity === 'critical' || change.severity === 'high') {
|
|
222
|
+
recommendations.push('Prioritize remediation for high-impact changes');
|
|
223
|
+
}
|
|
224
|
+
if (change.effectiveDate) {
|
|
225
|
+
const daysUntilEffective = Math.ceil((change.effectiveDate.getTime() - Date.now()) / (1000 * 60 * 60 * 24));
|
|
226
|
+
if (daysUntilEffective < 90) {
|
|
227
|
+
recommendations.push(`Deadline approaching: ${daysUntilEffective} days remaining`);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return recommendations;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Analyze deadline implications
|
|
234
|
+
*/
|
|
235
|
+
analyzeDeadlineImplications(change) {
|
|
236
|
+
const implications = [];
|
|
237
|
+
if (change.effectiveDate) {
|
|
238
|
+
const daysUntilEffective = Math.ceil((change.effectiveDate.getTime() - Date.now()) / (1000 * 60 * 60 * 24));
|
|
239
|
+
if (daysUntilEffective < 30) {
|
|
240
|
+
implications.push('CRITICAL: Less than 30 days to compliance deadline');
|
|
241
|
+
}
|
|
242
|
+
else if (daysUntilEffective < 90) {
|
|
243
|
+
implications.push('WARNING: Less than 90 days to compliance deadline');
|
|
244
|
+
}
|
|
245
|
+
else if (daysUntilEffective < 180) {
|
|
246
|
+
implications.push('PLANNING: Less than 180 days to compliance deadline');
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return implications;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Create compliance gaps
|
|
253
|
+
*/
|
|
254
|
+
createComplianceGaps(change, controls) {
|
|
255
|
+
for (const control of controls) {
|
|
256
|
+
if (control.remediationRequired) {
|
|
257
|
+
const gap = {
|
|
258
|
+
id: `gap-${change.id}-${control.controlId}`,
|
|
259
|
+
changeId: change.id,
|
|
260
|
+
controlId: control.controlId,
|
|
261
|
+
framework: control.framework,
|
|
262
|
+
gapType: 'new_requirement',
|
|
263
|
+
description: control.gapDescription,
|
|
264
|
+
status: 'open'
|
|
265
|
+
};
|
|
266
|
+
this.gaps.set(gap.id, gap);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Create timeline
|
|
272
|
+
*/
|
|
273
|
+
createTimeline(change) {
|
|
274
|
+
const timeline = {
|
|
275
|
+
changeId: change.id,
|
|
276
|
+
milestones: [
|
|
277
|
+
{
|
|
278
|
+
name: 'Detection',
|
|
279
|
+
date: change.publishedDate,
|
|
280
|
+
status: 'completed',
|
|
281
|
+
description: 'Regulatory change detected'
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
name: 'Analysis',
|
|
285
|
+
date: new Date(),
|
|
286
|
+
status: 'in_progress',
|
|
287
|
+
description: 'Impact analysis in progress'
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
name: 'Remediation',
|
|
291
|
+
date: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000), // 30 days from now
|
|
292
|
+
status: 'pending',
|
|
293
|
+
description: 'Implement required changes'
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
name: 'Compliance',
|
|
297
|
+
date: change.effectiveDate,
|
|
298
|
+
status: 'pending',
|
|
299
|
+
description: 'Full compliance achieved'
|
|
300
|
+
}
|
|
301
|
+
],
|
|
302
|
+
currentPhase: 'Analysis'
|
|
303
|
+
};
|
|
304
|
+
this.timelines.set(change.id, timeline);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
exports.RegulatoryChangeManagement = RegulatoryChangeManagement;
|
|
308
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@grc-claw/regulatory-change-management",
|
|
3
|
+
"version": "0.8.0",
|
|
4
|
+
"description": "Automated tracking of regulatory changes and impact analysis",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"publishConfig": {
|
|
9
|
+
"access": "public"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"test": "node --test dist/**/*.test.js"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"typescript": "^5.7.0"
|
|
18
|
+
}
|
|
19
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RegulatoryChangeManagement - Automated tracking of regulatory changes
|
|
3
|
+
*
|
|
4
|
+
* Monitors regulatory sources, detects changes, and maps them to existing controls.
|
|
5
|
+
* Provides impact analysis and remediation recommendations.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Multi-source regulatory monitoring (RSS, API, web scraping)
|
|
9
|
+
* - Change detection and classification
|
|
10
|
+
* - Impact analysis on existing controls
|
|
11
|
+
* - Automated remediation recommendations
|
|
12
|
+
* - Compliance gap identification
|
|
13
|
+
* - Regulatory timeline tracking
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export interface RegulatorySource {
|
|
17
|
+
id: string;
|
|
18
|
+
name: string;
|
|
19
|
+
type: 'rss' | 'api' | 'webhook' | 'manual';
|
|
20
|
+
url: string;
|
|
21
|
+
framework: string;
|
|
22
|
+
frequency: 'realtime' | 'daily' | 'weekly' | 'monthly';
|
|
23
|
+
lastChecked: Date;
|
|
24
|
+
enabled: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface RegulatoryChange {
|
|
28
|
+
id: string;
|
|
29
|
+
sourceId: string;
|
|
30
|
+
title: string;
|
|
31
|
+
description: string;
|
|
32
|
+
effectiveDate: Date;
|
|
33
|
+
publishedDate: Date;
|
|
34
|
+
framework: string;
|
|
35
|
+
changeType: 'new_regulation' | 'amendment' | 'guidance' | 'enforcement' | 'deadline';
|
|
36
|
+
severity: 'critical' | 'high' | 'medium' | 'low';
|
|
37
|
+
affectedControls: string[];
|
|
38
|
+
impactAnalysis?: ImpactAnalysis;
|
|
39
|
+
status: 'detected' | 'analyzed' | 'acknowledged' | 'in_remediation' | 'completed';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface ImpactAnalysis {
|
|
43
|
+
affectedControls: AffectedControl[];
|
|
44
|
+
overallImpact: 'critical' | 'high' | 'medium' | 'low' | 'none';
|
|
45
|
+
estimatedEffort: string;
|
|
46
|
+
recommendedActions: string[];
|
|
47
|
+
deadlineImplications: string[];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface AffectedControl {
|
|
51
|
+
controlId: string;
|
|
52
|
+
framework: string;
|
|
53
|
+
impact: 'critical' | 'high' | 'medium' | 'low' | 'none';
|
|
54
|
+
gapDescription: string;
|
|
55
|
+
remediationRequired: boolean;
|
|
56
|
+
estimatedEffort: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface RegulatoryTimeline {
|
|
60
|
+
changeId: string;
|
|
61
|
+
milestones: TimelineMilestone[];
|
|
62
|
+
currentPhase: string;
|
|
63
|
+
daysRemaining?: number;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface TimelineMilestone {
|
|
67
|
+
name: string;
|
|
68
|
+
date: Date;
|
|
69
|
+
status: 'pending' | 'in_progress' | 'completed' | 'overdue';
|
|
70
|
+
description: string;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface ComplianceGap {
|
|
74
|
+
id: string;
|
|
75
|
+
changeId: string;
|
|
76
|
+
controlId: string;
|
|
77
|
+
framework: string;
|
|
78
|
+
gapType: 'new_requirement' | 'strengthened_requirement' | 'new_evidence' | 'deadline_change';
|
|
79
|
+
description: string;
|
|
80
|
+
remediationPlan?: RemediationPlan;
|
|
81
|
+
status: 'open' | 'in_remediation' | 'closed';
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface RemediationPlan {
|
|
85
|
+
steps: RemediationStep[];
|
|
86
|
+
estimatedEffort: string;
|
|
87
|
+
assignedTo?: string;
|
|
88
|
+
deadline?: Date;
|
|
89
|
+
status: 'not_started' | 'in_progress' | 'completed';
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface RemediationStep {
|
|
93
|
+
name: string;
|
|
94
|
+
description: string;
|
|
95
|
+
estimatedHours: number;
|
|
96
|
+
dependencies: string[];
|
|
97
|
+
status: 'pending' | 'in_progress' | 'completed';
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export class RegulatoryChangeManagement {
|
|
101
|
+
private sources: Map<string, RegulatorySource> = new Map();
|
|
102
|
+
private changes: Map<string, RegulatoryChange> = new Map();
|
|
103
|
+
private gaps: Map<string, ComplianceGap> = new Map();
|
|
104
|
+
private timelines: Map<string, RegulatoryTimeline> = new Map();
|
|
105
|
+
|
|
106
|
+
constructor(
|
|
107
|
+
private readonly options: {
|
|
108
|
+
checkIntervalMs?: number;
|
|
109
|
+
maxChanges?: number;
|
|
110
|
+
autoAnalyze?: boolean;
|
|
111
|
+
} = {}
|
|
112
|
+
) {
|
|
113
|
+
this.options = {
|
|
114
|
+
checkIntervalMs: 3600000, // 1 hour
|
|
115
|
+
maxChanges: 1000,
|
|
116
|
+
autoAnalyze: true,
|
|
117
|
+
...options
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Register a regulatory source
|
|
123
|
+
*/
|
|
124
|
+
registerSource(source: RegulatorySource): void {
|
|
125
|
+
this.sources.set(source.id, source);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Check for regulatory changes
|
|
130
|
+
*/
|
|
131
|
+
async checkForChanges(): Promise<RegulatoryChange[]> {
|
|
132
|
+
const newChanges: RegulatoryChange[] = [];
|
|
133
|
+
|
|
134
|
+
for (const source of this.sources.values()) {
|
|
135
|
+
if (!source.enabled) continue;
|
|
136
|
+
|
|
137
|
+
try {
|
|
138
|
+
const changes = await this.fetchChanges(source);
|
|
139
|
+
for (const change of changes) {
|
|
140
|
+
if (!this.changes.has(change.id)) {
|
|
141
|
+
this.changes.set(change.id, change);
|
|
142
|
+
newChanges.push(change);
|
|
143
|
+
|
|
144
|
+
// Auto-analyze if enabled
|
|
145
|
+
if (this.options.autoAnalyze) {
|
|
146
|
+
await this.analyzeChange(change.id);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
source.lastChecked = new Date();
|
|
152
|
+
} catch (error) {
|
|
153
|
+
console.error(`Failed to check source ${source.name}:`, error);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return newChanges;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Analyze impact of a regulatory change
|
|
162
|
+
*/
|
|
163
|
+
async analyzeChange(changeId: string): Promise<ImpactAnalysis | null> {
|
|
164
|
+
const change = this.changes.get(changeId);
|
|
165
|
+
if (!change) return null;
|
|
166
|
+
|
|
167
|
+
// Analyze impact on controls
|
|
168
|
+
const affectedControls = await this.analyzeAffectedControls(change);
|
|
169
|
+
|
|
170
|
+
const impactAnalysis: ImpactAnalysis = {
|
|
171
|
+
affectedControls,
|
|
172
|
+
overallImpact: this.calculateOverallImpact(affectedControls),
|
|
173
|
+
estimatedEffort: this.estimateRemediationEffort(affectedControls),
|
|
174
|
+
recommendedActions: this.generateRecommendations(change, affectedControls),
|
|
175
|
+
deadlineImplications: this.analyzeDeadlineImplications(change)
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
change.impactAnalysis = impactAnalysis;
|
|
179
|
+
change.status = 'analyzed';
|
|
180
|
+
|
|
181
|
+
// Create compliance gaps
|
|
182
|
+
this.createComplianceGaps(change, affectedControls);
|
|
183
|
+
|
|
184
|
+
// Create timeline
|
|
185
|
+
this.createTimeline(change);
|
|
186
|
+
|
|
187
|
+
return impactAnalysis;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Get all regulatory changes
|
|
192
|
+
*/
|
|
193
|
+
getChanges(filters?: {
|
|
194
|
+
framework?: string;
|
|
195
|
+
severity?: string;
|
|
196
|
+
status?: string;
|
|
197
|
+
fromDate?: Date;
|
|
198
|
+
toDate?: Date;
|
|
199
|
+
}): RegulatoryChange[] {
|
|
200
|
+
let changes = Array.from(this.changes.values());
|
|
201
|
+
|
|
202
|
+
if (filters) {
|
|
203
|
+
if (filters.framework) {
|
|
204
|
+
changes = changes.filter(c => c.framework === filters.framework);
|
|
205
|
+
}
|
|
206
|
+
if (filters.severity) {
|
|
207
|
+
changes = changes.filter(c => c.severity === filters.severity);
|
|
208
|
+
}
|
|
209
|
+
if (filters.status) {
|
|
210
|
+
changes = changes.filter(c => c.status === filters.status);
|
|
211
|
+
}
|
|
212
|
+
if (filters.fromDate) {
|
|
213
|
+
changes = changes.filter(c => c.publishedDate >= filters.fromDate!);
|
|
214
|
+
}
|
|
215
|
+
if (filters.toDate) {
|
|
216
|
+
changes = changes.filter(c => c.publishedDate <= filters.toDate!);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return changes.sort((a, b) => b.publishedDate.getTime() - a.publishedDate.getTime());
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Get compliance gaps
|
|
225
|
+
*/
|
|
226
|
+
getGaps(filters?: {
|
|
227
|
+
framework?: string;
|
|
228
|
+
status?: string;
|
|
229
|
+
changeId?: string;
|
|
230
|
+
}): ComplianceGap[] {
|
|
231
|
+
let gaps = Array.from(this.gaps.values());
|
|
232
|
+
|
|
233
|
+
if (filters) {
|
|
234
|
+
if (filters.framework) {
|
|
235
|
+
gaps = gaps.filter(g => g.framework === filters.framework);
|
|
236
|
+
}
|
|
237
|
+
if (filters.status) {
|
|
238
|
+
gaps = gaps.filter(g => g.status === filters.status);
|
|
239
|
+
}
|
|
240
|
+
if (filters.changeId) {
|
|
241
|
+
gaps = gaps.filter(g => g.changeId === filters.changeId);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return gaps;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Get timeline for a change
|
|
250
|
+
*/
|
|
251
|
+
getTimeline(changeId: string): RegulatoryTimeline | undefined {
|
|
252
|
+
return this.timelines.get(changeId);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Update gap status
|
|
257
|
+
*/
|
|
258
|
+
updateGapStatus(gapId: string, status: ComplianceGap['status']): void {
|
|
259
|
+
const gap = this.gaps.get(gapId);
|
|
260
|
+
if (gap) {
|
|
261
|
+
gap.status = status;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Create remediation plan for a gap
|
|
267
|
+
*/
|
|
268
|
+
createRemediationPlan(gapId: string, plan: RemediationPlan): void {
|
|
269
|
+
const gap = this.gaps.get(gapId);
|
|
270
|
+
if (gap) {
|
|
271
|
+
gap.remediationPlan = plan;
|
|
272
|
+
gap.status = 'in_remediation';
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Get regulatory sources
|
|
278
|
+
*/
|
|
279
|
+
getSources(): RegulatorySource[] {
|
|
280
|
+
return Array.from(this.sources.values());
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Fetch changes from a source (stub - would integrate with real APIs)
|
|
285
|
+
*/
|
|
286
|
+
private async fetchChanges(source: RegulatorySource): Promise<RegulatoryChange[]> {
|
|
287
|
+
// In a real implementation, this would fetch from RSS/API/webhook
|
|
288
|
+
// For now, return empty array
|
|
289
|
+
return [];
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Analyze affected controls
|
|
294
|
+
*/
|
|
295
|
+
private async analyzeAffectedControls(change: RegulatoryChange): Promise<AffectedControl[]> {
|
|
296
|
+
// In a real implementation, this would analyze the change against the control framework
|
|
297
|
+
// For now, return empty array
|
|
298
|
+
return [];
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Calculate overall impact
|
|
303
|
+
*/
|
|
304
|
+
private calculateOverallImpact(controls: AffectedControl[]): ImpactAnalysis['overallImpact'] {
|
|
305
|
+
if (controls.length === 0) return 'none';
|
|
306
|
+
|
|
307
|
+
const hasCritical = controls.some(c => c.impact === 'critical');
|
|
308
|
+
const hasHigh = controls.some(c => c.impact === 'high');
|
|
309
|
+
const hasMedium = controls.some(c => c.impact === 'medium');
|
|
310
|
+
|
|
311
|
+
if (hasCritical) return 'critical';
|
|
312
|
+
if (hasHigh) return 'high';
|
|
313
|
+
if (hasMedium) return 'medium';
|
|
314
|
+
return 'low';
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Estimate remediation effort
|
|
319
|
+
*/
|
|
320
|
+
private estimateRemediationEffort(controls: AffectedControl[]): string {
|
|
321
|
+
const totalHours = controls.reduce((sum, c) => {
|
|
322
|
+
const hours = parseInt(c.estimatedEffort) || 0;
|
|
323
|
+
return sum + hours;
|
|
324
|
+
}, 0);
|
|
325
|
+
|
|
326
|
+
if (totalHours === 0) return 'Minimal';
|
|
327
|
+
if (totalHours < 40) return `${totalHours} hours`;
|
|
328
|
+
if (totalHours < 160) return `${Math.round(totalHours / 40)} weeks`;
|
|
329
|
+
return `${Math.round(totalHours / 160)} months`;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Generate recommendations
|
|
334
|
+
*/
|
|
335
|
+
private generateRecommendations(
|
|
336
|
+
change: RegulatoryChange,
|
|
337
|
+
controls: AffectedControl[]
|
|
338
|
+
): string[] {
|
|
339
|
+
const recommendations: string[] = [];
|
|
340
|
+
|
|
341
|
+
if (controls.length > 0) {
|
|
342
|
+
recommendations.push(`Review and update ${controls.length} affected controls`);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if (change.severity === 'critical' || change.severity === 'high') {
|
|
346
|
+
recommendations.push('Prioritize remediation for high-impact changes');
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (change.effectiveDate) {
|
|
350
|
+
const daysUntilEffective = Math.ceil(
|
|
351
|
+
(change.effectiveDate.getTime() - Date.now()) / (1000 * 60 * 60 * 24)
|
|
352
|
+
);
|
|
353
|
+
if (daysUntilEffective < 90) {
|
|
354
|
+
recommendations.push(`Deadline approaching: ${daysUntilEffective} days remaining`);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
return recommendations;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Analyze deadline implications
|
|
363
|
+
*/
|
|
364
|
+
private analyzeDeadlineImplications(change: RegulatoryChange): string[] {
|
|
365
|
+
const implications: string[] = [];
|
|
366
|
+
|
|
367
|
+
if (change.effectiveDate) {
|
|
368
|
+
const daysUntilEffective = Math.ceil(
|
|
369
|
+
(change.effectiveDate.getTime() - Date.now()) / (1000 * 60 * 60 * 24)
|
|
370
|
+
);
|
|
371
|
+
|
|
372
|
+
if (daysUntilEffective < 30) {
|
|
373
|
+
implications.push('CRITICAL: Less than 30 days to compliance deadline');
|
|
374
|
+
} else if (daysUntilEffective < 90) {
|
|
375
|
+
implications.push('WARNING: Less than 90 days to compliance deadline');
|
|
376
|
+
} else if (daysUntilEffective < 180) {
|
|
377
|
+
implications.push('PLANNING: Less than 180 days to compliance deadline');
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
return implications;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Create compliance gaps
|
|
386
|
+
*/
|
|
387
|
+
private createComplianceGaps(
|
|
388
|
+
change: RegulatoryChange,
|
|
389
|
+
controls: AffectedControl[]
|
|
390
|
+
): void {
|
|
391
|
+
for (const control of controls) {
|
|
392
|
+
if (control.remediationRequired) {
|
|
393
|
+
const gap: ComplianceGap = {
|
|
394
|
+
id: `gap-${change.id}-${control.controlId}`,
|
|
395
|
+
changeId: change.id,
|
|
396
|
+
controlId: control.controlId,
|
|
397
|
+
framework: control.framework,
|
|
398
|
+
gapType: 'new_requirement',
|
|
399
|
+
description: control.gapDescription,
|
|
400
|
+
status: 'open'
|
|
401
|
+
};
|
|
402
|
+
this.gaps.set(gap.id, gap);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Create timeline
|
|
409
|
+
*/
|
|
410
|
+
private createTimeline(change: RegulatoryChange): void {
|
|
411
|
+
const timeline: RegulatoryTimeline = {
|
|
412
|
+
changeId: change.id,
|
|
413
|
+
milestones: [
|
|
414
|
+
{
|
|
415
|
+
name: 'Detection',
|
|
416
|
+
date: change.publishedDate,
|
|
417
|
+
status: 'completed',
|
|
418
|
+
description: 'Regulatory change detected'
|
|
419
|
+
},
|
|
420
|
+
{
|
|
421
|
+
name: 'Analysis',
|
|
422
|
+
date: new Date(),
|
|
423
|
+
status: 'in_progress',
|
|
424
|
+
description: 'Impact analysis in progress'
|
|
425
|
+
},
|
|
426
|
+
{
|
|
427
|
+
name: 'Remediation',
|
|
428
|
+
date: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000), // 30 days from now
|
|
429
|
+
status: 'pending',
|
|
430
|
+
description: 'Implement required changes'
|
|
431
|
+
},
|
|
432
|
+
{
|
|
433
|
+
name: 'Compliance',
|
|
434
|
+
date: change.effectiveDate,
|
|
435
|
+
status: 'pending',
|
|
436
|
+
description: 'Full compliance achieved'
|
|
437
|
+
}
|
|
438
|
+
],
|
|
439
|
+
currentPhase: 'Analysis'
|
|
440
|
+
};
|
|
441
|
+
|
|
442
|
+
this.timelines.set(change.id, timeline);
|
|
443
|
+
}
|
|
444
|
+
}
|