fedramp-20x-mcp 0.4.8__py3-none-any.whl
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.
- fedramp_20x_mcp/__init__.py +14 -0
- fedramp_20x_mcp/__main__.py +12 -0
- fedramp_20x_mcp/data_loader.py +673 -0
- fedramp_20x_mcp/prompts/__init__.py +62 -0
- fedramp_20x_mcp/prompts/api_design_guide.txt +432 -0
- fedramp_20x_mcp/prompts/ato_package_checklist.txt +75 -0
- fedramp_20x_mcp/prompts/audit_preparation.txt +592 -0
- fedramp_20x_mcp/prompts/authorization_boundary_review.txt +76 -0
- fedramp_20x_mcp/prompts/azure_ksi_automation.txt +997 -0
- fedramp_20x_mcp/prompts/continuous_monitoring_setup.txt +61 -0
- fedramp_20x_mcp/prompts/documentation_generator.txt +499 -0
- fedramp_20x_mcp/prompts/gap_analysis.txt +25 -0
- fedramp_20x_mcp/prompts/initial_assessment_roadmap.txt +202 -0
- fedramp_20x_mcp/prompts/ksi_implementation_priorities.txt +283 -0
- fedramp_20x_mcp/prompts/migration_from_rev5.txt +440 -0
- fedramp_20x_mcp/prompts/quarterly_review_checklist.txt +231 -0
- fedramp_20x_mcp/prompts/significant_change_assessment.txt +50 -0
- fedramp_20x_mcp/prompts/vendor_evaluation.txt +349 -0
- fedramp_20x_mcp/prompts/vulnerability_remediation_timeline.txt +45 -0
- fedramp_20x_mcp/server.py +270 -0
- fedramp_20x_mcp/templates/__init__.py +75 -0
- fedramp_20x_mcp/templates/bicep/afr.txt +33 -0
- fedramp_20x_mcp/templates/bicep/cna.txt +48 -0
- fedramp_20x_mcp/templates/bicep/generic.txt +47 -0
- fedramp_20x_mcp/templates/bicep/iam.txt +211 -0
- fedramp_20x_mcp/templates/bicep/mla.txt +82 -0
- fedramp_20x_mcp/templates/bicep/rpl.txt +44 -0
- fedramp_20x_mcp/templates/bicep/svc.txt +54 -0
- fedramp_20x_mcp/templates/code/generic_csharp.txt +65 -0
- fedramp_20x_mcp/templates/code/generic_powershell.txt +65 -0
- fedramp_20x_mcp/templates/code/generic_python.txt +63 -0
- fedramp_20x_mcp/templates/code/iam_csharp.txt +150 -0
- fedramp_20x_mcp/templates/code/iam_powershell.txt +162 -0
- fedramp_20x_mcp/templates/code/iam_python.txt +224 -0
- fedramp_20x_mcp/templates/code/mla_python.txt +124 -0
- fedramp_20x_mcp/templates/terraform/afr.txt +29 -0
- fedramp_20x_mcp/templates/terraform/cna.txt +50 -0
- fedramp_20x_mcp/templates/terraform/generic.txt +40 -0
- fedramp_20x_mcp/templates/terraform/iam.txt +219 -0
- fedramp_20x_mcp/templates/terraform/mla.txt +29 -0
- fedramp_20x_mcp/templates/terraform/rpl.txt +32 -0
- fedramp_20x_mcp/templates/terraform/svc.txt +46 -0
- fedramp_20x_mcp/tools/__init__.py +167 -0
- fedramp_20x_mcp/tools/definitions.py +154 -0
- fedramp_20x_mcp/tools/documentation.py +155 -0
- fedramp_20x_mcp/tools/enhancements.py +2256 -0
- fedramp_20x_mcp/tools/evidence.py +701 -0
- fedramp_20x_mcp/tools/export.py +753 -0
- fedramp_20x_mcp/tools/ksi.py +90 -0
- fedramp_20x_mcp/tools/requirements.py +163 -0
- fedramp_20x_mcp-0.4.8.dist-info/METADATA +877 -0
- fedramp_20x_mcp-0.4.8.dist-info/RECORD +55 -0
- fedramp_20x_mcp-0.4.8.dist-info/WHEEL +4 -0
- fedramp_20x_mcp-0.4.8.dist-info/entry_points.txt +2 -0
- fedramp_20x_mcp-0.4.8.dist-info/licenses/LICENSE +27 -0
|
@@ -0,0 +1,753 @@
|
|
|
1
|
+
"""
|
|
2
|
+
FedRAMP 20x MCP Server - Export Tools
|
|
3
|
+
|
|
4
|
+
This module contains tool implementation functions for export.
|
|
5
|
+
"""
|
|
6
|
+
import json
|
|
7
|
+
import logging
|
|
8
|
+
from typing import Any, Optional
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
async def export_to_excel(
|
|
13
|
+
export_type: str,
|
|
14
|
+
output_path: Optional[str] = None
|
|
15
|
+
) -> str:
|
|
16
|
+
"""
|
|
17
|
+
Export FedRAMP 20x data to an Excel file.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
export_type: Type of data to export. Options:
|
|
21
|
+
- "ksi" - All 72 Key Security Indicators
|
|
22
|
+
- "all_requirements" - All 329 requirements across all families
|
|
23
|
+
- "definitions" - All FedRAMP definitions
|
|
24
|
+
output_path: Optional custom output path. If not provided, saves to Downloads folder
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
Path to the generated Excel file
|
|
28
|
+
"""
|
|
29
|
+
# Import openpyxl here to avoid import errors if not installed
|
|
30
|
+
try:
|
|
31
|
+
from openpyxl import Workbook
|
|
32
|
+
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
|
|
33
|
+
except ImportError:
|
|
34
|
+
return "Error: openpyxl package is required for Excel export. Install with: pip install openpyxl"
|
|
35
|
+
|
|
36
|
+
await data_loader.load_data()
|
|
37
|
+
|
|
38
|
+
# Determine output path
|
|
39
|
+
if output_path is None:
|
|
40
|
+
downloads_folder = str(Path.home() / "Downloads")
|
|
41
|
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
42
|
+
filename = f"FedRAMP_20x_{export_type}_{timestamp}.xlsx"
|
|
43
|
+
output_path = os.path.join(downloads_folder, filename)
|
|
44
|
+
|
|
45
|
+
# Create workbook
|
|
46
|
+
wb = Workbook()
|
|
47
|
+
if wb.active:
|
|
48
|
+
wb.remove(wb.active) # Remove default sheet
|
|
49
|
+
|
|
50
|
+
# Define styles
|
|
51
|
+
header_font = Font(bold=True, color="FFFFFF", size=11)
|
|
52
|
+
header_fill = PatternFill(start_color="366092", end_color="366092", fill_type="solid")
|
|
53
|
+
header_alignment = Alignment(horizontal="center", vertical="center", wrap_text=True)
|
|
54
|
+
cell_alignment = Alignment(vertical="top", wrap_text=True)
|
|
55
|
+
border = Border(
|
|
56
|
+
left=Side(style='thin'),
|
|
57
|
+
right=Side(style='thin'),
|
|
58
|
+
top=Side(style='thin'),
|
|
59
|
+
bottom=Side(style='thin')
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
if export_type == "ksi":
|
|
63
|
+
# Export all KSIs
|
|
64
|
+
ws = wb.create_sheet("Key Security Indicators")
|
|
65
|
+
|
|
66
|
+
# Headers
|
|
67
|
+
headers = ["KSI ID", "Name", "Category", "Status", "Statement", "Note", "NIST 800-53 Controls", "Reference", "Reference URL", "Impact Levels"]
|
|
68
|
+
ws.append(headers)
|
|
69
|
+
|
|
70
|
+
# Style headers
|
|
71
|
+
for col_num, header in enumerate(headers, 1):
|
|
72
|
+
cell = ws.cell(row=1, column=col_num)
|
|
73
|
+
cell.font = header_font
|
|
74
|
+
cell.fill = header_fill
|
|
75
|
+
cell.alignment = header_alignment
|
|
76
|
+
cell.border = border
|
|
77
|
+
|
|
78
|
+
# Get all KSIs
|
|
79
|
+
all_ksi = data_loader.list_all_ksi()
|
|
80
|
+
|
|
81
|
+
for ksi in all_ksi:
|
|
82
|
+
ksi_id = ksi.get('id', '')
|
|
83
|
+
name = ksi.get('name', '')
|
|
84
|
+
category = ksi.get('category', '')
|
|
85
|
+
retired = ksi.get('retired', False)
|
|
86
|
+
status = 'Retired' if retired else 'Active'
|
|
87
|
+
statement = ksi.get('statement', '')
|
|
88
|
+
note = ksi.get('note', '')
|
|
89
|
+
|
|
90
|
+
# Format controls
|
|
91
|
+
controls = ksi.get('controls', [])
|
|
92
|
+
if controls:
|
|
93
|
+
control_list = [f"{c.get('control_id', '').upper()} - {c.get('title', '')}" for c in controls]
|
|
94
|
+
controls_str = '; '.join(control_list)
|
|
95
|
+
else:
|
|
96
|
+
controls_str = ''
|
|
97
|
+
|
|
98
|
+
reference = ksi.get('reference', '')
|
|
99
|
+
reference_url = ksi.get('reference_url', '')
|
|
100
|
+
impact = ksi.get('impact', {})
|
|
101
|
+
impact_levels = ', '.join([k.title() for k, v in impact.items() if v]) if impact else ''
|
|
102
|
+
|
|
103
|
+
row = [ksi_id, name, category, status, statement, note, controls_str, reference, reference_url, impact_levels]
|
|
104
|
+
ws.append(row)
|
|
105
|
+
|
|
106
|
+
# Style data rows
|
|
107
|
+
for col_num in range(1, len(headers) + 1):
|
|
108
|
+
cell = ws.cell(row=ws.max_row, column=col_num)
|
|
109
|
+
cell.alignment = cell_alignment
|
|
110
|
+
cell.border = border
|
|
111
|
+
|
|
112
|
+
# Adjust column widths
|
|
113
|
+
ws.column_dimensions['A'].width = 15 # KSI ID
|
|
114
|
+
ws.column_dimensions['B'].width = 40 # Name
|
|
115
|
+
ws.column_dimensions['C'].width = 30 # Category
|
|
116
|
+
ws.column_dimensions['D'].width = 10 # Status
|
|
117
|
+
ws.column_dimensions['E'].width = 60 # Statement
|
|
118
|
+
ws.column_dimensions['F'].width = 40 # Note
|
|
119
|
+
ws.column_dimensions['G'].width = 70 # NIST 800-53 Controls
|
|
120
|
+
ws.column_dimensions['H'].width = 30 # Reference
|
|
121
|
+
ws.column_dimensions['I'].width = 50 # Reference URL
|
|
122
|
+
ws.column_dimensions['J'].width = 20 # Impact Levels
|
|
123
|
+
|
|
124
|
+
# Freeze header row
|
|
125
|
+
ws.freeze_panes = 'A2'
|
|
126
|
+
|
|
127
|
+
elif export_type == "all_requirements":
|
|
128
|
+
# Export all requirements
|
|
129
|
+
ws = wb.create_sheet("All Requirements")
|
|
130
|
+
|
|
131
|
+
headers = ["Requirement ID", "Family", "Term/Name", "Description", "Document"]
|
|
132
|
+
ws.append(headers)
|
|
133
|
+
|
|
134
|
+
# Style headers
|
|
135
|
+
for col_num, header in enumerate(headers, 1):
|
|
136
|
+
cell = ws.cell(row=1, column=col_num)
|
|
137
|
+
cell.font = header_font
|
|
138
|
+
cell.fill = header_fill
|
|
139
|
+
cell.alignment = header_alignment
|
|
140
|
+
cell.border = border
|
|
141
|
+
|
|
142
|
+
# Get all requirements
|
|
143
|
+
if not data_loader._data_cache:
|
|
144
|
+
return "Error: Data not loaded. Please try again."
|
|
145
|
+
all_reqs = data_loader._data_cache["requirements"]
|
|
146
|
+
|
|
147
|
+
for req_id, req in sorted(all_reqs.items()):
|
|
148
|
+
family = req_id.split('-')[0] if '-' in req_id else ''
|
|
149
|
+
term = req.get('term', req.get('name', ''))
|
|
150
|
+
description = req.get('description', req.get('definition', ''))
|
|
151
|
+
document = req.get('document_name', '')
|
|
152
|
+
|
|
153
|
+
row = [req_id, family, term, description, document]
|
|
154
|
+
ws.append(row)
|
|
155
|
+
|
|
156
|
+
# Style data rows
|
|
157
|
+
for col_num in range(1, len(headers) + 1):
|
|
158
|
+
cell = ws.cell(row=ws.max_row, column=col_num)
|
|
159
|
+
cell.alignment = cell_alignment
|
|
160
|
+
cell.border = border
|
|
161
|
+
|
|
162
|
+
# Adjust column widths
|
|
163
|
+
ws.column_dimensions['A'].width = 18 # ID
|
|
164
|
+
ws.column_dimensions['B'].width = 12 # Family
|
|
165
|
+
ws.column_dimensions['C'].width = 40 # Term
|
|
166
|
+
ws.column_dimensions['D'].width = 60 # Description
|
|
167
|
+
ws.column_dimensions['E'].width = 30 # Document
|
|
168
|
+
|
|
169
|
+
ws.freeze_panes = 'A2'
|
|
170
|
+
|
|
171
|
+
elif export_type == "definitions":
|
|
172
|
+
# Export all definitions
|
|
173
|
+
ws = wb.create_sheet("FedRAMP Definitions")
|
|
174
|
+
|
|
175
|
+
headers = ["Term", "Definition", "Notes", "References"]
|
|
176
|
+
ws.append(headers)
|
|
177
|
+
|
|
178
|
+
# Style headers
|
|
179
|
+
for col_num, header in enumerate(headers, 1):
|
|
180
|
+
cell = ws.cell(row=1, column=col_num)
|
|
181
|
+
cell.font = header_font
|
|
182
|
+
cell.fill = header_fill
|
|
183
|
+
cell.alignment = header_alignment
|
|
184
|
+
cell.border = border
|
|
185
|
+
|
|
186
|
+
# Get all definitions
|
|
187
|
+
all_defs = data_loader.list_all_definitions()
|
|
188
|
+
|
|
189
|
+
for defn in sorted(all_defs, key=lambda x: x.get('term', '')):
|
|
190
|
+
term = defn.get('term', '')
|
|
191
|
+
definition = defn.get('definition', '')
|
|
192
|
+
notes = defn.get('notes', '')
|
|
193
|
+
references = defn.get('references', '')
|
|
194
|
+
|
|
195
|
+
row = [term, definition, notes, references]
|
|
196
|
+
ws.append(row)
|
|
197
|
+
|
|
198
|
+
# Style data rows
|
|
199
|
+
for col_num in range(1, len(headers) + 1):
|
|
200
|
+
cell = ws.cell(row=ws.max_row, column=col_num)
|
|
201
|
+
cell.alignment = cell_alignment
|
|
202
|
+
cell.border = border
|
|
203
|
+
|
|
204
|
+
# Adjust column widths
|
|
205
|
+
ws.column_dimensions['A'].width = 30 # Term
|
|
206
|
+
ws.column_dimensions['B'].width = 60 # Definition
|
|
207
|
+
ws.column_dimensions['C'].width = 40 # Notes
|
|
208
|
+
ws.column_dimensions['D'].width = 30 # References
|
|
209
|
+
|
|
210
|
+
ws.freeze_panes = 'A2'
|
|
211
|
+
|
|
212
|
+
else:
|
|
213
|
+
return f"Error: Unknown export_type '{export_type}'. Valid options: ksi, all_requirements, definitions"
|
|
214
|
+
|
|
215
|
+
# Save workbook
|
|
216
|
+
wb.save(output_path)
|
|
217
|
+
|
|
218
|
+
return f"Excel file created successfully at: {output_path}"
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
async def export_to_csv(
|
|
223
|
+
export_type: str,
|
|
224
|
+
output_path: Optional[str] = None
|
|
225
|
+
) -> str:
|
|
226
|
+
"""
|
|
227
|
+
Export FedRAMP 20x data to a CSV file.
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
export_type: Type of data to export. Options:
|
|
231
|
+
- "ksi" - All 72 Key Security Indicators
|
|
232
|
+
- "all_requirements" - All 329 requirements across all families
|
|
233
|
+
- "definitions" - All FedRAMP definitions
|
|
234
|
+
output_path: Optional custom output path. If not provided, saves to Downloads folder
|
|
235
|
+
|
|
236
|
+
Returns:
|
|
237
|
+
Path to the generated CSV file
|
|
238
|
+
"""
|
|
239
|
+
await data_loader.load_data()
|
|
240
|
+
|
|
241
|
+
# Determine output path
|
|
242
|
+
if output_path is None:
|
|
243
|
+
downloads_folder = str(Path.home() / "Downloads")
|
|
244
|
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
245
|
+
filename = f"FedRAMP_20x_{export_type}_{timestamp}.csv"
|
|
246
|
+
output_path = os.path.join(downloads_folder, filename)
|
|
247
|
+
|
|
248
|
+
if export_type == "ksi":
|
|
249
|
+
# Export all KSIs
|
|
250
|
+
headers = ["KSI ID", "Name", "Category", "Status", "Statement", "Note", "NIST 800-53 Controls", "Reference", "Reference URL", "Impact Levels"]
|
|
251
|
+
|
|
252
|
+
with open(output_path, 'w', newline='', encoding='utf-8') as csvfile:
|
|
253
|
+
writer = csv.writer(csvfile)
|
|
254
|
+
writer.writerow(headers)
|
|
255
|
+
|
|
256
|
+
# Get all KSIs
|
|
257
|
+
all_ksi = data_loader.list_all_ksi()
|
|
258
|
+
|
|
259
|
+
for ksi in all_ksi:
|
|
260
|
+
ksi_id = ksi.get('id', '')
|
|
261
|
+
name = ksi.get('name', '')
|
|
262
|
+
category = ksi.get('category', '')
|
|
263
|
+
retired = ksi.get('retired', False)
|
|
264
|
+
status = 'Retired' if retired else 'Active'
|
|
265
|
+
statement = ksi.get('statement', '')
|
|
266
|
+
note = ksi.get('note', '')
|
|
267
|
+
|
|
268
|
+
# Format controls
|
|
269
|
+
controls = ksi.get('controls', [])
|
|
270
|
+
if controls:
|
|
271
|
+
control_list = [f"{c.get('control_id', '').upper()} - {c.get('title', '')}" for c in controls]
|
|
272
|
+
controls_str = '; '.join(control_list)
|
|
273
|
+
else:
|
|
274
|
+
controls_str = ''
|
|
275
|
+
|
|
276
|
+
reference = ksi.get('reference', '')
|
|
277
|
+
reference_url = ksi.get('reference_url', '')
|
|
278
|
+
impact = ksi.get('impact', {})
|
|
279
|
+
impact_levels = ', '.join([k.title() for k, v in impact.items() if v]) if impact else ''
|
|
280
|
+
|
|
281
|
+
row = [ksi_id, name, category, status, statement, note, controls_str, reference, reference_url, impact_levels]
|
|
282
|
+
writer.writerow(row)
|
|
283
|
+
|
|
284
|
+
elif export_type == "all_requirements":
|
|
285
|
+
# Export all requirements
|
|
286
|
+
headers = ["Requirement ID", "Family", "Term/Name", "Description", "Document"]
|
|
287
|
+
|
|
288
|
+
with open(output_path, 'w', newline='', encoding='utf-8') as csvfile:
|
|
289
|
+
writer = csv.writer(csvfile)
|
|
290
|
+
writer.writerow(headers)
|
|
291
|
+
|
|
292
|
+
# Get all requirements
|
|
293
|
+
if not data_loader._data_cache:
|
|
294
|
+
return "Error: Data not loaded. Please try again."
|
|
295
|
+
all_reqs = data_loader._data_cache["requirements"]
|
|
296
|
+
|
|
297
|
+
for req_id, req in sorted(all_reqs.items()):
|
|
298
|
+
family = req_id.split('-')[0] if '-' in req_id else ''
|
|
299
|
+
term = req.get('term', req.get('name', ''))
|
|
300
|
+
description = req.get('description', req.get('definition', ''))
|
|
301
|
+
document = req.get('document_name', '')
|
|
302
|
+
|
|
303
|
+
row = [req_id, family, term, description, document]
|
|
304
|
+
writer.writerow(row)
|
|
305
|
+
|
|
306
|
+
elif export_type == "definitions":
|
|
307
|
+
# Export all definitions
|
|
308
|
+
headers = ["Term", "Definition", "Notes", "References"]
|
|
309
|
+
|
|
310
|
+
with open(output_path, 'w', newline='', encoding='utf-8') as csvfile:
|
|
311
|
+
writer = csv.writer(csvfile)
|
|
312
|
+
writer.writerow(headers)
|
|
313
|
+
|
|
314
|
+
# Get all definitions
|
|
315
|
+
all_defs = data_loader.list_all_definitions()
|
|
316
|
+
|
|
317
|
+
for defn in sorted(all_defs, key=lambda x: x.get('term', '')):
|
|
318
|
+
term = defn.get('term', '')
|
|
319
|
+
definition = defn.get('definition', '')
|
|
320
|
+
notes = defn.get('notes', '')
|
|
321
|
+
references = defn.get('references', '')
|
|
322
|
+
|
|
323
|
+
row = [term, definition, notes, references]
|
|
324
|
+
writer.writerow(row)
|
|
325
|
+
|
|
326
|
+
else:
|
|
327
|
+
return f"Error: Unknown export_type '{export_type}'. Valid options: ksi, all_requirements, definitions"
|
|
328
|
+
|
|
329
|
+
return f"CSV file created successfully at: {output_path}"
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
async def generate_ksi_specification(
|
|
334
|
+
ksi_id: str,
|
|
335
|
+
evidence_collection_strategy: str,
|
|
336
|
+
output_path: Optional[str] = None
|
|
337
|
+
) -> str:
|
|
338
|
+
"""
|
|
339
|
+
Generate a product specification document for a KSI aligned with FedRAMP 20x requirements.
|
|
340
|
+
|
|
341
|
+
Args:
|
|
342
|
+
ksi_id: The KSI identifier (e.g., "KSI-AFR-01")
|
|
343
|
+
evidence_collection_strategy: High-level evidence collection strategy description
|
|
344
|
+
output_path: Optional custom output path. If not provided, saves to Downloads folder
|
|
345
|
+
|
|
346
|
+
Returns:
|
|
347
|
+
Path to the generated Word document
|
|
348
|
+
"""
|
|
349
|
+
# Import python-docx here to avoid import errors if not installed
|
|
350
|
+
try:
|
|
351
|
+
from docx import Document
|
|
352
|
+
from docx.shared import Pt, Inches, RGBColor
|
|
353
|
+
from docx.enum.text import WD_ALIGN_PARAGRAPH
|
|
354
|
+
from docx.enum.style import WD_STYLE_TYPE
|
|
355
|
+
except ImportError:
|
|
356
|
+
return "Error: python-docx package is required for Word document generation. Install with: pip install python-docx"
|
|
357
|
+
|
|
358
|
+
await data_loader.load_data()
|
|
359
|
+
|
|
360
|
+
# Get the KSI
|
|
361
|
+
ksi = data_loader.get_ksi(ksi_id.upper())
|
|
362
|
+
if not ksi:
|
|
363
|
+
return f"Error: KSI '{ksi_id}' not found"
|
|
364
|
+
|
|
365
|
+
# Determine output path
|
|
366
|
+
if output_path is None:
|
|
367
|
+
downloads_folder = str(Path.home() / "Downloads")
|
|
368
|
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
369
|
+
safe_name = ksi_id.replace('/', '_').replace('\\', '_')
|
|
370
|
+
filename = f"KSI_Spec_{safe_name}_{timestamp}.docx"
|
|
371
|
+
output_path = os.path.join(downloads_folder, filename)
|
|
372
|
+
|
|
373
|
+
# Create document
|
|
374
|
+
doc = Document()
|
|
375
|
+
|
|
376
|
+
# Set up styles
|
|
377
|
+
styles = doc.styles
|
|
378
|
+
|
|
379
|
+
# Document title
|
|
380
|
+
title = doc.add_heading(f"Product Specification: {ksi.get('name', ksi_id)}", 0)
|
|
381
|
+
title.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
|
382
|
+
|
|
383
|
+
# Subtitle with KSI ID
|
|
384
|
+
subtitle = doc.add_paragraph(f"KSI ID: {ksi_id}")
|
|
385
|
+
subtitle.alignment = WD_ALIGN_PARAGRAPH.CENTER
|
|
386
|
+
subtitle_run = subtitle.runs[0]
|
|
387
|
+
subtitle_run.font.size = Pt(14)
|
|
388
|
+
subtitle_run.font.color.rgb = RGBColor(54, 96, 146) # FedRAMP blue
|
|
389
|
+
|
|
390
|
+
# Add metadata table
|
|
391
|
+
doc.add_paragraph()
|
|
392
|
+
metadata_heading = doc.add_heading('Document Information', 2)
|
|
393
|
+
|
|
394
|
+
table = doc.add_table(rows=5, cols=2)
|
|
395
|
+
table.style = 'Light Grid Accent 1'
|
|
396
|
+
|
|
397
|
+
# Populate metadata
|
|
398
|
+
metadata = [
|
|
399
|
+
('KSI ID', ksi_id),
|
|
400
|
+
('Category', ksi.get('category', 'N/A')),
|
|
401
|
+
('Impact Levels', ', '.join([k.title() for k, v in ksi.get('impact', {}).items() if v]) or 'N/A'),
|
|
402
|
+
('Status', 'Retired' if ksi.get('retired', False) else 'Active'),
|
|
403
|
+
('Document Date', datetime.now().strftime('%B %d, %Y'))
|
|
404
|
+
]
|
|
405
|
+
|
|
406
|
+
for idx, (label, value) in enumerate(metadata):
|
|
407
|
+
table.rows[idx].cells[0].text = label
|
|
408
|
+
table.rows[idx].cells[1].text = str(value)
|
|
409
|
+
# Bold the labels
|
|
410
|
+
table.rows[idx].cells[0].paragraphs[0].runs[0].font.bold = True
|
|
411
|
+
|
|
412
|
+
# Overview section
|
|
413
|
+
doc.add_page_break()
|
|
414
|
+
doc.add_heading('1. Overview', 1)
|
|
415
|
+
|
|
416
|
+
doc.add_heading('1.1 Purpose', 2)
|
|
417
|
+
doc.add_paragraph(
|
|
418
|
+
f"This document provides a comprehensive product specification for implementing "
|
|
419
|
+
f"{ksi.get('name', ksi_id)} in compliance with FedRAMP 20x Key Security Indicators. "
|
|
420
|
+
f"It is designed to guide engineering teams through planning, implementation, and "
|
|
421
|
+
f"evidence collection activities."
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
doc.add_heading('1.2 Scope', 2)
|
|
425
|
+
impact = ksi.get('impact', {})
|
|
426
|
+
impact_text = "This KSI applies to "
|
|
427
|
+
if impact.get('low') and impact.get('moderate'):
|
|
428
|
+
impact_text += "both Low and Moderate impact systems."
|
|
429
|
+
elif impact.get('low'):
|
|
430
|
+
impact_text += "Low impact systems."
|
|
431
|
+
elif impact.get('moderate'):
|
|
432
|
+
impact_text += "Moderate impact systems."
|
|
433
|
+
else:
|
|
434
|
+
impact_text += "systems as defined by FedRAMP authorization requirements."
|
|
435
|
+
doc.add_paragraph(impact_text)
|
|
436
|
+
|
|
437
|
+
# Requirement Statement
|
|
438
|
+
doc.add_heading('2. Requirement Statement', 1)
|
|
439
|
+
statement = ksi.get('statement', '')
|
|
440
|
+
if statement:
|
|
441
|
+
doc.add_paragraph(statement)
|
|
442
|
+
else:
|
|
443
|
+
doc.add_paragraph("No statement available for this KSI.")
|
|
444
|
+
|
|
445
|
+
# Check for retired status
|
|
446
|
+
if ksi.get('retired', False):
|
|
447
|
+
note_para = doc.add_paragraph()
|
|
448
|
+
note_run = note_para.add_run(f"⚠ NOTE: {ksi.get('note', 'This KSI has been retired.')}")
|
|
449
|
+
note_run.font.color.rgb = RGBColor(192, 0, 0)
|
|
450
|
+
note_run.font.bold = True
|
|
451
|
+
|
|
452
|
+
# Related NIST 800-53 Controls
|
|
453
|
+
doc.add_heading('3. Related NIST 800-53 Controls', 1)
|
|
454
|
+
controls = ksi.get('controls', [])
|
|
455
|
+
if controls:
|
|
456
|
+
doc.add_paragraph(
|
|
457
|
+
"This KSI aligns with the following NIST 800-53 Rev 5 security controls. "
|
|
458
|
+
"Implementation must address these control requirements:"
|
|
459
|
+
)
|
|
460
|
+
for control in controls:
|
|
461
|
+
control_id = control.get('control_id', '').upper()
|
|
462
|
+
control_title = control.get('title', 'N/A')
|
|
463
|
+
p = doc.add_paragraph(style='List Bullet')
|
|
464
|
+
p.add_run(f"{control_id}: ").bold = True
|
|
465
|
+
p.add_run(control_title)
|
|
466
|
+
else:
|
|
467
|
+
doc.add_paragraph("No specific NIST 800-53 controls mapped to this KSI.")
|
|
468
|
+
|
|
469
|
+
# Reference Documentation
|
|
470
|
+
reference = ksi.get('reference')
|
|
471
|
+
reference_url = ksi.get('reference_url')
|
|
472
|
+
if reference or reference_url:
|
|
473
|
+
doc.add_heading('4. Reference Documentation', 1)
|
|
474
|
+
if reference:
|
|
475
|
+
doc.add_paragraph(f"Primary Reference: {reference}")
|
|
476
|
+
if reference_url:
|
|
477
|
+
doc.add_paragraph(f"Documentation URL: {reference_url}")
|
|
478
|
+
|
|
479
|
+
# Azure-First Implementation Guidance
|
|
480
|
+
doc.add_heading('5. Azure-First Implementation Guidance', 1)
|
|
481
|
+
|
|
482
|
+
doc.add_heading('5.1 Recommended Azure Services', 2)
|
|
483
|
+
doc.add_paragraph(
|
|
484
|
+
"Based on FedRAMP 20x requirements and Azure Government compliance capabilities, "
|
|
485
|
+
"consider the following Azure services for implementation:"
|
|
486
|
+
)
|
|
487
|
+
|
|
488
|
+
# Add Azure-specific recommendations based on category
|
|
489
|
+
category = ksi.get('category', '')
|
|
490
|
+
azure_recommendations = _get_azure_recommendations(category, controls)
|
|
491
|
+
|
|
492
|
+
for service, description in azure_recommendations:
|
|
493
|
+
p = doc.add_paragraph(style='List Bullet')
|
|
494
|
+
p.add_run(f"{service}: ").bold = True
|
|
495
|
+
p.add_run(description)
|
|
496
|
+
|
|
497
|
+
doc.add_heading('5.2 Infrastructure as Code', 2)
|
|
498
|
+
doc.add_paragraph(
|
|
499
|
+
"Implement using Azure Bicep or Terraform with Azure Provider for repeatable, "
|
|
500
|
+
"auditable deployments. Store IaC in version control (Azure Repos or GitHub) "
|
|
501
|
+
"with branch protection and approval workflows."
|
|
502
|
+
)
|
|
503
|
+
|
|
504
|
+
doc.add_heading('5.3 Automation and Monitoring', 2)
|
|
505
|
+
automation_items = [
|
|
506
|
+
"Use Azure Policy for continuous compliance monitoring and enforcement",
|
|
507
|
+
"Implement Azure Monitor and Log Analytics for centralized logging",
|
|
508
|
+
"Configure Azure Security Center for security posture management",
|
|
509
|
+
"Enable Microsoft Defender for Cloud for threat protection",
|
|
510
|
+
"Use Azure Automation for remediation workflows"
|
|
511
|
+
]
|
|
512
|
+
for item in automation_items:
|
|
513
|
+
doc.add_paragraph(item, style='List Bullet')
|
|
514
|
+
|
|
515
|
+
# Evidence Collection Strategy
|
|
516
|
+
doc.add_heading('6. Evidence Collection Strategy', 1)
|
|
517
|
+
|
|
518
|
+
doc.add_heading('6.1 User-Defined Strategy', 2)
|
|
519
|
+
doc.add_paragraph(evidence_collection_strategy)
|
|
520
|
+
|
|
521
|
+
doc.add_heading('6.2 Recommended Evidence Types', 2)
|
|
522
|
+
evidence_items = [
|
|
523
|
+
"Configuration screenshots from Azure Portal",
|
|
524
|
+
"Azure Policy compliance reports exported as JSON/CSV",
|
|
525
|
+
"Azure Monitor query results and dashboards",
|
|
526
|
+
"IaC templates (Bicep/Terraform) with commit history",
|
|
527
|
+
"Azure DevOps/GitHub Actions pipeline logs",
|
|
528
|
+
"Microsoft Entra ID audit logs for access control",
|
|
529
|
+
"Azure Resource Graph queries demonstrating compliance",
|
|
530
|
+
"Automated test results from security validation pipelines"
|
|
531
|
+
]
|
|
532
|
+
for item in evidence_items:
|
|
533
|
+
doc.add_paragraph(item, style='List Bullet')
|
|
534
|
+
|
|
535
|
+
doc.add_heading('6.3 Evidence Collection Schedule', 2)
|
|
536
|
+
doc.add_paragraph(
|
|
537
|
+
"Evidence should be collected regularly to align with FedRAMP "
|
|
538
|
+
"Collaborative Continuous Monitoring (CCM) requirements. Automate evidence "
|
|
539
|
+
"collection where possible using Azure Functions, Logic Apps, or Azure Automation. "
|
|
540
|
+
"Engineering teams should determine the appropriate collection frequency based on "
|
|
541
|
+
"system criticality and organizational requirements."
|
|
542
|
+
)
|
|
543
|
+
|
|
544
|
+
# Implementation Plan Template
|
|
545
|
+
doc.add_heading('7. Implementation Plan Template', 1)
|
|
546
|
+
|
|
547
|
+
phases = [
|
|
548
|
+
{
|
|
549
|
+
'phase': 'Phase 1: Requirements Analysis',
|
|
550
|
+
'activities': [
|
|
551
|
+
'Review NIST 800-53 control requirements',
|
|
552
|
+
'Map controls to Azure services and features',
|
|
553
|
+
'Identify gaps in current implementation',
|
|
554
|
+
'Document assumptions and dependencies'
|
|
555
|
+
]
|
|
556
|
+
},
|
|
557
|
+
{
|
|
558
|
+
'phase': 'Phase 2: Design',
|
|
559
|
+
'activities': [
|
|
560
|
+
'Create Azure architecture diagrams',
|
|
561
|
+
'Design IaC templates (Bicep/Terraform)',
|
|
562
|
+
'Define Azure Policy rules and initiatives',
|
|
563
|
+
'Design monitoring and alerting strategy',
|
|
564
|
+
'Plan evidence collection automation'
|
|
565
|
+
]
|
|
566
|
+
},
|
|
567
|
+
{
|
|
568
|
+
'phase': 'Phase 3: Implementation',
|
|
569
|
+
'activities': [
|
|
570
|
+
'Deploy Azure infrastructure using IaC',
|
|
571
|
+
'Configure Azure services and policies',
|
|
572
|
+
'Implement monitoring and logging',
|
|
573
|
+
'Set up automated evidence collection',
|
|
574
|
+
'Configure security controls and access policies'
|
|
575
|
+
]
|
|
576
|
+
},
|
|
577
|
+
{
|
|
578
|
+
'phase': 'Phase 4: Testing and Validation',
|
|
579
|
+
'activities': [
|
|
580
|
+
'Validate Azure Policy compliance',
|
|
581
|
+
'Test evidence collection automation',
|
|
582
|
+
'Perform security scanning and assessment',
|
|
583
|
+
'Validate control implementation against requirements',
|
|
584
|
+
'Document test results'
|
|
585
|
+
]
|
|
586
|
+
},
|
|
587
|
+
{
|
|
588
|
+
'phase': 'Phase 5: Documentation and Evidence',
|
|
589
|
+
'activities': [
|
|
590
|
+
'Collect and organize all evidence',
|
|
591
|
+
'Document implementation details',
|
|
592
|
+
'Create operational runbooks',
|
|
593
|
+
'Prepare for FedRAMP assessment',
|
|
594
|
+
'Update SSP (System Security Plan) as needed'
|
|
595
|
+
]
|
|
596
|
+
}
|
|
597
|
+
]
|
|
598
|
+
|
|
599
|
+
for phase_info in phases:
|
|
600
|
+
doc.add_heading(phase_info['phase'], 2)
|
|
601
|
+
doc.add_paragraph("Activities:")
|
|
602
|
+
for activity in phase_info['activities']:
|
|
603
|
+
doc.add_paragraph(activity, style='List Bullet')
|
|
604
|
+
|
|
605
|
+
# Team Roles and Responsibilities
|
|
606
|
+
doc.add_heading('8. Team Roles and Responsibilities', 1)
|
|
607
|
+
|
|
608
|
+
roles = [
|
|
609
|
+
('Cloud Architect', 'Design Azure architecture and services selection'),
|
|
610
|
+
('DevOps Engineer', 'Implement IaC templates and CI/CD pipelines'),
|
|
611
|
+
('Security Engineer', 'Configure security controls and monitoring'),
|
|
612
|
+
('Compliance Specialist', 'Ensure FedRAMP requirements are met'),
|
|
613
|
+
('Product Owner', 'Prioritize requirements and acceptance criteria'),
|
|
614
|
+
('QA Engineer', 'Validate implementation and test evidence collection')
|
|
615
|
+
]
|
|
616
|
+
|
|
617
|
+
for role, responsibility in roles:
|
|
618
|
+
p = doc.add_paragraph(style='List Bullet')
|
|
619
|
+
p.add_run(f"{role}: ").bold = True
|
|
620
|
+
p.add_run(responsibility)
|
|
621
|
+
|
|
622
|
+
# Success Criteria
|
|
623
|
+
doc.add_heading('9. Success Criteria', 1)
|
|
624
|
+
|
|
625
|
+
success_items = [
|
|
626
|
+
'All NIST 800-53 controls implemented and validated in Azure',
|
|
627
|
+
'Azure Policy reports 100% compliance for defined policies',
|
|
628
|
+
'Evidence collection automation operational and tested',
|
|
629
|
+
'IaC templates reviewed and approved',
|
|
630
|
+
'Security scanning shows no high/critical vulnerabilities',
|
|
631
|
+
'Documentation complete and reviewed',
|
|
632
|
+
'Team training completed on operations and maintenance',
|
|
633
|
+
'Quarterly evidence collection process validated'
|
|
634
|
+
]
|
|
635
|
+
|
|
636
|
+
for item in success_items:
|
|
637
|
+
doc.add_paragraph(item, style='List Bullet')
|
|
638
|
+
|
|
639
|
+
# Risks and Mitigation
|
|
640
|
+
doc.add_heading('10. Risks and Mitigation Strategies', 1)
|
|
641
|
+
|
|
642
|
+
risks = [
|
|
643
|
+
{
|
|
644
|
+
'risk': 'Azure service limitations may not fully satisfy control requirements',
|
|
645
|
+
'mitigation': 'Identify gaps early, use compensating controls, engage Azure support for guidance'
|
|
646
|
+
},
|
|
647
|
+
{
|
|
648
|
+
'risk': 'Evidence collection automation failures',
|
|
649
|
+
'mitigation': 'Implement monitoring and alerting, maintain manual collection procedures as backup'
|
|
650
|
+
},
|
|
651
|
+
{
|
|
652
|
+
'risk': 'Compliance drift over time',
|
|
653
|
+
'mitigation': 'Use Azure Policy in enforcement mode, implement continuous monitoring'
|
|
654
|
+
},
|
|
655
|
+
{
|
|
656
|
+
'risk': 'Resource constraints or timeline delays',
|
|
657
|
+
'mitigation': 'Prioritize critical controls, use phased implementation approach'
|
|
658
|
+
}
|
|
659
|
+
]
|
|
660
|
+
|
|
661
|
+
for risk_info in risks:
|
|
662
|
+
doc.add_heading(f"Risk: {risk_info['risk']}", 2)
|
|
663
|
+
doc.add_paragraph(f"Mitigation: {risk_info['mitigation']}")
|
|
664
|
+
|
|
665
|
+
# Appendix
|
|
666
|
+
doc.add_page_break()
|
|
667
|
+
doc.add_heading('Appendix A: Additional Resources', 1)
|
|
668
|
+
|
|
669
|
+
resources = [
|
|
670
|
+
'FedRAMP 20x Requirements: https://fedramp.gov/docs/',
|
|
671
|
+
'NIST 800-53 Rev 5: https://csrc.nist.gov/publications/detail/sp/800-53/rev-5/final',
|
|
672
|
+
'Azure Security Documentation: https://learn.microsoft.com/azure/security/',
|
|
673
|
+
'Azure Compliance: https://learn.microsoft.com/azure/compliance/',
|
|
674
|
+
'Azure Government: https://azure.microsoft.com/explore/global-infrastructure/government/',
|
|
675
|
+
'Microsoft Entra ID: https://learn.microsoft.com/entra/',
|
|
676
|
+
'Azure Policy: https://learn.microsoft.com/azure/governance/policy/'
|
|
677
|
+
]
|
|
678
|
+
|
|
679
|
+
for resource in resources:
|
|
680
|
+
doc.add_paragraph(resource, style='List Bullet')
|
|
681
|
+
|
|
682
|
+
# Save document
|
|
683
|
+
doc.save(output_path)
|
|
684
|
+
|
|
685
|
+
return f"Word document created successfully at: {output_path}"
|
|
686
|
+
|
|
687
|
+
|
|
688
|
+
def _get_azure_recommendations(category: str, controls: list) -> list:
|
|
689
|
+
"""Get Azure service recommendations based on KSI category and controls."""
|
|
690
|
+
recommendations = []
|
|
691
|
+
|
|
692
|
+
# Default recommendations for all KSIs
|
|
693
|
+
recommendations.extend([
|
|
694
|
+
('Microsoft Entra ID', 'Identity and access management, conditional access, MFA'),
|
|
695
|
+
('Azure Policy', 'Compliance enforcement and continuous monitoring'),
|
|
696
|
+
('Azure Monitor', 'Centralized logging, alerting, and diagnostics')
|
|
697
|
+
])
|
|
698
|
+
|
|
699
|
+
# Category-specific recommendations
|
|
700
|
+
category_lower = category.lower()
|
|
701
|
+
|
|
702
|
+
if 'education' in category_lower or 'training' in category_lower:
|
|
703
|
+
recommendations.extend([
|
|
704
|
+
('Microsoft Viva Learning', 'Security awareness training platform'),
|
|
705
|
+
('Azure AD Access Reviews', 'Regular access certification and training tracking')
|
|
706
|
+
])
|
|
707
|
+
|
|
708
|
+
if 'vulnerability' in category_lower or 'assessment' in category_lower:
|
|
709
|
+
recommendations.extend([
|
|
710
|
+
('Microsoft Defender for Cloud', 'Vulnerability scanning and security recommendations'),
|
|
711
|
+
('Azure Security Center', 'Security posture management and compliance tracking')
|
|
712
|
+
])
|
|
713
|
+
|
|
714
|
+
if 'incident' in category_lower or 'response' in category_lower:
|
|
715
|
+
recommendations.extend([
|
|
716
|
+
('Microsoft Sentinel', 'SIEM for incident detection and response'),
|
|
717
|
+
('Azure Logic Apps', 'Automated incident response workflows')
|
|
718
|
+
])
|
|
719
|
+
|
|
720
|
+
if 'data' in category_lower or 'encryption' in category_lower:
|
|
721
|
+
recommendations.extend([
|
|
722
|
+
('Azure Key Vault', 'Cryptographic key and secret management'),
|
|
723
|
+
('Azure Storage encryption', 'Data at rest encryption with customer-managed keys')
|
|
724
|
+
])
|
|
725
|
+
|
|
726
|
+
if 'network' in category_lower or 'boundary' in category_lower:
|
|
727
|
+
recommendations.extend([
|
|
728
|
+
('Azure Firewall', 'Network security and filtering'),
|
|
729
|
+
('Azure DDoS Protection', 'DDoS mitigation'),
|
|
730
|
+
('Azure Virtual Network', 'Network segmentation and isolation')
|
|
731
|
+
])
|
|
732
|
+
|
|
733
|
+
# Control-based recommendations
|
|
734
|
+
control_ids = [c.get('control_id', '').upper() for c in controls]
|
|
735
|
+
|
|
736
|
+
if any(c.startswith('AU-') for c in control_ids): # Audit controls
|
|
737
|
+
recommendations.append(('Azure Log Analytics', 'Centralized audit log collection and analysis'))
|
|
738
|
+
|
|
739
|
+
if any(c.startswith('CM-') for c in control_ids): # Configuration Management
|
|
740
|
+
recommendations.append(('Azure Automation State Configuration', 'Desired state configuration management'))
|
|
741
|
+
|
|
742
|
+
if any(c.startswith('SC-') for c in control_ids): # System and Communications Protection
|
|
743
|
+
recommendations.append(('Azure Front Door', 'Web application firewall and CDN'))
|
|
744
|
+
|
|
745
|
+
# Remove duplicates while preserving order
|
|
746
|
+
seen = set()
|
|
747
|
+
unique_recommendations = []
|
|
748
|
+
for rec in recommendations:
|
|
749
|
+
if rec[0] not in seen:
|
|
750
|
+
seen.add(rec[0])
|
|
751
|
+
unique_recommendations.append(rec)
|
|
752
|
+
|
|
753
|
+
return unique_recommendations
|